Commit 917fef78 authored by Arkadiy Marinenko's avatar Arkadiy Marinenko

библиотека nla3d с улучшенным выводом VTK-файлов (3 этап)

parent 68e008d9
......@@ -6,4 +6,8 @@
2. С помощью проекта CodeBlocks (файл math.cbp) или make-файла откомпилировать библиотеку math, находящуюся в папке math.
По умолчанию включена поддержка как решателей paralution (-DNLA3D_USE_PARALUTION), так и решателей mkl (-DNLA3D_USE_MKL). При необходимости, ненужное отключить в соответствующих настройках проекта CodeBlocks или в make-файле.
Особое внимание уделить файлу EquationSolver.h и значениям абсолютной невязки "double abs_tol = 1.0E-06" и относительной невязки "double rel_tol = 1.0E-06" для решателей paralution. Это критерии выхода для итерационного решателя. В зависимости от требуемой точности решения их нужно изменять.
3. С помощью проекта CodeBlocks (файл nla3d.cbp) или make-файла откомпилировать библиотеку nla3d, находящуюся в папке nla3d.
В данной версии nla3d улучшен алгоритм вывода данных для VTK-файлов. Скорость вывода увеличена в разы, особенно хорошо это заметно на больших сетках.
Для успешной компиляции необходимо наличие исходников библиотеки линейной алгебры eigen.
В ходе компиляции будет большое количество warning'ов, это нормально и связано с eigen.
#include "Dof.h"
namespace nla3d {
const uint16 Dof::numberOfDofTypes = Dof::UNDEFINED;
const char* const Dof::dofTypeLabels[] = {"UX", "UY", "UZ", "ROTX", "ROTY",
"ROTZ", "HYDRO_PRESSURE", "TEMP", "UNDEFINED"};
// Check that TypeLabels has the same length as numberOfDofTypes
static_assert(Dof::numberOfDofTypes == sizeof(Dof::dofTypeLabels)/sizeof(Dof::dofTypeLabels[0]) - 1,
"dofTypeLabels and dofType must have the same number of elements");
Dof::dofType Dof::label2dofType (const std::string& label) {
for (uint16 i = 0; i < numberOfDofTypes; i++) {
if (label.compare(dofTypeLabels[i]) == 0) {
return (dofType) i;
}
}
LOG(ERROR) << "Unknown dof key = " << label;
return Dof::UNDEFINED;
}
void DofCollection::initDofTable(uint32 _numberOfEntities) {
clearDofTable();
numberOfEntities = _numberOfEntities;
dofPos.assign(numberOfEntities + 1, 0);
}
// n index starts from 1
void DofCollection::addDof(uint32 n, std::initializer_list<Dof::dofType> __dofs) {
assert(n <= numberOfEntities);
assert(dofPos.size() > 0);
std::set<Dof::dofType> _dofs(__dofs);
std::vector<Dof> newDofs;
// if already registered - just return
if (dofPos[n-1] == dofPos[n]) {
for (auto v : _dofs)
newDofs.push_back(Dof(v));
} else {
for (auto v : _dofs) {
bool isFound = false;
for (auto it = dofs.begin() + dofPos[n-1]; it < dofs.begin() + dofPos[n]; it++) {
if (it->type == v) {
isFound = true;
break;
}
}
if (!isFound) {
newDofs.push_back(Dof(v));
}
}
}
if (newDofs.size() == 0) return;
dofs.insert(dofs.begin() + dofPos[n], newDofs.begin(), newDofs.end());
uniqueDofTypes.insert(std::begin(_dofs), std::end(_dofs));
// incement dofPos with newDofs.size() for all above
uint32 i = n;
while (i <= numberOfEntities) dofPos[i++] += newDofs.size();
numberOfUsedDofs += newDofs.size();
}
void DofCollection::clearDofTable() {
dofs.clear();
dofPos.clear();
uniqueDofTypes.clear();
numberOfUsedDofs = 0;
numberOfEntities = 0;
}
// n index starts from 1
bool DofCollection::isDofUsed(uint32 n, Dof::dofType dof) {
assert(n <= numberOfEntities);
assert(dofPos.size() > 0);
if (dofPos[n-1] == dofPos[n]) return false;
for (auto it = dofs.begin() + dofPos[n-1]; it < dofs.begin() + dofPos[n]; it++) {
if (it->type == dof) return true;
}
return false;
}
// return nullptr if Dof was not found
Dof* DofCollection::getDof(uint32 n, Dof::dofType dof) {
assert(n <= numberOfEntities);
assert(dofPos.size() > 0);
Dof* pdof = nullptr;
for (auto it = dofs.begin() + dofPos[n-1]; it < dofs.begin() + dofPos[n]; it++) {
if (it->type == dof) {
pdof = &(*it);
break;
}
}
return pdof;
}
std::set<Dof::dofType> DofCollection::getUniqueDofTypes() {
return uniqueDofTypes;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
namespace nla3d {
class DofCollection;
// class for Degree of Freedom informations
// (is it fixed boundary condition, what its number in equation system, ..)
class Dof {
public:
enum dofType {
UX = 0,
UY,
UZ,
ROTX,
ROTY,
ROTZ,
HYDRO_PRESSURE,
TEMP,
UNDEFINED // should be last
};
Dof(dofType t) : eqNumber(0), isConstrained(false), type(t) { }
uint32 eqNumber = 0; // 0 - not use
bool isConstrained = false; // is this DOF defined by B.C.
dofType type;
static const char* const dofTypeLabels[];
static const uint16 numberOfDofTypes;
static dofType label2dofType (const std::string& label);
static const char* dofType2label (const dofType t);
};
inline const char* Dof::dofType2label(const Dof::dofType t) {
assert(t < Dof::UNDEFINED);
return dofTypeLabels[t];
}
class DofCollection {
public:
uint32 getNumberOfUsedDofs();
uint32 getNumberOfEntities();
// return a poiner to Dof class for Entity n and for type dof
Dof* getDof(uint32 n, Dof::dofType dof);
// return begin and end iterator of Dofs for Entity n
std::pair<std::vector<Dof>::iterator,
std::vector<Dof>::iterator> getEntityDofs(uint32 n);
void initDofTable(uint32 _numberOfEntities);
void addDof(uint32 n, std::initializer_list<Dof::dofType> _dofs);
void clearDofTable();
bool isDofUsed(uint32 n, Dof::dofType dof);
std::set<Dof::dofType> getUniqueDofTypes();
private:
uint32 numberOfUsedDofs = 0;
uint32 numberOfEntities = 0;
// vector of Dof objects
std::vector<Dof> dofs;
// array of indexes to find where dofs for particular entity is located in dofs
// Dof for entity n will be located from dofPos[n-1] included to dofPos[n] excluded
std::vector<uint32> dofPos;
// set of unique dofs used in collection
std::set<Dof::dofType> uniqueDofTypes;
};
inline uint32 DofCollection::getNumberOfUsedDofs() {
return numberOfUsedDofs;
}
inline uint32 DofCollection::getNumberOfEntities() {
return numberOfEntities;
}
inline std::pair<std::vector<Dof>::iterator,
std::vector<Dof>::iterator> DofCollection::getEntityDofs(uint32 n) {
assert(n > 0);
assert(n <= numberOfEntities);
assert(dofPos.size() > 0);
return std::make_pair(dofs.begin() + dofPos[n-1], dofs.begin() + dofPos[n]);
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "FEComponent.h"
namespace nla3d {
const char* const FEComponent::labelsOfComponent[]={"NOT DEFINED","ELEM", "NODE", "LAST"};
FEComponent::FEComponent () {
type = NOT_DEFINED;
name = "";
}
FEComponent::~FEComponent () {
list.clear();
}
FEComponent::typeOfComponent FEComponent::typeFromString(const std::string& typeName) {
for (size_t i = 1; i < LAST; i++) {
if (typeName.compare(labelsOfComponent[i]) == 0) {
return static_cast<typeOfComponent> (i);
}
}
LOG(ERROR) << "Can't find FEComponent::type by name " << typeName;
return NOT_DEFINED;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
namespace nla3d {
class FEComponent {
public:
enum typeOfComponent {
NOT_DEFINED,
ELEMENTS,
NODES,
LAST
};
static const char* const labelsOfComponent[];
FEComponent ();
~FEComponent ();
std::vector<uint32> list;
typeOfComponent type;
std::string name;
static typeOfComponent typeFromString(const std::string& typeName);
void print ();
};
inline MAKE_LOGGABLE(FEComponent, obj, os) {
os << obj.name << ": " << obj.list.size() << " " << obj.labelsOfComponent[obj.type];
return os;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "FESolver.h"
namespace nla3d {
using namespace ::nla3d::math;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// TimeControl
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
uint16 TimeControl::getCurrentStep() {
return static_cast<uint16> (convergedTimeInstances.size()+1);
}
uint16 TimeControl::getNumberOfConvergedSteps() {
return static_cast<uint16> (convergedTimeInstances.size());
}
uint16 TimeControl::getCurrentEquilibriumStep() {
return currentEquilibriumStep;
}
uint16 TimeControl::getTotalNumberOfEquilibriumSteps() {
return totalNumberOfEquilibriumSteps;
}
bool TimeControl::nextStep(double delta) {
if (currentEquilibriumStep > 0) {
equilibriumSteps.push_back(currentEquilibriumStep);
convergedTimeInstances.push_back(currentTime);
}
if (currentTime >= endTime) {
return false;
}
// if delta bigger than endTime-currentTime
if (currentTime + delta >= endTime) {
currentTimeDelta = endTime - currentTime;
} else if (currentTime + 2*delta >= endTime) {
currentTimeDelta = (endTime - currentTime)/2.0;
} else {
currentTimeDelta = delta;
}
DCHECK (currentTimeDelta > 0.0);
currentTime += currentTimeDelta;
currentEquilibriumStep = 0;
LOG(INFO) << "***** Loadstep = " << getCurrentStep() << ", Time = "
<< getCurrentTime() << " of " << getEndTime();
return true;
}
void TimeControl::nextEquilibriumStep() {
currentEquilibriumStep++;
totalNumberOfEquilibriumSteps++;
LOG(INFO) << "***** Equilibrium iteration = " << currentEquilibriumStep
<< ", Cumulutive iterations = " << totalNumberOfEquilibriumSteps;
}
double TimeControl::getCurrentTime() {
return currentTime;
}
double TimeControl::getCurrentNormalizedTime() {
return (currentTime - startTime) / (endTime - startTime);
}
double TimeControl::getEndTime() {
return endTime;
}
double TimeControl::getStartTime() {
return startTime;
}
double TimeControl::getCurrentTimeDelta() {
if (currentEquilibriumStep == 1) {
return currentTimeDelta;
} else {
return 0.0;
}
}
double TimeControl::getCurrentNormalizedTimeDelta() {
if (currentEquilibriumStep == 1) {
return currentTimeDelta / (endTime - startTime);
} else {
return 0.0;
}
}
void TimeControl::setEndTime(double _endTime) {
LOG_IF(currentTime > 0.0, ERROR) << "Trying to set end time = " << _endTime
<< " when solution is running (current time = " << currentTime << ")";
endTime = _endTime;
}
void TimeControl::setStartTime(double _startTime) {
LOG_IF(currentTime > 0.0, ERROR) << "Trying to set start time = " << _startTime
<< " when solution is running (current time = " << currentTime << ")";
startTime = _startTime;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// FESolver
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
FESolver::FESolver() {
eqSolver = defaultEquationSolver;
}
FESolver::~FESolver() {
deletePostProcessors();
}
void FESolver::attachFEStorage(FEStorage *st) {
if (storage) {
LOG(WARNING) << "FEStorage already is attached. The old one will be dropped.";
}
storage = CHECK_NOTNULL(st);
}
size_t FESolver::getNumberOfPostProcessors() {
return postProcessors.size();
}
// numbering from 0
PostProcessor& FESolver::getPostProcessor(size_t _np) {
CHECK (_np < getNumberOfPostProcessors());
return *postProcessors[_np];
}
uint16 FESolver::addPostProcessor(PostProcessor *pp) {
CHECK_NOTNULL (pp);
uint16 num = static_cast<uint16>(this->postProcessors.size()+1);
pp->nPost_proc = num;
postProcessors.push_back(pp);
return num;
}
void FESolver::deletePostProcessors() {
for (size_t i = 0; i < postProcessors.size(); i++) {
delete postProcessors[i];
}
postProcessors.clear();
}
void FESolver::attachEquationSolver(math::EquationSolver *eq) {
if (eqSolver) {
LOG(WARNING) << "EquationSolver already is attached. The old one will be dropped.";
}
eqSolver = CHECK_NOTNULL(eq);
}
void FESolver::initSolutionData() {
storage->initSolutionData();
// make easy access to global system of equations entities
// pointer to stiff. matrix
matK = storage->getK();
// pointers to DoF values vector and it parts (c - constrained DoFs, s - to be solved (unknown)
// DoFs, l - mpc's lambdas
vecU.reinit(*(storage->getU()), 0, storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc());
vecUc.reinit(*(storage->getU()), 0, storage->nConstrainedDofs());
vecUs.reinit(*(storage->getU()), storage->nConstrainedDofs(), storage->nUnknownDofs());
vecUl.reinit(*(storage->getU()), storage->nConstrainedDofs() + storage->nUnknownDofs(), storage->nMpc());
vecUsl.reinit(*(storage->getU()), storage->nConstrainedDofs(), storage->nUnknownDofs() + storage->nMpc());
vecF.reinit(*(storage->getF()), 0, storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc());
vecFc.reinit(*(storage->getF()), 0, storage->nConstrainedDofs());
vecFs.reinit(*(storage->getF()), storage->nConstrainedDofs(), storage->nUnknownDofs());
vecFl.reinit(*(storage->getF()), storage->nConstrainedDofs() + storage->nUnknownDofs(), storage->nMpc());
vecFsl.reinit(*(storage->getF()), storage->nConstrainedDofs(), storage->nUnknownDofs() + storage->nMpc());
vecR.reinit(*(storage->getR()), 0, storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc());
vecRc.reinit(*(storage->getR()), 0, storage->nConstrainedDofs());
vecRs.reinit(*(storage->getR()), storage->nConstrainedDofs(), storage->nUnknownDofs());
vecRl.reinit(*(storage->getR()), storage->nConstrainedDofs() + storage->nUnknownDofs(), storage->nMpc());
vecRsl.reinit(*(storage->getR()), storage->nConstrainedDofs(), storage->nUnknownDofs() + storage->nMpc());
if (storage->isTransient()) {
matC = storage->getC();
matM = storage->getM();
vecDU.reinit(*(storage->getDU()), 0, storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc());
vecDUc.reinit(*(storage->getDU()), 0, storage->nConstrainedDofs());
vecDUs.reinit(*(storage->getDU()), storage->nConstrainedDofs(), storage->nUnknownDofs());
vecDUl.reinit(*(storage->getDU()), storage->nConstrainedDofs() + storage->nUnknownDofs(), storage->nMpc());
vecDUsl.reinit(*(storage->getDU()), storage->nConstrainedDofs(), storage->nUnknownDofs() + storage->nMpc());
vecDDU.reinit(*(storage->getDDU()), 0, storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc());
vecDDUc.reinit(*(storage->getDDU()), 0, storage->nConstrainedDofs());
vecDDUs.reinit(*(storage->getDDU()), storage->nConstrainedDofs(), storage->nUnknownDofs());
vecDDUl.reinit(*(storage->getDDU()), storage->nConstrainedDofs() + storage->nUnknownDofs(), storage->nMpc());
vecDDUsl.reinit(*(storage->getDDU()), storage->nConstrainedDofs(), storage->nUnknownDofs() + storage->nMpc());
}
}
void FESolver::setConstrainedDofs() {
for (auto fix : fixs) {
// TODO: now support only nodal dofs..
storage->setConstrainedNodeDof(fix.node, fix.node_dof);
}
}
void FESolver::applyBoundaryConditions(double time) {
TIMED_SCOPE(t, "applyBoundaryConditions");
LOG(INFO) << "Applying boundary conditions.. (" << loads.size() << " nodal loads and "
<< fixs.size() << "nodal fixations)";
// fill nodal loads
for (auto load : loads) {
storage->addValueR(load.node, load.node_dof, load.value * time);
}
// fill nodal displacements (kinematic fixs)
for (auto fix : fixs) {
//TODO: now support only nodal dofs..
uint32 eq_num = storage->getNodeDofEqNumber(fix.node, fix.node_dof);
// To be sure that constrained DoF lays in numberOfConstrainedDofs part
assert (eq_num - 1 < storage->nConstrainedDofs());
vecUc[eq_num - 1] = fix.value * time;
}
}
void FESolver::addFix(int32 n, Dof::dofType dof, const double value) {
// NOTE: we believes that all fixBC are distinct!
// Do not pass to it the same BC twice!
fixs.push_back(fixBC(n, dof, value));
}
void FESolver::addLoad(int32 n, Dof::dofType dof, const double value) {
loads.push_back(loadBC(n, dof, value));
}
void FESolver::dumpMatricesAndVectors(std::string filename) {
std::ofstream out(filename);
matK->block(1)->writeCoordinateTextFormat(out);
matK->block(1, 2)->writeCoordinateTextFormat(out);
matK->block(2)->writeCoordinateTextFormat(out);
if (storage->isTransient()) {
matC->block(1)->writeCoordinateTextFormat(out);
matC->block(1, 2)->writeCoordinateTextFormat(out);
matC->block(2)->writeCoordinateTextFormat(out);
matM->block(1)->writeCoordinateTextFormat(out);
matM->block(1, 2)->writeCoordinateTextFormat(out);
matM->block(2)->writeCoordinateTextFormat(out);
}
vecF.writeTextFormat(out);
vecR.writeTextFormat(out);
out.close();
}
void FESolver::compareMatricesAndVectors(std::string filename, double th) {
std::ifstream in(filename);
SparseSymMatrix K1;
K1.readCoordinateTextFormat(in);
CHECK(matK->block(1)->compare(K1, th));
SparseMatrix K12;
K12.readCoordinateTextFormat(in);
CHECK(matK->block(1, 2)->compare(K12, th));
SparseSymMatrix K2;
K2.readCoordinateTextFormat(in);
CHECK(matK->block(2)->compare(K2, th));
if (storage->isTransient()) {
SparseSymMatrix C1;
C1.readCoordinateTextFormat(in);
CHECK(matC->block(1)->compare(C1, th));
SparseMatrix C12;
C12.readCoordinateTextFormat(in);
CHECK(matC->block(1, 2)->compare(C12, th));
SparseSymMatrix C2;
C2.readCoordinateTextFormat(in);
CHECK(matC->block(2)->compare(C2, th));
SparseSymMatrix M1;
M1.readCoordinateTextFormat(in);
CHECK(matM->block(1)->compare(M1, th));
SparseMatrix M12;
M12.readCoordinateTextFormat(in);
CHECK(matM->block(1, 2)->compare(M12, th));
SparseSymMatrix M2;
M2.readCoordinateTextFormat(in);
CHECK(matM->block(2)->compare(M2, th));
}
dVec fvecF;
fvecF.readTextFormat(in);
CHECK(vecF.compare(fvecF, th));
dVec fvecR;
fvecR.readTextFormat(in);
in.close();
CHECK(vecR.compare(fvecR, th));
in.close();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// LinearFESolver
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
LinearFESolver::LinearFESolver() : FESolver() {
}
void LinearFESolver::solve () {
TIMED_SCOPE(timer, "solution");
LOG(INFO) << "Start the solution process";
// std::cout << "NLA3D: Start the solution process - LinearFESolver::solve" << std::endl;
// if (storage == nullptr)
// std::cout << "NLA3D: storage == nullptr" << std::endl;
// if (eqSolver == nullptr)
// std::cout << "NLA3D: eqSolver == nullptr" << std::endl;
CHECK_NOTNULL(storage);
CHECK_NOTNULL(eqSolver);
// setup matrix properties for EquationSolver
eqSolver->setSymmetric(true);
// std::cout << "NLA3D: eqSolver->setSymmetric(true)" << std::endl;
eqSolver->setPositive(false);
// std::cout << "NLA3D: eqSolver->setPositive(false)" << std::endl;
// This is right procedures to init solution infrmation in FEStorage:
// 1. Register all DoFs that will be used in solution
storage->initDofs();
// std::cout << "NLA3D: storage->initDofs()" << std::endl;
// 2. tell FEStorage which DoFs will be fixed (constrained)
setConstrainedDofs();
// std::cout << "NLA3D: setConstrainedDofs()" << std::endl;
// 3. Perform global system eq. numbering (first goes constrained DoFs, then unknown, Mpc
// equations are last ones)
storage->assignEquationNumbers();
// std::cout << "NLA3D: storage->assignEquationNumbers()" << std::endl;
// 4. Allocate memory, initialize matrices, assign pointer on this data.
initSolutionData();
// std::cout << "NLA3D: initSolutionData()" << std::endl;
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->pre();
}
vecR.zero();
storage->assembleGlobalEqMatrices();
applyBoundaryConditions(1.0);
// we need to calculate RHS for unknowns Dofs and Mps eq.
dVec rhs(storage->nUnknownDofs() + storage->nMpc());
rhs.zero();
rhs += vecFsl;
rhs += vecRsl;
// need to take into account elimination of constrained DoFs:
matBTVprod(*(matK->block(1,2)), vecUc, -1.0, rhs);
// solve equation system
eqSolver->solveEquations(matK->block(2), rhs.ptr(), vecUsl.ptr());
// restore reaction loads for constrained DoFs.
vecRc.zero();
matBVprod(*(matK->block(1)), vecUc, 1.0, vecRc);
matBVprod(*(matK->block(1,2)), vecUsl, 1.0, vecRc);
vecRc -= vecFc;
// update results for elements
storage->updateResults();
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->process (1);
}
LOG(INFO) << "***** SOLVED *****";
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->post (1);
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// NonlinearFESolver
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
NonlinearFESolver::NonlinearFESolver() : FESolver () {
}
void NonlinearFESolver::solve() {
TIMED_SCOPE(timer, "solution");
LOG(INFO) << "Start the solution process";
// std::cout << "NLA3D: Start the solution process - NonlinearFESolver::solve" << std::endl;
// if (storage == nullptr)
// std::cout << "NLA3D: storage == nullptr" << std::endl;
// if (eqSolver == nullptr)
// std::cout << "NLA3D: eqSolver == nullptr" << std::endl;
CHECK_NOTNULL(storage);
CHECK_NOTNULL(eqSolver);
// setup matrix properties for EquationSolver
eqSolver->setSymmetric(true);
eqSolver->setPositive(false);
storage->initDofs();
setConstrainedDofs();
storage->assignEquationNumbers();
initSolutionData();
dVec rhs(storage->nUnknownDofs() + storage->nMpc());
// need to store obtained constrained DoFs values obtained on previous equilibrium step in order
// to compute deltaUc for incremental approach
dVec Ucprev(storage->nConstrainedDofs());
Ucprev.zero();
// deltaU vector of unknowns for incremental approach
dVec deltaU(storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc());
dVec deltaUc(deltaU, 0, storage->nConstrainedDofs());
dVec deltaUsl(deltaU, storage->nConstrainedDofs(), storage->nUnknownDofs() + storage->nMpc());
dVec deltaUs(deltaU, storage->nConstrainedDofs(), storage->nUnknownDofs());
dVec deltaUl(deltaU, storage->nConstrainedDofs() + storage->nUnknownDofs(), storage->nMpc());
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->pre();
}
double currentCriteria = 0.0;
double timeDelta = (timeControl.getEndTime() - timeControl.getStartTime()) / numberOfLoadsteps;
while (timeControl.nextStep(timeDelta)) {
bool converged = false;
for (;;) {
timeControl.nextEquilibriumStep();
vecR.zero();
storage->assembleGlobalEqMatrices();
applyBoundaryConditions(timeControl.getCurrentNormalizedTime());
deltaUc = vecUc - Ucprev;
rhs.zero();
rhs += vecFsl;
rhs += vecRsl;
// nConstr x (nUnknown + nMPC)
matBTVprod(*(matK->block(1,2)), deltaUc, -1.0, rhs);
// solve equation system
eqSolver->solveEquations(matK->block(2), rhs.ptr(), deltaUsl.ptr());
// restore DoF values from increments
vecUs += deltaUs;
vecUl = deltaUl;
// restore constrained DoFs reactions
vecRc.zero();
matBVprod(*(matK->block(1)), deltaUc, 1.0, vecRc);
matBVprod(*(matK->block(1,2)), deltaUsl, 1.0, vecRc);
vecRc -= vecFc;
Ucprev = vecUc;
storage->updateResults();
// calculate convergence criteria
currentCriteria = calculateCriteria(deltaUs);
// TODO: 1. It seems that currentCriteria is already normalized in calculateCriteria(). we
// need to compare currentCriteria with 1.0
// TODO: 2. Current convergence criteria is not good. We need to introduce equilibrium balance
// criteria too along with kinematic one.
if (currentCriteria < convergenceCriteria) {
converged = true;
break;
}
LOG_IF(currentCriteria > 1.0e6 || std::isnan(currentCriteria), FATAL) << "The solution is diverged!";
if (timeControl.getCurrentEquilibriumStep() >= numberOfIterations)
break;
}//iterations
LOG_IF(!converged, FATAL) << "The solution is not converged with "
<< timeControl.getCurrentEquilibriumStep() << " equilibrium iterations";
LOG(INFO) << "Loadstep " << timeControl.getCurrentStep() << " completed with " << timeControl.getCurrentEquilibriumStep();
// TODO: figure out why TIMED_BLOCK doesn't work here..
// TIMED_BLOCK(t, "PostProcessor::process") {
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->process (timeControl.getCurrentStep());
}
// }
} //loadsteps
LOG(INFO) << "***** SOLVED *****";
// TIMED_BLOCK(t, "PostProcessor::post") {
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->post (timeControl.getCurrentStep());
}
// }
}
double NonlinearFESolver::calculateCriteria(dVec& delta) {
double curCriteria = 0.0;
for (uint32 i = 0; i < delta.size(); i++) {
curCriteria += fabs(delta[i]);
}
curCriteria /= delta.size();
curCriteria = curCriteria / convergenceCriteria;
LOG(INFO) << "Solution delta criteria = " << curCriteria;
return curCriteria;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// LinearTransientFESolver
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
LinearTransientFESolver::LinearTransientFESolver() : FESolver() {
}
void LinearTransientFESolver::solve() {
TIMED_SCOPE(timer, "solution");
LOG(INFO) << "Start the solution process";
CHECK_NOTNULL(storage);
CHECK_NOTNULL(eqSolver);
// setup matrix properties for EquationSolver
eqSolver->setSymmetric(true);
eqSolver->setPositive(false);
storage->setTransient(true);
double dt = (time1 - time0) / numberOfTimesteps;
double curTime = time0 + dt;
uint16 curTimestep = 1;
// initialize parameters of Newmark procedure
a0 = 1.0 / (alpha * dt * dt);
a1 = delta / (alpha * dt);
a2 = 1.0 / (alpha * dt);
a3 = 1.0 / (2.0 * alpha) - 1.0;
a4 = delta / alpha - 1.0;
a5 = dt / 2.0 * (delta / alpha - 2.0);
a6 = dt * (1.0 - delta);
a7 = delta * dt;
storage->initDofs();
setConstrainedDofs();
storage->assignEquationNumbers();
initSolutionData();
vecR.zero();
uint32 nEq = storage->nUnknownDofs() + storage->nMpc();
uint32 nAll = storage->nConstrainedDofs() + storage->nUnknownDofs() + storage->nMpc();
dVec vecUnext(nAll);
dVec vecUnextc(vecUnext, 0, storage->nConstrainedDofs());
dVec vecUnextsl(vecUnext, storage->nConstrainedDofs(), nEq);
dVec vecDUnext(nAll);
dVec vecDDUnext(nAll);
math::SparseSymMatrix matKmod(matK->block(2)->getSparsityInfo());
dVec rhs(nEq);
// TODO: this hangs the program: vecU = initValue;
for (uint32 i = 0; i < vecU.size(); i++) {
vecU[i] = initValue;
}
vecDU.zero();
vecDDU.zero();
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->pre();
}
storage->assembleGlobalEqMatrices();
applyBoundaryConditions(1.0);
// dummy implementation of `matKmod = a0 * matM + a1 * matC + matK`
for (uint32 i = 0; i < matKmod.nValues(); i++) {
matKmod.getValuesArray()[i] = a0 * matM->block(2)->getValuesArray()[i] +
a1 * matC->block(2)->getValuesArray()[i] +
matK->block(2)->getValuesArray()[i];
}
// factorize eKmode just once as far sa we have a deal with linear system
eqSolver->factorizeEquations(&matKmod);
// timestepping
while (curTime <= time1) {
for (;;) {
rhs = vecFsl + vecRsl;
matBVprod(*(matM->block(2)), a0*vecUsl + a2*vecDUsl + a3*vecDDUsl, 1.0, rhs);
matBVprod(*(matC->block(2)), a1*vecUsl + a4*vecDUsl + a5*vecDDUsl, 1.0, rhs);
// copy constrained dofs values
vecUnextc = vecUc;
// solve equation system
eqSolver->substituteEquations(&matKmod, &rhs[0], &vecUnextsl[0]);
break;
}//iterations
// restore derivatives
vecDDUnext = a0 * (vecUnext - vecU) - a2 * vecDU - a3 * vecDDU;
vecDUnext = vecDU + a6 * vecDDUnext + a7 * vecDDU;
vecU = vecUnext;
vecDU = vecDUnext;
vecDDU = vecDDUnext;
storage->updateResults();
LOG(INFO) << "Time " << curTime << " completed";
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->process(curTimestep);
}
curTimestep++;
curTime += dt;
} //timesteps
LOG(INFO) << "***** SOLVED *****";
for (size_t i = 0; i < getNumberOfPostProcessors(); i++) {
postProcessors[i]->post(curTimestep);
}
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
#include "math/Vec.h"
#include "math/EquationSolver.h"
#include "FEStorage.h"
#include "PostProcessor.h"
#include <Eigen/Dense>
namespace nla3d {
using namespace math;
class FEStorage;
class PostProcessor;
// TimeControl class helps to control time stepping and equilibrium iterations.
// Before solution one can setup setStartTime() and setEndTime() and then perform time stepping with
// nextStep(delta) where delta is solver specific time step. For every time step many equilibrium
// iterations could be performed by nextEquilibriumStep(). All intermediate time steps are stored in
// convergedTimeInstances, for every time steps number of equilibrium steps are stored in
// equilibriumSteps.
class TimeControl {
public:
uint16 getCurrentStep ();
uint16 getNumberOfConvergedSteps ();
uint16 getCurrentEquilibriumStep ();
uint16 getTotalNumberOfEquilibriumSteps ();
bool nextStep (double delta);
void nextEquilibriumStep ();
double getCurrentTime ();
double getCurrentNormalizedTime ();
double getEndTime ();
double getStartTime ();
double getCurrentTimeDelta ();
double getCurrentNormalizedTimeDelta ();
void setEndTime (double _endTime);
void setStartTime (double _startTime);
protected:
std::list<double> convergedTimeInstances;
std::list<uint16> equilibriumSteps;
uint16 currentEquilibriumStep = 0;
uint16 totalNumberOfEquilibriumSteps = 0;
double currentTimeDelta = 0.0;
double endTime = 1.0;
double startTime = 0.0;
double currentTime = 0.0;
};
// The abstract class that represents the FE solver. This class use information and methods from
// FEStorage in order to apply particular solution scheme. For example, here are already some of
// them: solver for linear steady FE model with concentrated loads and fixed DoFs (LinearFESolver),
// solver for non-linear quasi-steady FE model based on incremental approach (NonlinearFESolver),
// solver for linear transient FE model based on Newmark time-integration scheme
// (LinearTransientFESolver).
//
// FESolver keeps a pointer to FEStorage to receive assembled global equation system, and a pointer
// to math::EquationSolver to solve linear system of equations.
//
// FESovler manages PostProcessors objects in order to call them on every time step to do some
// useful routines (write res files, log reaction forces, etc.)
//
// FESolver by default contains arrays of loadBC and fixBC structures in order to apply constant BC
// on DoFs (concentrated load and fixed DoF value). But particular realizations of FESolver can
// incorporate time-depended BC along with or instead of loadBC and fixBC.
//
// FESolver by mean of initSolutionData() keeps pointers to FEStorage matrices and vectors. matK for
// stiffness matrix, matC for dumping matrix, matM for inertia matrix. vecU - for DoF values vector
// with references to particular parts of it: vecUc(part for constrained DoFs), vecUs(part for
// unknown DoFs), vecUl(part for Lagrangian lambdas from Mpc equations), vecUsl(combination of vecUs and
// vecUl). The same for first and second time derivatives: vecDU, vecDDU.
// Also there are references to RHS parts: vecF - for FE internal loads. vecFl has special treatment
// as RHS of Mpc equations. vecR - for external concentrated loads. vecRc is treated as reaction
// forces of fixation for constrained DoFs, vecRs is treated as external concetrated loads and vecRl
// should be zero all times. All references above are just easy way to access FEStorage internal
// data. Actually all this vectors and matrices ale allocated inside of FEStorage. By performing
// FEStorage::assembleGlobalEqMatrices() matK/C/M, vecF are assembled with particular numbers based
// on FE element formulation and on Mpc equations. Other entities (vecU, vecDU, vecDDU, vecR) are
// fully under FESovler control. FESolver is responsible on timely updating this values.
//
class FESolver {
public:
FESolver ();
virtual ~FESolver ();
void attachFEStorage(FEStorage *st);
void attachEquationSolver(math::EquationSolver *eq);
// main method were all solution scheme specific routines are performed
virtual void solve() = 0;
size_t getNumberOfPostProcessors();
// get an instance of PostProcessor by its number
// _np >= 0
PostProcessor& getPostProcessor(size_t _np);
// The function stores PostProcessor pointer in FESolver. FESovler will free the memory by itself.
uint16 addPostProcessor(PostProcessor *pp);
void deletePostProcessors();
// run FEStorage procedures for initialization solution data structures and map matK, matC, matM,
// vecU, vecDU, vecDDU, vecR, vecF pointer on FEStorage's allocated structures.
void initSolutionData();
// The function applies boundary conditions stored in `loads` and `fixs` arrays.
// `time` should be normalized(in range [0.0; 1.0])
void applyBoundaryConditions(double time);
// constrained DoFs has special treatment in FEStorage, that's why before initialization of
// solution data we need to tell FEStorage which DoFs is constrained. setConstrainedDofs() does
// this work based on `fixs` array.
void setConstrainedDofs();
// add DoF fixation (constraint) boundary condition
void addFix(int32 n, Dof::dofType dof, const double value = 0.0);
// add DoF load (force) boundary condition
void addLoad(int32 n, Dof::dofType dof, const double value = 0.0);
// for debug purpose:
// dump matrices matK, matC, matM and vectors vecF, vecR
void dumpMatricesAndVectors(std::string filename);
// read matrices matK, matC, matM and vectors vecF, vecR from file and compare them with
// solution instances
void compareMatricesAndVectors(std::string filename, double th = 1.0e-9);
protected:
FEStorage* storage = nullptr;
math::EquationSolver* eqSolver = nullptr;
// Array of PostProcessors. Here is a conception of PostProcessors that can do useful work every
// iteration of the solution. For example here is a VtkProcessor class to write *.vtk file with
// model and solution data (displacements, stresses and others). An instance of PostProcessor
// class is created outside of FESolver. But with function addPostProcessor(..) FESolver take
// control on the PostProcessor instance. The memory released in deletePostProcessors().
std::vector<PostProcessor*> postProcessors;
// the references on FEStorage FE data structures which is frequently used by FESolver. This
// references make it easy to operate with important FE data structures.
BlockSparseSymMatrix<2>* matK = nullptr;
BlockSparseSymMatrix<2>* matC = nullptr;
BlockSparseSymMatrix<2>* matM = nullptr;
// vecU is a reference on a whole DoF values vector, but vecUc, vecUs, vecUl, vecUsl are
// references on parts of the full vector. Some times it's handy to operate with the particular
// part only.
dVec vecU;
dVec vecUc;
dVec vecUs;
dVec vecUl;
dVec vecUsl;
// for first time derivatives
dVec vecDU;
dVec vecDUc;
dVec vecDUs;
dVec vecDUl;
dVec vecDUsl;
// for second time derivatives
dVec vecDDU;
dVec vecDDUc;
dVec vecDDUs;
dVec vecDDUl;
dVec vecDDUsl;
// for internal element loads
dVec vecF;
dVec vecFc;
dVec vecFs;
dVec vecFl;
dVec vecFsl;
// for external loads
dVec vecR;
dVec vecRc;
dVec vecRs;
dVec vecRl;
dVec vecRsl;
// List of concentrated load boundary conditions (addition to RHS of global eq. system)
std::list<loadBC> loads;
// List of prescribed values for DoFs.
// NOTE: DoFs with fixed values are treated in nla3d in a special manner: this DoFs are eliminated from
// global solve system and then reaction loads (vecRc vector) of a such DoFs are found.
std::list<fixBC> fixs;
};
// particular realization of FESolver for linear tasks
class LinearFESolver : public FESolver {
public:
LinearFESolver();
virtual void solve();
};
// Iterative solver for Full Newton-Raphson procedure
// The convergence is controlled by mean increment of DoF values
class NonlinearFESolver : public FESolver {
public:
NonlinearFESolver();
TimeControl timeControl;
uint16 numberOfIterations = 20;
uint16 numberOfLoadsteps = 10;
double convergenceCriteria = 1.0e-3;
virtual void solve();
protected:
double calculateCriteria(dVec& delta);
};
// Solver for time integration of linear systems: M * DDU + C * DU + K * U = F + R
// use Newmark scheme (based on section 9.2.4. Bathe K.J., Finite Element Procedures, 1997)
class LinearTransientFESolver : public FESolver {
public:
LinearTransientFESolver();
uint16 numberOfTimesteps = 100;
double alpha = 0.25; // trapezoidal rule by default
double delta = 0.5;
// start time
double time0 = 0.0;
// end time
double time1 = 1.0;
// initial values for vecUc
double initValue = 0.0;
virtual void solve ();
double a0, a1, a2, a3, a4, a5, a6, a7;
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "FEStorage.h"
#include "elements/element.h"
namespace nla3d {
using namespace math;
FEStorage::FEStorage() {
};
FEStorage::~FEStorage () {
if (material) {
delete material;
}
deleteSolutionData();
deleteMesh();
}
void FEStorage::assembleGlobalEqMatrices() {
assert(matK);
assert(matK->isCompressed());
TIMED_SCOPE(t, "assembleGlobalEqMatrix");
LOG(INFO) << "Start formulation of global eq. matrices ( " << nElements() << " elements)";
zeroK();
zeroF();
for (uint32 el = 0; el < nElements(); el++) {
elements[el]->buildK();
}
//t.checkpoint("Element::build()");
// because of non-linear MPC we need to update
// MPC coefficients every step
for (size_t i = 0; i < mpcCollections.size(); i++) {
mpcCollections[i]->update();
// DEBUG:
// mpcCollections[i]->printEquations(std::cout);
}
//t.checkpoint("MpcCollection::update()");
// loop over mpc equations and add corresponding terms into global eq. system
for (auto mpc : mpcs) {
uint32 eq_num = mpc->eqNum;
assert(eq_num > 0);
assert(eq_num <= vecF.size());
assert(eq_num <= nDofs() + nMpc());
vecF[eq_num - 1] = mpc->b;
for (auto term : mpc->eq) {
addValueMPC(mpc->eqNum, term.node, term.node_dof, term.coef);
}
}
// if it was demanded to have transient matrices (C and M)
if (transient) {
assert(matC && matM);
assert(matC->isCompressed());
assert(matM->isCompressed());
zeroC();
zeroM();
for (uint32 el = 0; el < nElements(); el++) {
elements[el]->buildC();
elements[el]->buildM();
}
}
}
void FEStorage::setConstrainedNodeDof(uint32 node, Dof::dofType dtype) {
Dof* dof = nodeDofs.getDof(node, dtype);
assert(dof);
if (dof->isConstrained) {
return;
} else {
dof->isConstrained = true;
_nConstrainedDofs++;
}
}
void FEStorage::setConstrainedElementDof(uint32 el, Dof::dofType dtype) {
Dof* dof = elementDofs.getDof(el, dtype);
assert(dof);
if (dof->isConstrained) {
return;
} else {
dof->isConstrained = true;
_nConstrainedDofs++;
}
}
double FEStorage::getReaction(uint32 node, Dof::dofType dof) {
if (isNodeDofUsed(node, dof)) {
uint32 eq_num = getNodeDofEqNumber(node, dof);
return getReaction(eq_num);
} else {
return 0.0;
}
}
double FEStorage::getReaction(uint32 eq) {
assert(eq > 0 && eq <= vecR.size());
// NOTE: vecR contains not only reactions but also external forces (for eq > nConstrainedDofs())
// so this function will return external force for eq > nConstrainedDofs()
return vecR[eq - 1];
}
Material* FEStorage::getMaterial() {
assert(material);
return material;
}
void FEStorage::getElementNodes(uint32 el, Node** node_ptr) {
assert(el <= nElements());
Element* elp = elements[el-1];
for (uint16 i=0; i<elp->getNNodes(); i++)
node_ptr[i] = nodes[elp->getNodeNumber(i)-1];
}
// prt array always has 3 elements
void FEStorage::getNodePosition(uint32 n, double* ptr, bool deformed) {
assert(n > 0 && n <= nNodes());
for (uint16 i = 0; i < 3; i++) {
ptr[i] = nodes[n-1]->pos[i];
}
if (deformed) {
if (isNodeDofUsed(n, Dof::UX)) {
ptr[0] += getNodeDofSolution(n, Dof::UX);
}
if (isNodeDofUsed(n, Dof::UY)) {
ptr[1] += getNodeDofSolution(n, Dof::UY);
}
if (isNodeDofUsed(n, Dof::UZ)) {
ptr[2] += getNodeDofSolution(n, Dof::UZ);
}
}
}
FEComponent* FEStorage::getFEComponent(size_t i) {
assert(i < feComponents.size());
return feComponents[i];
}
FEComponent* FEStorage::getFEComponent(const std::string& name) {
for (size_t i = 0; i < feComponents.size(); i++) {
if (name.compare(feComponents[i]->name) == 0) {
return feComponents[i];
}
}
LOG(WARNING) << "Can't find a component with name " << name;
return NULL;
}
void FEStorage::addMpc (Mpc* mpc) {
assert (mpc);
assert (mpc->eq.size() > 0);
mpcs.push_back(mpc);
}
void FEStorage::addMpcCollection (MpcCollection* mpcCol) {
assert(mpcCol);
mpcCollections.push_back(mpcCol);
}
void FEStorage::addFEComponent (FEComponent* comp) {
assert(comp);
feComponents.push_back(comp);
}
void FEStorage::addNode (Node* node) {
//Node() fires Vec<3> constructor, thus Node coordinates are (0,0,0) by default
//TODO: try-catch of memory overflow
nodes.push_back(node);
}
std::vector<uint32> FEStorage::createNodes (uint32 _nn) {
//Node() fires Vec<3> constructor, thus Node coordinates are (0,0,0) by default
//TODO: try-catch of memory overflow
std::vector<uint32> newIndexes;
newIndexes.reserve(_nn);
uint32 nextNumber = nodes.size() + 1;
nodes.reserve(elements.size() + _nn);
for (uint32 i = 0; i < _nn; i++) {
Node* pnode = new Node;
nodes.push_back(pnode);
}
for (uint32 i = nextNumber; i <= nodes.size(); i++) {
newIndexes.push_back(i);
}
return newIndexes;
}
void FEStorage::addElement (Element* el) {
el->storage = this;
el->elNum = nElements() + 1;
//TODO: try-catch of memory overflow
elements.push_back(el);
}
std::vector<uint32> FEStorage::createElements(uint32 _en, ElementType elType) {
//TODO: catch if not enough memory
std::vector<uint32> newIndexes;
newIndexes.reserve(_en);
uint32 nextNumber = elements.size() + 1;
ElementFactory::createElements(elType, _en, elements);
for (uint32 i = nextNumber; i <= elements.size(); i++) {
//access elNum protected values as friend
elements[i - 1]->elNum = i;
elements[i - 1]->storage = this;
newIndexes.push_back(i);
}
return newIndexes;
}
void FEStorage::deleteMesh() {
deleteElements();
deleteNodes();
deleteMpcs();
deleteMpcCollections();
deleteFeComponents();
topology.clear();
}
void FEStorage::deleteNodes() {
for (uint32 i = 0; i < nNodes(); i++) {
delete nodes[i];
}
nodes.clear();
}
void FEStorage::deleteElements() {
for (uint32 i = 0; i < nElements(); i++) {
delete elements[i];
}
elements.clear();
}
void FEStorage::deleteMpcs() {
list<Mpc*>::iterator mpc = mpcs.begin();
while (mpc != mpcs.end()) {
delete *mpc;
mpc++;
}
}
void FEStorage::deleteMpcCollections() {
for (size_t i = 0; i < mpcCollections.size(); i++) {
delete mpcCollections[i];
}
mpcCollections.clear();
}
void FEStorage::deleteFeComponents() {
for (size_t i = 0; i < feComponents.size(); i++) {
delete feComponents[i];
}
feComponents.clear();
}
void FEStorage::deleteDofArrays() {
elementDofs.clearDofTable();
nodeDofs.clearDofTable();
}
void FEStorage::deleteSolutionData() {
_nDofs = 0;
_nConstrainedDofs = 0;
_nUnknownDofs = 0;
if (matK) {
delete matK;
matK = nullptr;
}
if (matC) {
delete matC;
matC = nullptr;
}
if (matM) {
delete matM;
matM = nullptr;
}
deleteDofArrays();
vecU.clear();
vecDU.clear();
vecDDU.clear();
vecR.clear();
vecF.clear();
}
void FEStorage::listFEComponents() {
for (size_t i = 0; i < feComponents.size(); i++) {
LOG(INFO) << *feComponents[i];
}
}
void FEStorage::initDofs() {
TIMED_SCOPE(t, "initDofs");
if (nNodes() < 1 && nElements() < 1) {
LOG(FATAL) << "No any nodes or elements";
}
nodeDofs.initDofTable(nNodes());
elementDofs.initDofTable(nElements());
for (uint32 el = 0; el < nElements(); el++) {
elements[el]->pre();
}
for (size_t i = 0; i < mpcCollections.size(); i++) {
mpcCollections[i]->pre();
mpcCollections[i]->registerMpcsInStorage();
}
// Total number of dofs (only registered by elements)
_nDofs = elementDofs.getNumberOfUsedDofs() + nodeDofs.getNumberOfUsedDofs();
// std::cout << "NLA3D: START checking _nDofs, _nDofs size = " << _nDofs << std::endl;
CHECK(_nDofs);
// std::cout << "NLA3D: STOP checking _nDofs" << std::endl;
auto udofs = getUniqueNodeDofTypes();
if (udofs.size()) {
std::stringstream ss;
ss << "Types of nodal DoFs:";
for (auto type : udofs)
ss << " " << Dof::dofType2label(type);
LOG(INFO) << ss.str();
}
LOG(INFO) << "Number of nodal DoFs: " << nodeDofs.getNumberOfUsedDofs();
udofs = getUniqueElementDofTypes();
if (udofs.size()) {
std::stringstream ss;
ss << "Types of element DoFs:";
for (auto type : udofs)
ss << " " << Dof::dofType2label(type);
LOG(INFO) << ss.str();
}
LOG(INFO) << "Number of element DoFs: " << elementDofs.getNumberOfUsedDofs();
}
void FEStorage::assignEquationNumbers() {
// _nUnknownDofs - number of Dof need to be found on every step
_nUnknownDofs = nDofs() - nConstrainedDofs();
CHECK(_nUnknownDofs);
uint32 next_eq_solve = nConstrainedDofs() + 1;
uint32 next_eq_const = 1;
// TODO: we can try to do numbering in more efficient way if will loop over element nodes (as it
// does in buildK() procedure)
for (uint32 i = 1; i <= nElements(); i++) {
for (uint16 it = 0; it < Dof::numberOfDofTypes; it++) {
Dof::dofType t = static_cast<Dof::dofType> (it);
Dof* d = elementDofs.getDof(i, t);
if (d) {
if (d->isConstrained) {
d->eqNumber = next_eq_const++;
} else {
d->eqNumber = next_eq_solve++;
}
}
}
}
for (uint32 i = 1; i <= nNodes(); i++) {
for (uint16 it = 0; it < Dof::numberOfDofTypes; it++) {
Dof::dofType t = static_cast<Dof::dofType> (it);
Dof* d = nodeDofs.getDof(i, t);
if (d) {
if (d->isConstrained) {
d->eqNumber = next_eq_const++;
} else {
d->eqNumber = next_eq_solve++;
}
}
}
}
assert(next_eq_const - 1 == nConstrainedDofs());
assert(next_eq_solve - 1 == nDofs());
for (auto mpc : mpcs) {
assert(mpc->eq.size());
mpc->eqNum = next_eq_solve++;
}
assert(next_eq_solve - 1 == nDofs() + nMpc());
LOG(INFO) << "DoFs = " << nDofs() << ", constrained DoFs = " << nConstrainedDofs() << ", MPC eq. = "
<< nMpc() << ", TOTAL eq. = " << nUnknownDofs() + nMpc();
}
void FEStorage::initSolutionData () {
TIMED_SCOPE(t, "initSolutionData");
// We need to know topology of the mesh in order to determine SparsityInfo for sparse matrices
learnTopology();
// In nla3d solution procedure there are 3 distinguish types of unknowns. First one "c" -
// constrained DoFs, and consequently known at solution time. Second one "s" - unknown DoFs (need
// to be solved). And last one "lambda" - Lagrange multipliers for applied MPC constraints.
// Equations numbers assigned in a such manner that constrained DoFs equations go first, thus the
// global System of Linera Algebraic Equations looks llike this:
//
// | Kcc | KcsMPCc | | Uc | | Fc | | Rc |
// |-------|--------------| |-------| | | | |
// | | | * | Us | = | Fs | + | Rs |
// |KcsMPCc| KssMPCs | |-------| | | | |
// | ^T | | | Ul | | Fl | | Rl |
//
// 1. But really only
//
// | | | Us | | |
// | KssMPCs | * |-------| = |RHS |
// | | | Ul | | |
//
// will be be solved by eq. solver.
//
// where:
//
// | | | Fs | | Rs | | | | |
// |RHS | = | | + | | - |KcsMPCc^T| * | Uc |
// | | | Fl | | Rl | | | | |
//
// 2. Then to restore reaction loads:
//
// | | | | | | | | | | | Us |
// | Rc | =-| Fc | + | Kcc | * | Uc | + |KcsMPCc| * | |
// | | | | | | | | | | | Ul |
matK = new BlockSparseSymMatrix<2>({nConstrainedDofs(), nUnknownDofs() + nMpc()});
if (transient) {
// share sparsity info with K matrices
matC = new BlockSparseSymMatrix<2>(matK);
matM = new BlockSparseSymMatrix<2>(matK);
}
// initialize solutions vectors
vecU.reinit(nDofs()+nMpc());
if (transient) {
vecDU.reinit(nDofs()+nMpc());
vecDDU.reinit(nDofs()+nMpc());
}
vecR.reinit(nDofs()+nMpc());
vecF.reinit(nDofs()+nMpc());
if (!material) {
LOG(WARNING) << "FEStorage::initializeSolutionData: material isn't defined";
}
// Need to restore non-zero entries in Sparse Matrices based on mesh topology and registered Dofs
// As far as we know from topology which elements are neighbors to each other we can estimate
// quantity and positions of non-zero coef. in Sparse Matrix matK. Other matrices matC, matK will
// have the same sparsity as stiffness matrix matK.
for (uint32 nn = 1; nn <= nNodes(); nn++) {
auto nn_dofs = nodeDofs.getEntityDofs(nn);
for (auto en : topology[nn-1]) {
// register element dofs to node nn
auto en_dofs = elementDofs.getEntityDofs(en);
for (auto d1 = en_dofs.first; d1 != en_dofs.second; d1++)
for (auto d2 = nn_dofs.first; d2 != nn_dofs.second; d2++)
addEntryK(d1->eqNumber, d2->eqNumber);
// cycle over element en Nodes and register nn vs nn2 nodes dofs
for (uint16 enn = 0; enn < getElement(en).getNNodes(); enn++) {
uint32 nn2 = getElement(en).getNodeNumber(enn);
if (nn2 < nn) continue;
// register node nn2 dofs to node nn dofs
auto nn2_dofs = nodeDofs.getEntityDofs(nn2);
for (auto d1 = nn2_dofs.first; d1 != nn2_dofs.second; d1++)
for (auto d2 = nn_dofs.first; d2 != nn_dofs.second; d2++)
addEntryK(d1->eqNumber, d2->eqNumber);
}
}
}
// register element dofs vs element dofs
for (uint32 en = 1; en <= nElements(); en++) {
auto en_dofs = elementDofs.getEntityDofs(en);
for (auto d1 = en_dofs.first; d1 != en_dofs.second; d1++)
for (auto d2 = en_dofs.first; d2 != en_dofs.second; d2++)
addEntryK(d1->eqNumber, d2->eqNumber);
}
// register MPC coefficients
for (auto mpc : mpcs) {
assert(mpc->eq.size());
uint32 eq_num = mpc->eqNum;
for (auto term : mpc->eq) {
uint32 eq_j = getNodeDofEqNumber(term.node, term.node_dof);
addEntryMPC(mpc->eqNum, eq_j);
}
}
// compress sparsity info. After that we can't add new position in sparse matrices
matK->compress();
if (transient) {
matC->compress();
matM->compress();
}
}
void FEStorage::printDofInfo(std::ostream& out) {
for (uint32 en = 1; en <= nElements(); en++) {
auto en_dofs = elementDofs.getEntityDofs(en);
for (auto d1 = en_dofs.first; d1 != en_dofs.second; d1++)
out << "E" << en << ":" << Dof::dofType2label(d1->type) << " eq = " << d1->eqNumber
<< " constrained = " << d1->isConstrained << endl;
}
for (uint32 nn = 1; nn <= nNodes(); nn++) {
auto nn_dofs = nodeDofs.getEntityDofs(nn);
for (auto d1 = nn_dofs.first; d1 != nn_dofs.second; d1++)
out << "N" << nn << ":" << Dof::dofType2label(d1->type) << " eq = " << d1->eqNumber
<< " constrained = " << d1->isConstrained << endl;
}
}
void FEStorage::updateResults() {
TIMED_SCOPE(t, "updateSolutionResults");
// calculate element's update procedures (calculate stresses, strains, ..)
for (uint32 el = 0; el < nElements(); el++) {
elements[el]->update();
}
}
void FEStorage::learnTopology() {
topology.clear();
topology.assign(nNodes(), std::set<uint32>());
for (uint32 en = 1; en <= nElements(); en++) {
for (uint16 nn = 0; nn < getElement(en).getNNodes(); nn++) {
uint32 noden = getElement(en).getNodeNumber(nn);
topology[noden-1].insert(getElement(en).getElNum());
}
}
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include <list>
#include "sys.h"
#include "materials/MaterialFactory.h"
#include "elements/ElementFactory.h"
#include "math/BlockSparseMatrix.h"
#include "FEComponent.h"
#include "Mpc.h"
namespace nla3d {
class Element;
class Node;
class Dof;
class ElementFactory;
// FEStorage - heart of the nla3d program, it contains all FE data:
// * nodes array `nodes`, elements array `elements`,
// * nodal DoFs array `elementDofs`, element DoFs array `nodeDofs`,
// * solution matrices and vectors (`matK`, `vecU`, `vecF`, `vecR` and others),
// * mesh topology (connectivity) info `topology`,
// * mpc equations `mpcs`.
//
// FEStorage provides methods to fill and work with FE data described above:
// * methods to add nodes and elements to FEStorage
// * methods to initialize solution data and provide it to FESolver
// * methods to assemble global system of equations in form of: M * DDU + C * DU + K * U = F + R
// * a lot of getters to get FE data
//
// NOTE: Numbering. Node, element, MPC equation, Equation numbers are started from 1.
// NOTE: FEStorage works with dense node, element arrays. That means that element/node numbers
// should start from 1 and up to nElements()/nNodes().
class FEStorage {
public:
FEStorage();
~FEStorage();
// A pointer to Material instance. nla3d supports only one material for FE model. External code
// should create material instance and pass it to FEStorage. Then FEStorage takes a control on
// material. The material will be deleted in ~FEStorage().
// NOTE: material conception should be implement in Element class. In later updates
// FEStorage::Material will be deleted.
Material* material = nullptr;
// *** Operations for preparing the global system of equations ***
//
// These functions add a value to an entry of global system of equations matrices (stiffness,
// damping, inertia).
// For example: addValueK(32, Dof::UX, 42, Dof::UZ, 0.32) will add 0.32 to a corresponding
// entry in a global stiffness matrix.
// NOTE: node > 0
// NOTE: use addValueK(uint32 nodei, ...) to add nodal Dof vs nodal Dof value
// or use addValueK(uint32 eqi, ..) to add value for equation number i vs equation number j (more general case)
void addValueK(uint32 nodei, Dof::dofType dofi, uint32 nodej, Dof::dofType dofj, double value);
void addValueK(uint32 eqi, uint32 eqj, double value);
// for damping matrix
void addValueC(uint32 nodei, Dof::dofType dofi, uint32 nodej, Dof::dofType dofj, double value);
void addValueC(uint32 eqi, uint32 eqj, double value);
// for mass matrix
void addValueM(uint32 nodei, Dof::dofType dofi, uint32 nodej, Dof::dofType dofj, double value);
void addValueM(uint32 eqi, uint32 eqj, double value);
// Add value to a matrix of MPC coefficients for DoFs.
// NOTE: MPC equations can work only with vecU values, excluding derivatives values
// vecDU, vecDDU.
void addValueMPC(uint32 eq_num, uint32 nodej, Dof::dofType dofj, double coef);
void addValueMPC(uint32 eq_num, uint32 eqj, double coef);
// Add value to vecF (term of RHS of global equations system representing internal element loads)
// NOTE: use addValueF(uint32 nodei, ...) to add value to vecF for nodal Dof
// or use addValueF(uint32 eqi, ..) to add value for equation number i (more general case)
// NOTE: common way is that element's assemble procedure contributes to vecF by mean of these
// functions
void addValueF(uint32 nodei, Dof::dofType dofi, double value);
void addValueF(uint32 eqi, double value);
// Add value to vecR (term of RHS of global equations system representing external loads)
// NOTE: common way is that FESolver or user code contribute to vecR by mean of these
// functions
void addValueR(uint32 nodei, Dof::dofType dofi, double value);
void addValueR(uint32 eqi, double value);
// fill with zeros the matrices of global system of equations
void zeroK();
void zeroC();
void zeroM();
// fill with zeros the vector vecF
void zeroF();
// get the K, C, M matrices (stiffness, damping, inertia) of the global system of equations. In current realization the
// matrix is symmetric, but semi-positive defined, because of MPC equations (zeros on diagonals in
// Lagrangian columns x rows)
math::BlockSparseSymMatrix<2>* getK();
math::BlockSparseSymMatrix<2>* getC();
math::BlockSparseSymMatrix<2>* getM();
// get RHS vectors of the global system of equations
math::dVec* getF();
math::dVec* getR();
// get DoF values vectors
math::dVec* getU();
math::dVec* getDU();
math::dVec* getDDU();
// Procedure of filling global equations system matrices and RHS vectors with actual values.
// if isTransient() == bool then matC, matM are also assembled.
void assembleGlobalEqMatrices();
// getters to get numbers of different entities stored in FEStorage
uint32 nNodes();
uint32 nElements();
uint32 nDofs();
uint32 nUnknownDofs();
uint32 nConstrainedDofs();
uint32 nMpc();
// if isTransient() == bool then FEStorage initialize matC, matM, vecDU, vecDDU along with matK,
// vecU.
void setTransient(bool _transient);
bool isTransient();
// Operations with DoFs
//
// Registation of DoFs is a key moment in nla3d. Every element (and other entities like MPC
// collections) should register its DoFs which the element will use.
// For example: addNodeDof(32, Dof::UX) means that DoF UX for node 32 will be used in a global system of
// equations. Unregistered DoFs will not participate in global equations system. This mechanism
// leads to ability to have different DoFs in different nodes.
// NOTE: `node` > 0
void addNodeDof(uint32 node, std::initializer_list<Dof::dofType> _dofs);
// NOTE: `element` > 0
void addElementDof(uint32 el, std::initializer_list<Dof::dofType> _dofs);
// functions to determine how many uniques DoF types are used for nodes and elements
std::set<Dof::dofType> getUniqueNodeDofTypes();
std::set<Dof::dofType> getUniqueElementDofTypes();
// return true if the corresponding DoF was added in nodeDofs or elementDofs.
bool isElementDofUsed(uint32 el, Dof::dofType dof);
bool isNodeDofUsed(uint32 node, Dof::dofType dof);
// get the number of equation in the global eq. system for particular DoF
uint32 getElementDofEqNumber(uint32 el, Dof::dofType dof);
uint32 getNodeDofEqNumber(uint32 node, Dof::dofType dof);
// get a pointer to Dof class for particular DoF
Dof* getElementDof(uint32 el, Dof::dofType dof);
Dof* getNodeDof(uint32 node, Dof::dofType dof);
// get the obtained value for a particular DoF. The value is a solution to the present moment
double getNodeDofSolution(uint32 node, Dof::dofType dof);
double getElementDofSolution(uint32 el, Dof::dofType dof);
// as far as constrained (fixed) DoFs have special treatment in FEStorage outer code (commonly
// FESolver) should register all constrained DoFs by mean of these functions (before calling
// assignEquationNumbers())
void setConstrainedNodeDof(uint32 node, Dof::dofType dtype);
void setConstrainedElementDof(uint32 el, Dof::dofType dtype);
// Get value of reaction for particular DoFs. Reactions are defined only for constrained DoFs. If
// a reaction value is asked for not constrained DoFs, a value of external concentrated load is
// returned.
// NOTE: actually this functions just return vecR[eq-1] value, for eq <= nConstrainedDofs() it's
// reaction load, but for eq > nConstrainedDofs() it's external DoF's concentrated load.
double getReaction(uint32 node, Dof::dofType dof);
double getReaction(uint32 eq);
// get an instance of Material class
Material* getMaterial();
// get an instance of particular node
// NOTE: `_nn` > 0
Node& getNode(uint32 _nn);
// function fills node_ptr with pointers to Node classes for element el.
// Calling side should reserve a space for an array of pointers node_ptr.
// size of node_ptr should be at least Element::getNNodes()
// NOTE: `el` > 0
void getElementNodes(uint32 el, Node** node_ptr);
// The function return a spatial position of node `n` in either initial state (`deformed` = false)
// or deformed state (`deformed` = true);
// A calling side should take care about memory allocation for ptr. Size of ptr should be at least
// 3 as the function always return 3-dimensional coordinates.
void getNodePosition(uint32 n, double* ptr, bool deformed = false);
// NOTE: `_en` > 0
Element& getElement(uint32 _en);
// get a FEComponent instance by registration number
// NOTE: `i` > -1
FEComponent* getFEComponent(size_t i);
// get a FEComponent instance by component name
FEComponent* getFEComponent(const std::string& name);
double getUelement(unsigned int j);
// storing operations
//
// Add `mpc` to `mpcs` array. FEStorage will delete Mpc instances by itslef.
void addMpc(Mpc* mpc);
// Add `mpcCol` to `mpcCollections` array. FEStorage will delete MpcCollection instances by itslef.
void addMpcCollection(MpcCollection* mpcCol);
// Add `comp` to `feComponents` array. FEStorage will delete FEComponent instances by itslef.
void addFEComponent(FEComponent* comp);
// The function add a node to the nodes table.
// TODO: this is inefficient functions because we can't ensure memory localization for all nodes
// instanses in `nodes` array
void addNode(Node* node);
// The function creates an vector `nodes` with dynamically allocated Node instances inside.
// Numbers of newly created elements pass back to the caller.
// NOTE: nodes will be created with default Node() constructor (node coordinates 0, 0, 0).
// NOTE: new nodes are concantenated to old nodes which already were in FEStorage
std::vector<uint32> createNodes(uint32 nn);
// add an element to the element array `elements`.
// TODO: this is inefficient functions because we can't ensure memory localization for all
// elements instances in `elements` array
void addElement(Element* el);
// The function creates a vector with dynamically allocated Element instances inside. Particular
// realization of abstract Element class is chosen from elType variable by mean of ElementFactory
// class (see elements/ElemenetFactory.h). Numbers of newly created elements pass back to the
// caller.
// NOTE: elements will be created with default constructor. User code should then fill node
// numbers (by Element::getNodeNumber(..)) and other stuff related to particular realization of
// Element class.
std::vector<uint32> createElements(uint32 en, ElementType elType);
// template version of function described above. User code should provide example Element entity
// of particular class.
template<typename T>
std::vector<uint32> createElements(uint32 _en, T example);
// delete procedures
//
// delete all FE model things: elements, nodes, MPCs, MPC Collections, FE components, topology
// info.
void deleteMesh();
// delete nodes table: delete all dynamically allocated Node instances,
// and clear vector of pointers `nodes`.
void deleteNodes();
// delete elements table: delete all dynamically allocated Element instances,
// and clear vector of pointers `elements`.
void deleteElements();
// delete MPC list: delete all dynamically allocated Mpc instances,
// and clear vector of pointers `mpcs`.
void deleteMpcs();
void deleteMpcCollections();
void deleteFeComponents();
// delete elementDofs and nodeDofs arrays (and free the memory)
void deleteDofArrays();
// delete all data allocated in FEStorage::initSolutionData(), delete DoFs arrays
void deleteSolutionData();
// print procedures
void listFEComponents();
// for debug purpose only. Be carefully, this is output intensive..
void printDofInfo(std::ostream& out);
// solution procedures
// These functions perform all preparing steps before FESolver will solve the problem:
// After this procedure one can't add more elements, nodes, boundary conditions, MPCs, ..
// 1. Fill DoFs tables by calling Element->pre() and MpcCollection->pre() to register needed DoFs.
void initDofs();
// 2. Outer code should call FEStorage::setConstrained[Node/Element]Dof(..) to set the particular
// DoFs to be constrained (fixed).
// 3. Assign number for globals system equations by calling assignEquationNumbers();
void assignEquationNumbers();
// 4. Allocate all solution data structures: matK/C/M, vecU/DU/DDU/F/R
void initSolutionData();
// After global equations system is solved and vecU/DU/DDU/R is updated with appropriate values
// FESolver should call this procedure to update element solution data
// NOTE: actually Element::update() is called
void updateResults();
private:
// fill `topology` data based on the current mesh (Element::nodes numbers)
void learnTopology();
// these functions are used to train sparsity info for matK/C/M
// provide info that entry (eqi, eqj) are not zero
// should be called before matK->compressed()
void addEntryK(uint32 eqi, uint32 eqj);
void addEntryMPC(uint32 eq_num, uint32 eqj);
// Total number of DoFs (registered by FEStorage::add[Node/Element]Dof(..))
uint32 _nDofs = 0;
// Number of constrained (fixed) DoFs values (registered by
// FEStorage::setConstrained[Node/Element]Dof())
uint32 _nConstrainedDofs = 0;
// _nUnknownDofs - number of Dof need to be found on every step (excluding Lagrangian lambdas)
uint32 _nUnknownDofs;
// Array of elements. Elements are created by the FEStorage::createElements(..) function. It's
// important that elements in nla3d have consecutive numbering. That means that they have numbers
// in [1; nElements()].
// NOTE: Elements are deleted by deleteElements() function.
std::vector<Element*> elements;
// Array of nodes. Nodes are created by FEStorage::createNodes(..). Nodes are created with default
// coordinates (0,0,0). Nodes have consecutive numbering. They have numbers in [1; nNodes()].
std::vector<Node*> nodes;
// List of MPC equations. List is populated by FEStorage::addMpc(..). An instance of Mpc class is
// created outside of FEStorage class. But after addMpc(..) function FEStorage takes control on
// the instance of Mpc. MPCs can be deleted by FEStorage::deleteMpcs(). Of course, they are
// deleted in ~FEStorage() destructor.
std::list<Mpc*> mpcs;
// FE components - lists consist of numbers of entities. Here is the support of nodal components
// and element components. This components can be used to apply BCs and/or MPCs. FE component is
// attached to FE Storage by FEStorage::addFEComponent(..) function. All stored components can be
// listed by FEStorage::listFEComponents(..). One can access to the particular component by
// methods FEStorage:: getFEComponent(..).
std::vector<FEComponent*> feComponents;
// An array of MpcCollection. Some number of MPCs can be grouped together because of they were
// produced by a single rule. For example, in FE model can rigid body MPC equations between the
// master node and slave nodes. The relation between the master node and slave nodes described by
// a group of MPC equations. All of them are grouped in MpcCollection. See Mpc.h for more details.
// Here is a rule: Mpc defined inside of MpcCollection should be added to FEStorage::mpcs by
// addMpc(..). That means that Mpc instances are dynamically created in MpcCollection, but then
// FEStorage class takes control on it.
std::vector<MpcCollection*> mpcCollections;
// Stiffness matrix. It's BlockSparseSymMatrix because of block(1) and block(1, 2) related to
// equations for constrained (fixed) DoFs and will not be used in equation solver. Instead of this
// only block(2) should be solved by eq. solver.
// NOTE: Such separation is possible because FEStorage gives to constrained DoFs equation numbers
// from 1 to nConstrainedDofs(). Unknown DoFs has numbers from nConstrainedDofs() + 1 to
// nConstrainedDofs() + nUnknownDofs().
math::BlockSparseSymMatrix<2>* matK = nullptr;
// matrices for transient analysis:
// NOTE: matK, matC, matM have the same SparsityInfo for fast math like this : matKmod = c1 * matK + c2 *
// matC + c3 * matM.
// Damping matrix
math::BlockSparseSymMatrix<2>* matC = nullptr;
// Inertia matrix
math::BlockSparseSymMatrix<2>* matM = nullptr;
// A found values for DoFs. vecU has size of nDofs + nMpc(). FESolver is responsible of filling
// this vector.
// The whole vector could be divided into different parts: values for constrained DoFs in range
// [0; nConstrainedDofs()), values for unknown DoFs [nConstrainedDofs(); nDofs()), values for
// Mpc's Lagrangian lambdas [nDofs(); nDofs() + nMpc()).
math::dVec vecU;
// Found first time derivatives for DoF's values. FESovler is responsible of filling this vector.
math::dVec vecDU;
// Found second time derivatives for DoF's values. FESovler is responsible of filling this vector.
math::dVec vecDDU;
// vector stores external concentrated loads. vecR has size nDofs + nMpc(). FESolver is responsible of filling
// this vector.
// The whole vector could be divided into different parts: values of reactions for constrained DoFs in range
// [0; nConstrainedDofs()), values of external loads for unknown DoFs [nConstrainedDofs(); nDofs()), for
// Mpc's Lagrangian lambdas [nDofs(); nDofs() + nMpc()) values should be alway remain zeros.
math::dVec vecR;
// internal element loads. vecF has size nDofs + nMpc(). FEStorage::assembleGlobalEqMatrices() is
// responsible of filling this vector.
math::dVec vecF;
// * In nla3d every DoF is represented by `Dof` class.
// * DoF can be nodal or for an element.
// * `elementDofs` and `nodeDofs` are DofCollection
// DofCollection keeps Dof objects and give it by (node, dof) pair
// * Elements (or another entities) should register DoFs by using
// FEStorage::add[Node/Element]Dof(node, dof)
DofCollection elementDofs;
DofCollection nodeDofs;
// topology array. It stores a set of element numbers [el1, el2, el3..] attached to the node `n`:
// topology[n-1] = [el1, el2, el3..]
std::vector<std::set<uint32> > topology;
// if transient is true that means that assembleGlobalEqMatrices() should assemble M and C
// matrices too
bool transient = false;
};
inline void FEStorage::addValueK(uint32 nodei, Dof::dofType dofi, uint32 nodej, Dof::dofType dofj, double value) {
uint32 rowEq = getNodeDofEqNumber(nodei, dofi);
uint32 colEq = getNodeDofEqNumber(nodej, dofj);
addValueK(rowEq, colEq, value);
}
inline void FEStorage::addValueK(uint32 eqi, uint32 eqj, double value) {
// eqi - row equation
// eqj - column equation
matK->addValue(eqi, eqj, value);
}
inline void FEStorage::addValueC(uint32 nodei, Dof::dofType dofi, uint32 nodej, Dof::dofType dofj, double value) {
uint32 rowEq = getNodeDofEqNumber(nodei, dofi);
uint32 colEq = getNodeDofEqNumber(nodej, dofj);
addValueC(rowEq, colEq, value);
}
inline void FEStorage::addValueC(uint32 eqi, uint32 eqj, double value) {
// eqi - row equation
// eqj - column equation
matC->addValue(eqi, eqj, value);
}
inline void FEStorage::addValueM(uint32 nodei, Dof::dofType dofi, uint32 nodej, Dof::dofType dofj, double value) {
uint32 rowEq = getNodeDofEqNumber(nodei, dofi);
uint32 colEq = getNodeDofEqNumber(nodej, dofj);
addValueM(rowEq, colEq, value);
}
inline void FEStorage::addValueM(uint32 eqi, uint32 eqj, double value) {
// eqi - row equation
// eqj - column equation
matM->addValue(eqi, eqj, value);
}
inline void FEStorage::addValueMPC(uint32 eq_num, uint32 nodej, Dof::dofType dofj, double coef) {
uint32 colEq = getNodeDofEqNumber(nodej, dofj);
addValueMPC(eq_num, colEq, coef);
}
inline void FEStorage::addValueMPC(uint32 eq_num, uint32 eqj, double coef) {
matK->addValue(eq_num, eqj, coef);
}
inline void FEStorage::addValueF(uint32 nodei, Dof::dofType dofi, double value) {
uint32 rowEq = getNodeDofEqNumber(nodei, dofi);
addValueF(rowEq, value);
}
inline void FEStorage::addValueF(uint32 eqi, double value) {
assert(eqi > 0);
assert(eqi <= vecF.size());
assert(eqi <= nDofs() + nMpc());
vecF[eqi - 1] += value;
}
inline void FEStorage::addValueR(uint32 nodei, Dof::dofType dofi, double value) {
uint32 rowEq = getNodeDofEqNumber(nodei, dofi);
addValueR(rowEq, value);
}
inline void FEStorage::addValueR(uint32 eqi, double value) {
assert(eqi > 0);
assert(eqi <= vecR.size());
assert(eqi <= nDofs() + nMpc());
vecR[eqi - 1] += value;
}
inline void FEStorage::zeroK() {
assert(matK);
matK->zero();
}
inline void FEStorage::zeroC() {
assert(matC);
matC->zero();
}
inline void FEStorage::zeroM() {
assert(matM);
matM->zero();
}
inline void FEStorage::zeroF() {
vecF.zero();
}
inline math::BlockSparseSymMatrix<2>* FEStorage::getK() {
assert(matK->isCompressed());
return matK;
}
inline math::BlockSparseSymMatrix<2>* FEStorage::getC() {
assert(matC->isCompressed());
return matC;
}
inline math::BlockSparseSymMatrix<2>* FEStorage::getM() {
assert(matM->isCompressed());
return matM;
}
inline math::dVec* FEStorage::getF() {
assert(vecF.size() > 0);
return &vecF;
}
inline math::dVec* FEStorage::getU() {
assert(vecU.size() > 0);
return &vecU;
}
inline double FEStorage::getUelement(unsigned int j) {
assert(vecU.size() > 0);
return vecU[j];
}
inline math::dVec* FEStorage::getDU() {
assert(vecDU.size() > 0);
return &vecDU;
}
inline math::dVec* FEStorage::getDDU() {
assert(vecDDU.size() > 0);
return &vecDDU;
}
inline math::dVec* FEStorage::getR() {
assert(vecR.size() > 0);
return &vecR;
}
inline uint32 FEStorage::nDofs() {
return _nDofs;
}
inline uint32 FEStorage::nConstrainedDofs() {
return _nConstrainedDofs;
}
inline uint32 FEStorage::nUnknownDofs() {
return _nUnknownDofs;
}
inline uint32 FEStorage::nMpc() {
return static_cast<uint32> (mpcs.size());
}
inline uint32 FEStorage::nNodes () {
return static_cast<uint32> (nodes.size());
}
inline uint32 FEStorage::nElements () {
return static_cast<uint32> (elements.size());
}
inline void FEStorage::setTransient(bool _transient) {
transient = _transient;
}
inline bool FEStorage::isTransient() {
return transient;
}
inline void FEStorage::addNodeDof(uint32 node, std::initializer_list<Dof::dofType> _dofs) {
assert(nodeDofs.getNumberOfEntities() > 0);
nodeDofs.addDof(node, _dofs);
}
inline void FEStorage::addElementDof(uint32 el, std::initializer_list<Dof::dofType> _dofs) {
assert(elementDofs.getNumberOfEntities() > 0);
elementDofs.addDof(el, _dofs);
}
inline std::set<Dof::dofType> FEStorage::getUniqueNodeDofTypes() {
return nodeDofs.getUniqueDofTypes();
}
inline std::set<Dof::dofType> FEStorage::getUniqueElementDofTypes() {
return elementDofs.getUniqueDofTypes();
}
inline bool FEStorage::isElementDofUsed (uint32 el, Dof::dofType dof) {
assert(elementDofs.getNumberOfEntities() > 0);
return elementDofs.isDofUsed(el, dof);
}
inline bool FEStorage::isNodeDofUsed (uint32 node, Dof::dofType dof) {
assert(nodeDofs.getNumberOfEntities() > 0);
return nodeDofs.isDofUsed(node, dof);
}
inline uint32 FEStorage::getElementDofEqNumber(uint32 el, Dof::dofType dof) {
return getElementDof(el, dof)->eqNumber;
}
inline uint32 FEStorage::getNodeDofEqNumber(uint32 node, Dof::dofType dof) {
return getNodeDof(node, dof)->eqNumber;
}
inline Dof* FEStorage::getElementDof (uint32 el, Dof::dofType dof) {
assert(elementDofs.getNumberOfUsedDofs() > 0);
return elementDofs.getDof(el, dof);
}
inline Dof* FEStorage::getNodeDof (uint32 node, Dof::dofType dof) {
assert(nodeDofs.getNumberOfUsedDofs() > 0);
return nodeDofs.getDof(node, dof);
}
inline double FEStorage::getNodeDofSolution (uint32 node, Dof::dofType dof) {
assert(vecU.size() > 0);
return vecU[getNodeDofEqNumber(node, dof) - 1];
}
inline double FEStorage::getElementDofSolution (uint32 el, Dof::dofType dof) {
assert(vecU.size() > 0);
return vecU[getElementDofEqNumber(el, dof) - 1];
}
inline Node& FEStorage::getNode(uint32 _nn) {
assert(_nn> 0 && _nn <= nNodes());
return *nodes[_nn-1];
}
inline Element& FEStorage::getElement(uint32 _en) {
assert(_en <= nElements());
return *(elements[_en-1]);
}
inline void FEStorage::addEntryK(uint32 eqi, uint32 eqj) {
// eqi - row equation
// eqj - column equation
matK->addEntry(eqi, eqj);
}
inline void FEStorage::addEntryMPC(uint32 eq_num, uint32 eqj) {
assert(matK);
assert(eq_num > nConstrainedDofs() + nUnknownDofs());
assert(eq_num <= nConstrainedDofs() + nUnknownDofs() + nMpc());
assert(eqj > 0 && eqj <= nConstrainedDofs() + nUnknownDofs());
matK->addEntry(eq_num, eqj);
}
} // namespace nla3d
// 'dirty' hack to avoid include loops (element-vs-festorage)
#include "elements/element.h"
namespace nla3d {
template<typename T>
std::vector<uint32> FEStorage::createElements(uint32 _en, T example) {
//TODO: catch if not enough memory
std::vector<uint32> newIndexes;
newIndexes.reserve(_en);
uint32 nextNumber = elements.size() + 1;
elements.reserve(elements.size() + _en);
for (uint32 i = 0; i < _en; i++) {
T* els = new T;
elements.push_back(els);
}
for (uint32 i = nextNumber; i <= elements.size(); i++) {
//access elNum protected values as friend
elements[i - 1]->elNum = i;
elements[i - 1]->storage = this;
newIndexes.push_back(i);
}
return newIndexes;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "Mpc.h"
#include "FEStorage.h"
#include "Node.h"
namespace nla3d {
using namespace math;
void Mpc::print (std::ostream& out) {
list<MpcTerm>::iterator token = eq.begin();
bool first = true;
while (token != eq.end()) {
//Cij_add(eq_num, token->node, token->node_dof, token->coef);
if (!first) {
out << " + ";
} else {
first = false;
}
out << toStr(token->coef) << " * " << toStr(token->node) << ":" << Dof::dofTypeLabels[token->node_dof];
token++;
}
out << " = " << toStr(b);
}
void MpcCollection::registerMpcsInStorage () {
for (size_t i = 0; i < collection.size(); i++) {
storage->addMpc(collection[i]);
}
}
void MpcCollection::printEquations (std::ostream& out) {
for (size_t i = 0; i < collection.size(); i++) {
collection[i]->print(out);
out << std::endl;
}
}
RigidBodyMpc::RigidBodyMpc () {
}
RigidBodyMpc::~RigidBodyMpc () {
collection.clear();
}
void RigidBodyMpc::pre () {
// create Mpc for all nodes and all dofs..
collection.assign(slaveNodes.size() * dofs.size(), NULL);
for (size_t i = 0; i < slaveNodes.size(); i++) {
for (size_t j = 0; j < dofs.size(); j++) {
Mpc* mpc = new Mpc;
mpc->b = 0.0;
mpc->eq.push_back(MpcTerm(slaveNodes[i], dofs[j], 1.0));
mpc->eq.push_back(MpcTerm(masterNode, dofs[j], -1.0));
mpc->eq.push_back(MpcTerm(masterNode, Dof::ROTX, 0.0));
mpc->eq.push_back(MpcTerm(masterNode, Dof::ROTY, 0.0));
mpc->eq.push_back(MpcTerm(masterNode, Dof::ROTZ, 0.0));
collection[i*dofs.size() + j] = mpc;
}
}
storage->addNodeDof(masterNode, {Dof::UX, Dof::UY, Dof::UZ, Dof::ROTX, Dof::ROTY, Dof::ROTZ});
}
void RigidBodyMpc::update () {
// rigid body motion formula is the next:
// {u} = {w} +([C]-[I])*({p}-{q})
// where {u} - displacement of a slave node
// {w} - displacement of the master node
// [C] - rotation matrix (depends on rotation vector {theta})
// {p} - position of the master node
// {q} - position of a slave node
// where [C] in componentwise form:
// C_{ij} = \delta_{ij} cos\theta + \frac{sin\theta}{\theta}e_{ikj}\theta_k + \left(\frac{1-cos\theta}{\theta^2}\theta_i \theta_j\right)
Vec<3> theta0;
Vec<3> w0;
theta0[0] = storage->getNodeDofSolution(masterNode, Dof::ROTX);
theta0[1] = storage->getNodeDofSolution(masterNode, Dof::ROTY);
theta0[2] = storage->getNodeDofSolution(masterNode, Dof::ROTZ);
w0[0] = storage->getNodeDofSolution(masterNode, Dof::UX);
w0[1] = storage->getNodeDofSolution(masterNode, Dof::UY);
w0[2] = storage->getNodeDofSolution(masterNode, Dof::UZ);
double thetaNorm = theta0.length();
Vec<3> masterPos;
Vec<3> slavePos;
Mat<3,3> C;
double c1, c2, c3, c4;
if (thetaNorm < 1.0e-3) {
// for very small angles we need to define some undefined values
// TODO: for now eps = 1.0e-3 was choosed randomly, need to check it
c1 = 1.0;
c2 = 0.5;
c3 = -1.0/3.0;
c4 = -1.0/12.0;
} else {
// define constant exactly
c1 = sin(thetaNorm) / thetaNorm;
c2 = (1.0 - cos(thetaNorm)) / (thetaNorm * thetaNorm);
c3 = (thetaNorm * cos(thetaNorm) - sin(thetaNorm)) / (thetaNorm * thetaNorm * thetaNorm);
c4 = (thetaNorm * sin(thetaNorm) - 2.0 + 2.0 * cos(thetaNorm)) / (thetaNorm * thetaNorm * thetaNorm * thetaNorm);
}
for (uint16 i = 0; i < 3; i++) {
for (uint16 j = 0; j < 3; j++) {
double theta0Mat_ij = solidmech::LeviCivita[i][0][j] * theta0[0] +
solidmech::LeviCivita[i][1][j] * theta0[1] +
solidmech::LeviCivita[i][2][j] * theta0[2];
C[i][j] = cos(thetaNorm) * solidmech::I[i][j] + c1 * theta0Mat_ij +
c2 * theta0[i] * theta0[j];
}
}
storage->getNodePosition(masterNode, masterPos.ptr(), false);
for (size_t n = 0; n < slaveNodes.size(); n++) {
storage->getNodePosition(slaveNodes[n], slavePos.ptr(), false);
Vec<3> pqVec = slavePos - masterPos;
// drivatives for C matrix for i row (with respect to j and l)
Mat<3,3> cDeriv;
for (size_t d = 0; d < dofs.size(); d++) {
uint16 i;
switch (dofs[d]) {
case Dof::UX:
i = 0;
break;
case Dof::UY:
i = 1;
break;
case Dof::UZ:
i = 2;
break;
default:
LOG(FATAL) << "which degree of freedom?";
}
for (uint16 j = 0; j < 3; j++) {
for (uint16 l = 0; l < 3; l++) {
cDeriv[j][l] = solidmech::I[i][j]*(-c1)*theta0[l] + c3*theta0[l]*(
solidmech::LeviCivita[i][0][j]*theta0[0] +
solidmech::LeviCivita[i][1][j]*theta0[1] +
solidmech::LeviCivita[i][2][j]*theta0[2]) +
c1*solidmech::LeviCivita[i][l][j] +
c4*theta0[l]*theta0[i]*theta0[j] +
c2*(solidmech::I[i][l]*theta0[j] + theta0[i]*solidmech::I[j][l]);
}
}
collection[n*static_cast<uint16> (dofs.size()) + d]->b = w0[i] + (C[i][0] - solidmech::I[i][0])*pqVec[0] +
(C[i][1] - solidmech::I[i][1])*pqVec[1] + (C[i][2] - solidmech::I[i][2])*pqVec[2] -
storage->getNodeDofSolution(slaveNodes[n], dofs[d]);
list<MpcTerm>::iterator token = collection[n*dofs.size() + d]->eq.begin();
token++;
token++;
// masterNode::ROTX coef
token->coef = - (cDeriv[0][0]*pqVec[0] + cDeriv[1][0]*pqVec[1] + cDeriv[2][0]*pqVec[2]);
token++;
// masterNode::ROTY coef
token->coef = - (cDeriv[0][1]*pqVec[0] + cDeriv[1][1]*pqVec[1] + cDeriv[2][1]*pqVec[2]);
token++;
// masterNode::ROTZ coef
token->coef = - (cDeriv[0][2]*pqVec[0] + cDeriv[1][2]*pqVec[1] + cDeriv[2][2]*pqVec[2]);
}
}
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include <list>
#include "sys.h"
#include "Dof.h"
namespace nla3d {
class FEStorage;
class MpcTerm {
public:
MpcTerm() : node(0), node_dof(Dof::UNDEFINED), coef(1.0) {
}
MpcTerm(int32 n, Dof::dofType dof, double coef = 1.0) : node(n), node_dof(dof), coef(coef) {
}
int32 node = 0;
Dof::dofType node_dof = Dof::UNDEFINED;
double coef = 0.0;
};
class Mpc {
public:
void print (std::ostream& out);
std::list<MpcTerm> eq;
double b; // rhs of MPC eq.
uint32 eqNum; // number of equation in global assembly
};
class MpcCollection {
public:
virtual void update() = 0;
virtual void pre() = 0;
void printEquations(std::ostream& out);
void registerMpcsInStorage();
std::vector<Mpc*> collection;
FEStorage* storage;
};
class RigidBodyMpc : public MpcCollection {
public:
RigidBodyMpc();
~RigidBodyMpc();
uint32 masterNode;
void pre ();
void update ();
std::vector<uint32> slaveNodes;
std::vector<Dof::dofType> dofs;
};
class fixBC {
public:
fixBC () { }
fixBC (int32 n, Dof::dofType dof, double val = 0.0) : node(n), node_dof(dof), value(val)
{ }
int32 node = 0;
Dof::dofType node_dof = Dof::UNDEFINED;
double value = 0.0;
};
class loadBC {
public:
loadBC () : node(0), node_dof(Dof::UNDEFINED), value(0.0)
{ }
loadBC (int32 n, Dof::dofType dof, double val = 0.0) : node(n), node_dof(dof), value(val)
{ }
int32 node = 0;
Dof::dofType node_dof = Dof::UNDEFINED;
double value = 0.0;
};
} // namespace nal3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "Node.h"
namespace nla3d {
void Node::display (uint32 nn) {
LOG(INFO) << "N " << nn << " : " << pos;
}
std::string Node::toString() {
std::string str;
char buff[100];
sprintf_s(buff,100,"%f %f %f",pos[0], pos[1], pos[2]);
str+=buff;
return str; //TODO: do it easy
}
}
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
#include "math/Vec.h"
#include "Dof.h"
namespace nla3d {
//class Node represents spatial 3D node
class Node {
public:
//in-out operation: (rudiment actually..)
void display (uint32 nn);
std::string toString();
math::Vec<3> pos;
friend class Element;
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "PostProcessor.h"
namespace nla3d {
PostProcessor::PostProcessor(FEStorage *st) {
assert(st);
storage = st;
active = false;
failed = false;
}
std::string PostProcessor::getStatus () {
char buf[300];
sprintf_s(buf, 200, "PostProcessor No %d: %s\n\tActive - %s, Failed - %s",nPost_proc, name.c_str(),active?"true":"false",active?"true":"false");
return std::string(buf);
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
#include "FEStorage.h"
namespace nla3d {
class FEStorage;
class FESolver;
//Data_Processor
class PostProcessor {
public:
PostProcessor(FEStorage *st);
~PostProcessor() { };
virtual void pre ()=0;
virtual void process (uint16 curLoadstep)=0;
virtual void post (uint16 curLoadstep)=0;
std::string getStatus ();
uint16 getnPost_num () {
return nPost_proc;
}
void setActive (bool act) {
if (failed) {
LOG(WARNING) << "PostProcessor " << nPost_proc << " (" << name << "): can't set active,"
<< "because post_proc has been already failed";
return;
}
active = act;
}
bool getActive () {
return active;
}
uint16 getProc_num () {
return nPost_proc;
}
friend class FEStorage;
friend class FESolver;
protected:
FEStorage *storage;
uint16 nPost_proc;
std::string name;
bool active;
bool failed;
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "ReactionProcessor.h"
#include "Node.h"
namespace nla3d {
ReactionProcessor::ReactionProcessor(FEStorage *st) : PostProcessor(st) {
name ="ReactionProcessor";
}
ReactionProcessor::ReactionProcessor(FEStorage *st, std::string _filename) : PostProcessor(st) {
name ="ReactionProcessor";
filename = _filename;
}
void ReactionProcessor::pre() {
if (nodes.size() == 0) {
LOG(WARNING) << "Can't work. No nodes. Processor name = " << name;
return;
}
// define Dofs by the following rule:
// 1. If dofs is not empty than user alreade define which dof use to calculate reaction
// 2. If dofs.size() == 0 , select dof thich are constrained in current node set.
// if different nodes has different constrained dofs - error
if (dofs.size() == 0) {
for (uint16 d = 0; d < Dof::numberOfDofTypes; d++) {
Dof::dofType t = static_cast<Dof::dofType> (d);
if (storage->isNodeDofUsed(nodes[0], t) && storage->getNodeDof(nodes[0], t)->isConstrained) {
dofs.push_back(t);
}
}
if (dofs.size() == 0) {
LOG(WARNING) << "Can't select dofs for reactions (they are unconstrained)";
}
// check that others nodes have the same constrained dofs
bool sameDofsConstrained = true;
uint32 n = 0;
while (sameDofsConstrained && n < nodes.size()) {
for (uint16 d = 0; d < dofs.size(); d++) {
if (!storage->isNodeDofUsed(nodes[n], dofs[d]) && !storage->getNodeDof(nodes[n], dofs[d])->isConstrained) {
LOG(FATAL) << "Different dofs are constrained in the node set. Autochoosing of dofs is failed";
sameDofsConstrained = false;
break;
}
}
n++;
}
} else {
// in case of user defined dofs just check that this dofs are used in the FE
bool dofsUsed = true;
uint32 n = 0;
while (dofsUsed && n < nodes.size()) {
for (uint16 d = 0; d < dofs.size(); d++) {
if (!storage->isNodeDofUsed(nodes[n], dofs[d])) {
LOG(ERROR) << "some dofs are not used in FE calculations";
dofsUsed = false;
break;
}
}
n++;
}
}
if (filename.length() > 0) {
std::ofstream file(filename.c_str(),std::ios::trunc);
if (!file) {
LOG(WARNING) << "Can't create a file with name " << filename;
return;
}
for (uint16 d = 0; d < dofs.size(); d++) {
file << "\t" << Dof::dofTypeLabels[dofs[d]];
}
file << std::endl;
file << "0";
for (uint16 d = 0; d < dofs.size(); d++) {
file << "\t" << 0.0;
}
file << std::endl;
file.close();
}
sumOfDofsReactions.assign(dofs.size(), std::vector<double> ());
for (uint16 d = 0; d < dofs.size(); d++) {
//sumOfDofsReactions[d].reserve(qLoadstep+1);
sumOfDofsReactions[d].push_back(0.0);
}
}
void ReactionProcessor::process (uint16 curLoadstep) {
std::vector<double> reactions;
reactions.assign(dofs.size(), 0.0);
for (uint32 n = 0; n < nodes.size(); n++) {
for (uint16 d = 0; d < dofs.size(); d++) {
reactions[d] += storage->getReaction(nodes[n],dofs[d]);
}
}
if (filename.length() > 0) {
std::ofstream file(filename.c_str(),std::ios::app);
if (!file) {
LOG(WARNING) << "Can't open a file with name " << filename;
return;
}
file << curLoadstep;
for (uint16 d = 0; d < dofs.size(); d++) {
file << "\t" << reactions[d];
}
file << std::endl;
file.close();
}
for (uint16 d = 0; d < dofs.size(); d++) {
sumOfDofsReactions[d].push_back(reactions[d]);
}
}
void ReactionProcessor::post (uint16 curLoadstep) {
}
std::vector<double> ReactionProcessor::getReactions (Dof::dofType dof) {
for (uint16 d = 0; d < dofs.size(); d++) {
if (dofs[d] == dof) {
return sumOfDofsReactions[d];
}
}
LOG(ERROR) << "There is not results for DoF = " << Dof::dofTypeLabels[dof];
return std::vector<double> ();
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
#include "PostProcessor.h"
#include "FEStorage.h"
namespace nla3d {
class ReactionProcessor : public PostProcessor {
public:
ReactionProcessor(FEStorage *st);
ReactionProcessor(FEStorage *st, std::string _filename);
virtual ~ReactionProcessor() { };
virtual void pre ();
virtual void process (uint16 curLoadstep);
virtual void post (uint16 curLoadstep);
std::vector<double> getReactions (Dof::dofType dof);
std::vector<uint32> nodes;
std::vector<Dof::dofType> dofs;
protected:
std::string filename;
std::vector<std::vector<double> > sumOfDofsReactions;
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "VtkProcessor.h"
#include "elements/element.h"
/*
* Globals.
*/
static FILE *fp = NULL;
//static int useBinary = 0;
static int useBinary = 1;
static int numInColumn = 0;
/* ****************************************************************************
* Function: end_line_binary
*
* Purpose:
* If floats or ints have been written using the write_float or write_int
* functions, this will issue a newline (if necessary) so that a new
* heading can be placed.
*
* ************************************************************************* */
static void end_line_binary(void)
{
if (!useBinary)
{
char str2[8] = "\n";
fprintf(fp, str2);
numInColumn = 0;
}
}
/* ****************************************************************************
* Function: open_file_binary
*
* Purpose:
* Opens a file for writing and assigns the handle to the global variable
* "fp".
*
* ************************************************************************* */
static void open_file_binary(const char *filename)
{
char full_filename[1024];
if (strstr(filename, ".vtk") != NULL)
{
strcpy(full_filename, filename);
}
else
{
sprintf(full_filename, "%s.vtk", filename);
}
//// fp = fopen(full_filename, "w+");
fp = fopen(full_filename, "w+b");
}
/* ****************************************************************************
* Function: close_file_binary
*
* Purpose:
* Closes the file with handle "fp" (a global variable).
*
* ************************************************************************* */
static void close_file_binary(void)
{
end_line_binary();
fclose(fp);
fp = NULL;
}
/* ****************************************************************************
* Function: force_big_endian_binary
*
* Purpose:
* Determines if the machine is little-endian. If so, then, for binary
* data, it will force the data to be big-endian.
*
* Note: This assumes that all inputs are 4 bytes long.
*
* ************************************************************************* */
static void force_big_endian_binary(unsigned char *bytes)
{
static int doneTest = 0;
static int shouldSwap = 0;
if (!doneTest)
{
int tmp1 = 1;
unsigned char *tmp2 = (unsigned char *)&tmp1;
if (*tmp2 != 0)
shouldSwap = 1;
doneTest = 1;
}
if (shouldSwap & useBinary)
{
unsigned char tmp = bytes[0];
bytes[0] = bytes[3];
bytes[3] = tmp;
tmp = bytes[1];
bytes[1] = bytes[2];
bytes[2] = tmp;
}
}
/* ****************************************************************************
* Function: write_string_binary
*
* Purpose:
* Writes a character to the open file.
*
* ************************************************************************* */
static void write_string_binary(const char *str)
{
fprintf(fp, str);
}
/* ****************************************************************************
* Function: new_section_binary
*
* Purpose:
* Adds a new line, provided we didn't already just do so and we are
* writing an ASCII file.
*
* ************************************************************************* */
static void new_section_binary(void)
{
if (numInColumn != 0)
end_line_binary();
numInColumn = 0;
}
/* ****************************************************************************
* Function: write_int_binary
*
* Purpose:
* Writes an integer to the currently open file. This routine takes
* care of ASCII vs binary issues.
*
* ************************************************************************* */
static void write_int_binary(int val)
{
if (useBinary)
{
force_big_endian_binary((unsigned char *)&val);
fwrite(&val, sizeof(int), 1, fp);
}
else
{
char str[128];
sprintf(str, "%d ", val);
fprintf(fp, str);
if (((numInColumn++) % 9) == 8)
{
char str2[8] = "\n";
fprintf(fp, str2);
numInColumn = 0;
}
}
}
/* ****************************************************************************
* Function: write_float_binary
*
* Purpose:
* Writes an float to the currently open file. This routine takes
* care of ASCII vs binary issues.
*
* ************************************************************************* */
static void write_float_binary(float val)
{
if (useBinary)
{
force_big_endian_binary((unsigned char *)&val);
fwrite(&val, sizeof(float), 1, fp);
}
else
{
char str[128];
sprintf(str, "%20.12e ", val);
fprintf(fp, str);
if (((numInColumn++) % 9) == 8)
{
end_line_binary();
}
}
}
namespace nla3d {
using namespace math;
VtkProcessor::VtkProcessor(FEStorage *st, std::string _fileName) : PostProcessor(st) {
name = "VtkProcessor";
file_name = _fileName;
}
VtkProcessor::~VtkProcessor() {
}
void VtkProcessor::pre(){
useDisplacementsDofs = false;
nodalDofTypes.clear();
elementDofTypes.clear();
// find which nodal Dofs used in FEStorage and add to list nodalDofTypes for printing out into VTK
// file
auto u_dofs = storage->getUniqueNodeDofTypes();
for (auto type : u_dofs) {
// take care of displacement dofs: if one of them exist in storage we need to write 3x1 vector
// into VTK (for wrapping)
if (type == Dof::UX || type == Dof::UY || type == Dof::UZ) {
useDisplacementsDofs = true;
continue;
}
nodalDofTypes.insert(type);
}
// the same for Element dofs
u_dofs = storage->getUniqueElementDofTypes();
for (auto type : u_dofs) {
elementDofTypes.insert(type);
}
// if was requested reveal query codes to be written into VTK
if(_writeAllResults) {
revealAllResults();
}
// write zero VTK file (before solution)
std::string cur_fn = file_name + "0" + ".vtk";
//*// std::ofstream file(cur_fn.c_str(), std::ios::trunc);
open_file_binary(cur_fn.c_str());
//*// write_header(file);
write_header_binary();
//*// write_geometry(file);
write_geometry_binary();
//*// write_point_data(file);
write_point_data_binary();
//*// write_cell_data(file);
write_cell_data_binary();
//*// file.close();
close_file_binary();
}
void VtkProcessor::process (uint16 curLoadstep) {
std::string cur_fn = file_name + toStr(curLoadstep) + ".vtk";
//*// std::ofstream file(cur_fn.c_str(), std::ios::trunc);
open_file_binary(cur_fn.c_str());
//*// write_header(file);
write_header_binary();
//*// write_geometry(file);
write_geometry_binary();
//*// write_point_data(file);
write_point_data_binary();
//*// write_cell_data(file);
write_cell_data_binary();
//*// file.close();
close_file_binary();
}
void VtkProcessor::write_vecres(std::ofstream &file)
{
math::dVec *vecU;
vecU = storage->getU();
for (unsigned int i = 0; i < vecU->size(); i++)
{
double znach = storage->getUelement(i);
file << znach << std::endl;
}
}
void VtkProcessor::post (uint16 curLoadstep) {
}
void VtkProcessor::writeAllResults(bool write) {
_writeAllResults = write;
}
bool VtkProcessor::registerResults(std::initializer_list<scalarQuery> queries) {
// check that each query is valid
for(auto v : queries) {
assert(v > scalarQuery::UNDEF && v < scalarQuery::LAST);
}
cellScalarQueries.insert(queries);
return true;
}
bool VtkProcessor::registerResults(std::initializer_list<vectorQuery> queries) {
// check that each query is valid
for(auto v : queries) {
assert(v > vectorQuery::UNDEF && v < vectorQuery::LAST);
}
cellVectorQueries.insert(queries);
return true;
}
bool VtkProcessor::registerResults(std::initializer_list<tensorQuery> queries) {
// check that each query is valid
for(auto v : queries) {
assert(v > tensorQuery::UNDEF && v < tensorQuery::LAST);
}
cellTensorQueries.insert(queries);
return true;
}
void VtkProcessor::write_header (std::ofstream &file) {
file << "# vtk DataFile Version 2.0" << std::endl;
file << "This file is generated by nla3D program" << std::endl << "ASCII" << std::endl;
file << "DATASET UNSTRUCTURED_GRID" << std::endl;
}
/* ****************************************************************************
* Function: write_header_binary
*
* Purpose:
* Writes the standard VTK header to the file. This should be the first
* thing written to the file.
*
* ************************************************************************* */
void VtkProcessor::write_header_binary() {
fprintf(fp, "# vtk DataFile Version 2.0\n");
fprintf(fp, "This file is generated by nla3D program\n");
if (useBinary)
fprintf(fp, "BINARY\n");
else
fprintf(fp, "ASCII\n");
fprintf(fp, "DATASET UNSTRUCTURED_GRID\n");
}
void VtkProcessor::write_geometry(std::ofstream &file, bool def) {
Vec<3> xi;
file << "POINTS " << storage->nNodes() << " float" << std::endl;
for (uint32 i=1; i <= storage->nNodes(); i++)
{
storage->getNodePosition(i, xi.ptr(), def);
file << xi << std::endl;
}
/*
CELLS en en*9
4 i j k l
CELL_TYPES en
11
11
*/
// calculate overall size of element data
uint32 data_size = 0;
for (uint32 i=1; i <= storage->nElements(); i++)
data_size += storage->getElement(i).getNNodes() + 1;
file << "CELLS " << storage->nElements() << " " << data_size << std::endl;
for (uint32 i=1; i <= storage->nElements(); i++)
{
uint16 nodesNum = storage->getElement(i).getNNodes();
file << nodesNum;
for (uint16 j=0; j < nodesNum; j++)
file << " " << storage->getElement(i).getNodeNumber(j)-1;
file << std::endl;
}
file << "CELL_TYPES " << storage->nElements() << std::endl;
for (uint32 i=1; i <= storage->nElements(); i++) {
ElementShape eltype = storage->getElement(i).getShape();
if (eltype == ElementShape::QUAD) {
file << "9" << std::endl; //VTK_QUAD, see VTK file formats
} else if (eltype == ElementShape::HEXAHEDRON) {
file << "12" << std::endl; //VTK_HEXAHEDRON
} else if (eltype == ElementShape::TETRA) {
file << "10" << std::endl; //VTK_TETRA
} else if (eltype == ElementShape::TRIANGLE) {
file << "5" << std::endl; //VTK_TRIANGLE
} else if (eltype == ElementShape::LINE) {
file << "3" << std::endl; //VTK_LINE
} else {
LOG_N_TIMES(10, ERROR) << "Don't now what type of elements here it is (el_num = " << i << ")";
}
}
}
void VtkProcessor::write_geometry_binary(bool def) {
Vec<3> xi;
fprintf(fp, "POINTS %d float\n", storage->nNodes());
for (uint32 i = 1; i <= storage->nNodes(); i++)
{
storage->getNodePosition(i, xi.ptr(), def);
write_float_binary(xi[0]);
write_float_binary(xi[1]);
write_float_binary(xi[2]);
end_line_binary();
}
/*
CELLS en en*9
4 i j k l
CELL_TYPES en
11
11
*/
// calculate overall size of element data
uint32 data_size = 0;
for (uint32 i = 1; i <= storage->nElements(); i++)
data_size += storage->getElement(i).getNNodes() + 1;
fprintf(fp, "CELLS %d %d\n", storage->nElements(), data_size);
for (uint32 i = 1; i <= storage->nElements(); i++)
{
uint16 nodesNum = storage->getElement(i).getNNodes();
write_int_binary(nodesNum);
for (uint16 j = 0; j < nodesNum; j++)
write_int_binary(storage->getElement(i).getNodeNumber(j) - 1);
end_line_binary();
}
fprintf(fp, "CELL_TYPES %d\n", storage->nElements());
for (uint32 i = 1; i <= storage->nElements(); i++) {
ElementShape eltype = storage->getElement(i).getShape();
if (eltype == ElementShape::QUAD) {
write_int_binary(9); //VTK_QUAD, see VTK file formats
end_line_binary();
}
else if (eltype == ElementShape::HEXAHEDRON) {
write_int_binary(12); //VTK_HEXAHEDRON
end_line_binary();
}
else if (eltype == ElementShape::TETRA) {
write_int_binary(10); //VTK_TETRA
end_line_binary();
}
else if (eltype == ElementShape::TRIANGLE) {
write_int_binary(5); //VTK_TRIANGLE
end_line_binary();
}
else if (eltype == ElementShape::LINE) {
write_int_binary(3); //VTK_LINE
end_line_binary();
}
else {
LOG_N_TIMES(10, ERROR) << "Don't now what type of elements here it is (el_num = " << i << ")";
}
}
}
void VtkProcessor::write_point_data(std::ofstream &file) {
size_t nn = storage->nNodes();
file << "POINT_DATA " << nn << std::endl;
std::vector<Vec<3> > dataVector;
std::vector<double> dataScalar;
if (useDisplacementsDofs) {
dataVector.assign(nn, Vec<3> ());
for (uint32 j = 1; j <= nn; j++) {
if (storage->isNodeDofUsed(j, Dof::UX)) {
dataVector[j-1][0] = storage->getNodeDofSolution(j, Dof::UX);
}
if (storage->isNodeDofUsed(j, Dof::UY)) {
dataVector[j-1][1] = storage->getNodeDofSolution(j, Dof::UY);
}
if (storage->isNodeDofUsed(j, Dof::UZ)) {
dataVector[j-1][2] = storage->getNodeDofSolution(j, Dof::UZ);
}
}
writeVector(file, "disp", dataVector);
for (uint32 j = 1; j <= nn; j++) {
dataVector[j-1][0] = storage->getReaction(j, Dof::UX);
dataVector[j-1][1] = storage->getReaction(j, Dof::UY);
dataVector[j-1][2] = storage->getReaction(j, Dof::UZ);
}
writeVector(file, "reaction", dataVector);
dataVector.clear();
}
for (auto v : nodalDofTypes) {
dataScalar.assign(nn, 0.0);
for (uint32 j = 1; j <= nn; j++) {
if (storage->isNodeDofUsed(j, v)) {
dataScalar[j-1] = storage->getNodeDofSolution(j, v);
}
}
writeScalar(file, Dof::dofType2label(v), dataScalar);
dataScalar.clear();
}
}
void VtkProcessor::write_point_data_binary() {
size_t nn = storage->nNodes();
fprintf(fp, "POINT_DATA %d\n", nn);
std::vector<Vec<3> > dataVector;
std::vector<double> dataScalar;
if (useDisplacementsDofs) {
dataVector.assign(nn, Vec<3>());
for (uint32 j = 1; j <= nn; j++) {
if (storage->isNodeDofUsed(j, Dof::UX)) {
dataVector[j - 1][0] = storage->getNodeDofSolution(j, Dof::UX);
}
if (storage->isNodeDofUsed(j, Dof::UY)) {
dataVector[j - 1][1] = storage->getNodeDofSolution(j, Dof::UY);
}
if (storage->isNodeDofUsed(j, Dof::UZ)) {
dataVector[j - 1][2] = storage->getNodeDofSolution(j, Dof::UZ);
}
}
writeVector_binary("disp", dataVector);
for (uint32 j = 1; j <= nn; j++) {
dataVector[j - 1][0] = storage->getReaction(j, Dof::UX);
dataVector[j - 1][1] = storage->getReaction(j, Dof::UY);
dataVector[j - 1][2] = storage->getReaction(j, Dof::UZ);
}
writeVector_binary("reaction", dataVector);
dataVector.clear();
}
for (auto v : nodalDofTypes) {
dataScalar.assign(nn, 0.0);
for (uint32 j = 1; j <= nn; j++) {
if (storage->isNodeDofUsed(j, v)) {
dataScalar[j - 1] = storage->getNodeDofSolution(j, v);
}
}
writeScalar_binary(Dof::dofType2label(v), dataScalar);
dataScalar.clear();
}
}
//Write cell data averaging from all integration points.
//Use global coordinate system. all futher transformations
//should be done on paraview side.
void VtkProcessor::write_cell_data(std::ofstream &file) {
size_t en = storage->nElements();
std::vector<MatSym<3> > dataTensor;
std::vector<Vec<3> > dataVector;
std::vector<double> dataScalar;
// if queries are empty - no cell data to be stored into VTK
if (cellScalarQueries.size() == 0 && cellVectorQueries.size() == 0 &&
cellTensorQueries.size() == 0 && elementDofTypes.size() == 0) {
return;
}
file << "CELL_DATA " << en << std::endl;
// write element DoFs
for (auto v : elementDofTypes) {
dataScalar.assign(en, 0.0);
for (uint32 j = 1; j <= en; j++) {
if (storage->isElementDofUsed(j, v)) {
dataScalar[j-1] = storage->getElementDofSolution(j, v);
}
}
writeScalar(file, Dof::dofType2label(v), dataScalar);
dataScalar.clear();
}
// write Element scalar results (only if they are requested in cellScalarQueries)
for(auto query : cellScalarQueries) {
dataScalar.assign(en, 0.0);
for (uint32 i = 1; i <= storage->nElements(); i++) {
dataScalar[i-1] = 0.0;
storage->getElement(i).getScalar(&(dataScalar[i-1]), query);
}
writeScalar(file, query2label(query), dataScalar);
}
// write Element vector results (only if they are requested in cellScalarQueries)
for(auto query : cellVectorQueries) {
dataVector.assign(en, Vec<3>());
for (uint32 i = 1; i <= storage->nElements(); i++) {
dataVector[i-1].zero();
storage->getElement(i).getVector(&(dataVector[i-1]), query);
}
writeVector(file, query2label(query), dataVector);
}
// write Element tensor results (only if they are requested in cellScalarQueries)
for(auto query : cellTensorQueries) {
dataTensor.assign(en, MatSym<3>());
for (uint32 i = 1; i <= storage->nElements(); i++) {
dataTensor[i-1].zero();
storage->getElement(i).getTensor(&(dataTensor[i-1]), query);
}
writeTensor(file, query2label(query), dataTensor);
}
}
void VtkProcessor::write_cell_data_binary() {
size_t en = storage->nElements();
std::vector<MatSym<3> > dataTensor;
std::vector<Vec<3> > dataVector;
std::vector<double> dataScalar;
// if queries are empty - no cell data to be stored into VTK
if (cellScalarQueries.size() == 0 && cellVectorQueries.size() == 0 &&
cellTensorQueries.size() == 0 && elementDofTypes.size() == 0) {
return;
}
fprintf(fp, "CELL_DATA %d\n", en);
// write element DoFs
for (auto v : elementDofTypes) {
dataScalar.assign(en, 0.0);
for (uint32 j = 1; j <= en; j++) {
if (storage->isElementDofUsed(j, v)) {
dataScalar[j - 1] = storage->getElementDofSolution(j, v);
}
}
writeScalar_binary(Dof::dofType2label(v), dataScalar);
dataScalar.clear();
}
// write Element scalar results (only if they are requested in cellScalarQueries)
for (auto query : cellScalarQueries) {
dataScalar.assign(en, 0.0);
for (uint32 i = 1; i <= storage->nElements(); i++) {
dataScalar[i - 1] = 0.0;
storage->getElement(i).getScalar(&(dataScalar[i - 1]), query);
}
writeScalar_binary(query2label(query), dataScalar);
}
// write Element vector results (only if they are requested in cellScalarQueries)
for (auto query : cellVectorQueries) {
dataVector.assign(en, Vec<3>());
for (uint32 i = 1; i <= storage->nElements(); i++) {
dataVector[i - 1].zero();
storage->getElement(i).getVector(&(dataVector[i - 1]), query);
}
writeVector_binary(query2label(query), dataVector);
}
// write Element tensor results (only if they are requested in cellScalarQueries)
for (auto query : cellTensorQueries) {
dataTensor.assign(en, MatSym<3>());
for (uint32 i = 1; i <= storage->nElements(); i++) {
dataTensor[i - 1].zero();
storage->getElement(i).getTensor(&(dataTensor[i - 1]), query);
}
writeTensor_binary(query2label(query), dataTensor);
}
}
void VtkProcessor::writeScalar(std::ofstream &file, const char* name, std::vector<double>& data) {
file << "SCALARS " << name << " float 1" << std::endl;
file << "LOOKUP_TABLE default"<< std::endl;
for (size_t i = 0; i < data.size(); i++) {
file << data[i] << std::endl;
}
file << std::endl;
}
void VtkProcessor::writeScalar_binary(const char* name, std::vector<double>& data) {
fprintf(fp, "SCALARS %s float 1\n", name);
fprintf(fp, "LOOKUP_TABLE default\n");
for (size_t i = 0; i < data.size(); i++) {
write_float_binary(data[i]);
end_line_binary();
}
end_line_binary();
}
void VtkProcessor::writeTensor(std::ofstream &file, const char* name, std::vector<MatSym<3> >& data) {
for (size_t j = 0; j < 6; j++) {
file << "SCALARS " << name << "_" << solidmech::labelsTensorComponent[j] << " float 1" << std::endl;
file << "LOOKUP_TABLE default"<< std::endl;
for (size_t i = 0; i < data.size(); i++) {
file << data[i].data[j] << std::endl;
}
file << std::endl;
}
}
void VtkProcessor::writeTensor_binary(const char* name, std::vector<MatSym<3> >& data) {
for (size_t j = 0; j < 6; j++) {
fprintf(fp, "SCALARS %s_%s float 1\n", name, solidmech::labelsTensorComponent[j]);
fprintf(fp, "LOOKUP_TABLE default\n");
for (size_t i = 0; i < data.size(); i++) {
write_float_binary(data[i].data[j]);
end_line_binary();
}
end_line_binary();
}
}
void VtkProcessor::writeVector(std::ofstream &file, const char* name, std::vector<Vec<3> >& data) {
file << "VECTORS " << name << " float" << std::endl;
for (size_t i = 0; i < data.size(); i++) {
file << data[i] << std::endl;
}
file << std::endl;
}
void VtkProcessor::writeVector_binary(const char* name, std::vector<Vec<3> >& data) {
fprintf(fp, "VECTORS %s float\n", name);
for (size_t i = 0; i < data.size(); i++) {
write_float_binary(data[i][0]);
write_float_binary(data[i][1]);
write_float_binary(data[i][2]);
end_line_binary();
}
end_line_binary();
}
void VtkProcessor::revealAllResults() {
std::set<ElementType> typesRevealed;
bool ret;
for (uint32 i = 1; i <= storage->nElements(); i++) {
Element& el = storage->getElement(i);
ElementType etype = el.getType();
if(typesRevealed.find(etype) != typesRevealed.end()) continue;
// remember that we already revealed the elements of type `etype`
typesRevealed.insert(etype);
// This code doesn't work on -O2 optimized code under clang..
// Here is a kludge - we just print out the etype number..
LOG(INFO) << static_cast<int>(etype);
double sdummy = 0.0;
scalarQuery squery = scalarQuery::UNDEF;
// Loop over scalarQuery items
while(true) {
squery = static_cast<scalarQuery>(static_cast<int>(squery) + 1);
if(squery == scalarQuery::LAST) break;
sdummy = 0.0;
// try to ask the element about particular query code. If result is false, than this element
// know nothing about the query code, skip the code.
ret = el.getScalar(&sdummy, squery);
if(ret == true) {
cellScalarQueries.insert(squery);
}
}
Vec<3> vdummy;
vectorQuery vquery = vectorQuery::UNDEF;
// Loop over vectorQuery items
while(true) {
vquery = static_cast<vectorQuery>(static_cast<int>(vquery) + 1);
if(vquery == vectorQuery::LAST) break;
vdummy.zero();
// try to ask the element about particular query code. If result is false, than this element
// know nothing about the query code, skip the code.
ret = el.getVector(&vdummy, vquery);
if(ret == true) {
cellVectorQueries.insert(vquery);
}
}
MatSym<3> mdummy;
tensorQuery tquery = tensorQuery::UNDEF;
// Loop over tensorQuery items
while(true) {
tquery = static_cast<tensorQuery>(static_cast<int>(tquery) + 1);
if(tquery == tensorQuery::LAST) break;
mdummy.zero();
// try to ask the element about particular query code. If result is false, than this element
// know nothing about the query code, skip the code.
ret = el.getTensor(&mdummy, tquery);
if(ret == true) {
cellTensorQueries.insert(tquery);
}
}
}
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include <set>
#include <map>
#include "sys.h"
#include "PostProcessor.h"
#include "FEStorage.h"
#include "elements/query.h"
namespace nla3d {
// VtkProcessor is dedicated to dump mesh and results into *.vtk legacy textual format. On pre()
// callback filename0.vtk is written which contains initial state of FE problem before solution. On
// every solution step process(...) callback is called which writes filename%d.vtk with solution
// data relevant to this step. Later this banch of files could be used to visualize solution fields
// in external program (ex. Paraview).
//
// By default, VtkProcessor write DoF fields (displacements, reactions for structural problems and
// temperatures for thermal). But by registering element results by registerResult(...) one can add
// addition fields (scalar, vector or tensor ones) to be written into vtk files.
class VtkProcessor : public PostProcessor {
public:
VtkProcessor(FEStorage *st, std::string _fileName);
virtual ~VtkProcessor();
virtual void pre();
virtual void process(uint16 curLoadstep);
virtual void post(uint16 curLoadstep);
// One can use writeAllResults(true) in order to ask VtkProcess to determine which query codes is
// relevant to FEStorage's elements automatically. As results, all accessible element results will
// be written into vtk files.
void writeAllResults(bool write = true);
// The methods below can be used to manually set query codes for elements. Every query will be add
// addition cell data fields into vtk file.
bool registerResults(std::initializer_list<scalarQuery> queries);
bool registerResults(std::initializer_list<vectorQuery> queries);
bool registerResults(std::initializer_list<tensorQuery> queries);
private:
std::string file_name;
void write_header(std::ofstream &file);
void write_header_binary();
// write geometry(mesh) into the vtk's `file`. If `def == true` then write node coordinates in
// deformed state
void write_geometry(std::ofstream &file, bool def = false);
void write_geometry_binary(bool def = false);
void write_point_data(std::ofstream &file);
void write_point_data_binary();
void write_cell_data(std::ofstream &file);
void write_cell_data_binary();
void writeScalar(std::ofstream &file, const char* name, std::vector<double>& data);
void writeScalar_binary(const char* name, std::vector<double>& data);
void writeTensor(std::ofstream &file, const char* name, std::vector<math::MatSym<3> >& data);
void writeTensor_binary(const char* name, std::vector<math::MatSym<3> >& data);
void writeVector(std::ofstream &file, const char* name, std::vector<math::Vec<3> >& data);
void writeVector_binary(const char* name, std::vector<math::Vec<3> >& data);
void write_vecres(std::ofstream &file);
void revealAllResults();
bool useDisplacementsDofs = false;
bool _writeAllResults = false;
std::set<Dof::dofType> nodalDofTypes;
std::set<Dof::dofType> elementDofTypes;
std::set<scalarQuery> cellScalarQueries;
std::set<vectorQuery> cellVectorQueries;
std::set<tensorQuery> cellTensorQueries;
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "elements/ElementFactory.h"
#include "elements/PLANE41.h"
#include "elements/SOLID81.h"
#include "elements/TRUSS3.h"
#include "elements/TETRA0.h"
#include "elements/TETRA1.h"
#include "elements/QUADTH.h"
#include "elements/TRIANGLE4.h"
namespace nla3d {
ElementType ElementFactory::elName2elType (std::string elName) {
for (uint16 i = 0; i < (uint16) ElementType::UNDEFINED; i++) {
if (elName.compare(elTypeLabels[i]) == 0) {
return (ElementType) i;
}
}
return ElementType::UNDEFINED;
}
void ElementFactory::createElements (ElementType elId, const uint32 n, std::vector<Element*>& ptr) {
if (elId == ElementType::UNDEFINED) {
LOG(FATAL) << "Element type is undefined";
}
if (n == 0) {
return;
}
// reserve memory in the vector for newly created elements
ptr.reserve(ptr.size() + n);
switch (elId) {
case ElementType::PLANE41:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementPLANE41());
}
break;
case ElementType::SOLID81:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementSOLID81());
}
break;
case ElementType::TRUSS3:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementTRUSS3());
}
break;
case ElementType::TRIANGLE4:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementTRIANGLE4());
}
break;
case ElementType::TETRA0:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementTETRA0());
}
break;
case ElementType::TETRA1:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementTETRA1());
}
break;
case ElementType::QUADTH:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new ElementQUADTH());
}
break;
case ElementType::SurfaceLINETH:
for (uint32 i = 0; i < n; i++) {
ptr.push_back(new SurfaceLINETH());
}
break;
default:
LOG(ERROR) << "Don't have an element with id " << (uint16) elId;
}
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
namespace nla3d {
class Element;
class ElementFactory {
public:
static ElementType elName2elType (std::string elName);
static void createElements (ElementType elId, const uint32 n, std::vector<Element*>& ptr);
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "elements/PLANE41.h"
namespace nla3d {
using namespace math;
using namespace solidmech;
const solidmech::tensorComponents ElementPLANE41::components[3] = {M_XX, M_YY, M_XY};
const uint16 ElementPLANE41::num_components = 3;
//------------------ElementPLANE41--------------------
void ElementPLANE41::pre() {
if (det.size()==0) {
makeJacob();
}
S.assign(nOfIntPoints(), Vec<3>(0.0f, 0.0f, 0.0f));
C.assign(nOfIntPoints(), Vec<3>(1.0f, 1.0f, 0.0f));
O.assign(nOfIntPoints(), Vec<4>(0.0f, 0.0f, 0.0f, 0.0f));
// register element equations
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::UX, Dof::UY});
}
storage->addElementDof(getElNum(), {Dof::HYDRO_PRESSURE});
}
void ElementPLANE41::buildK() {
Mat<8,8> Kuu; // displacement stiff. matrix
Mat<8,1> Kup;
Mat<9,9> Ke; // element stiff. matrix
double Kpp = 0.0;
double Fp = 0.0;
Vec<8> Qe; // displacement rhs
Vec<9> Fe; // element
Vec<6> CVec;
CVec[M_XZ] = 0.0;
CVec[M_YZ] = 0.0;
CVec[M_ZZ] = 1.0;
MatSym<3> matD_d;
Vec<3> vecD_p;
double p_e = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
Mat_Hyper_Isotrop_General* mat = dynamic_cast<Mat_Hyper_Isotrop_General*> (storage->getMaterial());
CHECK_NOTNULL(mat);
double k = mat->getK();
double dWt; //Gaussian quadrature
for (uint16 np=0; np < nOfIntPoints(); np++) {
dWt = intWeight(np);
// all meterial functions are waiting [C] for 3D case. So we need to use CVec here.
CVec[M_XX] = C[np][0];
CVec[M_YY] = C[np][1];
CVec[M_XY] = C[np][2];
mat->getDdDp_UP(num_components, components, CVec.ptr(), p_e, matD_d.ptr(), vecD_p.ptr());
//matD_d will be 3x3 symmetric matrix. We need to convert it onto 3x3 usual matrix
Mat<3,3> matE_c = matD_d.toMat();
Mat<3,1> matE_p;
matE_p[0][0] = vecD_p[0];
matE_p[1][0] = vecD_p[1];
matE_p[2][0] = vecD_p[2];
double J = solidmech::J_C(CVec.ptr());
Mat<3,8> matB = make_B(np);
//матрица S для матричного умножения
Mat<4,4> matS = Mat<4,4>(S[np][0],S[np][2], 0.0, 0.0,
S[np][2],S[np][1], 0.0, 0.0,
0.0, 0.0, S[np][0],S[np][2],
0.0, 0.0, S[np][2],S[np][1]);
//матрица Омега.используется для составления
//матр. накопленных линейных деформаций к текущему шагу
Mat<3,4> matO = Mat<3,4>(O[np][0], 0.0, O[np][2], 0.0,
0.0, O[np][1], 0.0, O[np][3],
O[np][1], O[np][0], O[np][3], O[np][2]);
Mat<4,8> matBomega = make_Bomega(np);
Mat<3,8> matBl = matO * matBomega;
matB += matBl;
Kuu += (matB.transpose() * matE_c * matB * 2.0 + matBomega.transpose() * matS * matBomega)*dWt;
Fp += (J - 1 - p_e/k)*dWt;
Qe += (matB.transpose() * S[np] * dWt);
Kup+= matB.transpose()*matE_p *dWt;
Kpp -= 1.0/k*dWt;
}// loop over intergration points
//сборка в одну матрицу
for (uint16 i=0; i < 8; i++)
for (uint16 j=0; j < 8; j++)
Ke[i][j] = Kuu[i][j];
for (uint16 i=0; i<8; i++)
Ke[i][8] = Kup[i][0];
for (uint16 i=0; i<8; i++)
Ke[8][i] = Kup[i][0];
Ke[8][8] = Kpp;
for (uint16 i=0; i < 8; i++)
Fe[i] = -Qe[i];
Fe[8] = -Fp;
//загнать в глоб. матрицу жесткости и узловых сил
assembleK(Ke, Fe);
}
//
inline Mat<3,8> ElementPLANE41::make_B(uint16 np) {
Mat<3,8> B = Mat<3,8>(NiXj[np][0][0], 0.0f, NiXj[np][1][0], 0.0f, NiXj[np][2][0], 0.0f, NiXj[np][3][0], 0.0f,
0.0f, NiXj[np][0][1], 0.0f, NiXj[np][1][1], 0.0f, NiXj[np][2][1], 0.0f, NiXj[np][3][1],
NiXj[np][0][1], NiXj[np][0][0], NiXj[np][1][1], NiXj[np][1][0], NiXj[np][2][1], NiXj[np][2][0], NiXj[np][3][1], NiXj[np][3][0]);
return B;
}
//
Mat<4,8> ElementPLANE41::make_Bomega(uint16 np) {
Mat<4,8> Bomega = Mat<4,8>(NiXj[np][0][0], 0.0f, NiXj[np][1][0], 0.0f, NiXj[np][2][0], 0.0f, NiXj[np][3][0], 0.0f,
NiXj[np][0][1], 0.0f, NiXj[np][1][1], 0.0f, NiXj[np][2][1], 0.0f, NiXj[np][3][1], 0.0f,
0.0f, NiXj[np][0][0], 0.0f, NiXj[np][1][0], 0.0f, NiXj[np][2][0], 0.0f, NiXj[np][3][0],
0.0f, NiXj[np][0][1], 0.0f, NiXj[np][1][1], 0.0f, NiXj[np][2][1], 0.0f, NiXj[np][3][1]);
return Bomega;
}
void ElementPLANE41::update() {
// get nodal solutions from storage
Vec<8> U;
for (uint16 i = 0; i < getNNodes(); i++) {
U[i*2 + 0] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UX);
U[i*2 + 1] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UY);
}
Mat_Hyper_Isotrop_General* mat = dynamic_cast<Mat_Hyper_Isotrop_General*> (storage->getMaterial());
CHECK_NOTNULL(mat);
Vec<6> CVec;
CVec[M_XZ] = 0.0;
CVec[M_YZ] = 0.0;
CVec[M_ZZ] = 1.0;
//восстанавливаем преращение давления
double p_e = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
for (uint16 np=0; np < nOfIntPoints(); np++) {
Mat<4,8> matBomega = make_Bomega(np);
O[np] = matBomega * U;
C[np][0] = 1.0f + 2*O[np][0]+1.0f*(O[np][0]*O[np][0]+O[np][2]*O[np][2]);
C[np][1]=1.0f + 2*O[np][3]+1.0f*(O[np][3]*O[np][3]+O[np][1]*O[np][1]);
C[np][2]=O[np][1]+O[np][2]+O[np][0]*O[np][1]+O[np][2]*O[np][3];
//восстановление напряжений Пиолы-Кирхгоффа из текущего состояния
//all meterial functions are waiting [C] for 3D case. So we need to use CVec here.
CVec[M_XX] = C[np][0];
CVec[M_YY] = C[np][1];
CVec[M_XY] = C[np][2];
mat->getS_UP (num_components, components, CVec.ptr(), p_e, S[np].ptr());
}
}
bool ElementPLANE41::getScalar(double* scalar, scalarQuery query, uint16 gp, const double scale) {
//see queries in query.h
//gp - needed gauss point
assert(scalar != nullptr);
if (gp == GP_MEAN) { //need to average result over the element
double dWtSum = volume();
double dWt;
for (uint16 np = 0; np < nOfIntPoints(); np ++) {
dWt = intWeight(np);
bool ret = getScalar(scalar, query, np, dWt / dWtSum * scale);
if(ret == false) return false;
}
return true;
}
switch (query) {
case scalarQuery::SP:
*scalar += storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE) * scale;
return true;
}
return false;
}
bool ElementPLANE41::getVector(math::Vec<3>* vector, vectorQuery query, uint16 gp, const double scale) {
assert(vector != nullptr);
if (gp == GP_MEAN) { //need to average result over the element
double dWtSum = volume();
double dWt;
for (uint16 np = 0; np < nOfIntPoints(); np ++) {
dWt = intWeight(np);
bool ret = getVector(vector, query, np, dWt / dWtSum * scale);
if(ret == false) return false;
}
return true;
}
double IC[3];
Vec<6> CVec;
switch (query) {
case vectorQuery::IC:
CVec[M_XZ] = 0.0;
CVec[M_YZ] = 0.0;
CVec[M_ZZ] = 1.0;
CVec[M_XX] = C[gp][0];
CVec[M_YY] = C[gp][1];
CVec[M_XY] = C[gp][2];
solidmech::IC_C(CVec.ptr(), IC);
vector[0] += IC[0] * scale;
vector[1] += IC[1] * scale;
vector[2] += IC[2] * scale;
return true;
}
return false;
}
//return a tensor in a global coordinate system
bool ElementPLANE41::getTensor(math::MatSym<3>* tensor, tensorQuery query, uint16 gp, const double scale) {
assert(tensor != nullptr);
if (gp == GP_MEAN) { //need to average result over the element
double dWtSum = volume();
double dWt;
for (uint16 np = 0; np < nOfIntPoints(); np ++) {
dWt = intWeight(np);
bool ret = getTensor(tensor, query, np, dWt / dWtSum * scale);
if(ret == false) return false;
}
return true;
}
// here we obtain result for query on particular Gaussian point
assert (gp < nOfIntPoints());
MatSym<3> matS;
Mat_Hyper_Isotrop_General* mat;
Mat<3,3> matF;
double J;
Vec<6> CVec;
CVec[M_XZ] = 0.0;
CVec[M_YZ] = 0.0;
CVec[M_ZZ] = 1.0;
CVec[M_XX] = C[gp][0];
CVec[M_YY] = C[gp][1];
CVec[M_XY] = C[gp][2];
double p_e;
switch (query) {
case tensorQuery::COUCHY:
matF.zero();
matF.data[0][0] = 1+O[gp][0]; //11
matF.data[0][1] = O[gp][1]; //12
matF.data[1][0] = O[gp][2]; //21
matF.data[1][1] = 1+O[gp][3];//22
matF.data[2][2] = 1; //33
J = matF.data[0][0]*(matF.data[1][1]*matF.data[2][2]-matF.data[1][2]*matF.data[2][1])-
matF.data[0][1]*(matF.data[1][0]*matF.data[2][2]-matF.data[1][2]*matF.data[2][0])+
matF.data[0][2]*(matF.data[1][0]*matF.data[2][1]-matF.data[1][1]*matF.data[2][0]);
//In order to complete matS (3x3 symmetric matrix, PK2 tensor) we need
//to know S33 component:
//1) One solution is to calculate S33 on every solution step
//and store it in S[np] vector.
//2) Second solution is to resotre S33 right here.
//Now 2) is working.
p_e = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
mat = dynamic_cast<Mat_Hyper_Isotrop_General*>(storage->getMaterial());
CHECK_NOTNULL(mat);
mat->getS_UP(6, solidmech::defaultTensorComponents, CVec.ptr(), p_e, matS.data);
matBTDBprod(matF, matS, 1.0/J, *tensor); //Symmetric Couchy tensor
return true;
case tensorQuery::PK2:
mat = dynamic_cast<Mat_Hyper_Isotrop_General*>(storage->getMaterial());
CHECK_NOTNULL(mat);
p_e = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
mat->getS_UP(6, solidmech::defaultTensorComponents, CVec.ptr(), p_e, matS.data);
tensor->data[0] += matS.data[0]*scale;
tensor->data[1] += matS.data[1]*scale;
tensor->data[2] += matS.data[2]*scale;
tensor->data[3] += matS.data[3]*scale;
tensor->data[4] += matS.data[4]*scale;
tensor->data[5] += matS.data[5]*scale;
return true;
case tensorQuery::C:
tensor->data[0] += CVec[M_XX]*scale;
tensor->data[1] += CVec[M_XY]*scale;
tensor->data[2] += CVec[M_XZ]*scale;
tensor->data[3] += CVec[M_YY]*scale;
tensor->data[4] += CVec[M_YZ]*scale;
tensor->data[5] += CVec[M_ZZ]*scale;
return true;
case tensorQuery::E:
tensor->data[0] += (CVec[M_XX]-1.0)*0.5*scale;
tensor->data[1] += CVec[M_XY]*0.5*scale;
tensor->data[2] += CVec[M_XZ]*0.5*scale;
tensor->data[3] += (CVec[M_YY]-1.0)*0.5*scale;
tensor->data[4] += CVec[M_YZ]*0.5*scale;
tensor->data[5] += (CVec[M_ZZ]-1.0)*0.5*scale;
return true;
}
return false;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
#include "elements/isoparametric.h"
#include "FEStorage.h"
#include "solidmech.h"
namespace nla3d {
//4-node 2D QUAD nonlinear element based on mixed approach
class ElementPLANE41 : public ElementIsoParamQUAD {
public:
ElementPLANE41 () {
intOrder = 2;
type = ElementType::PLANE41;
}
ElementPLANE41 (const ElementPLANE41& from) {
operator=(from);
}
//solving procedures
void pre();
void buildK();
void update();
math::Mat<3,8> make_B (uint16 nPoint); //функция создает линейную матрицу [B]
math::Mat<4,8> make_Bomega (uint16 nPoint); //функция создает линейную матрицу [Bomega]
//postproc procedures
bool getScalar(double* scalar, scalarQuery code, uint16 gp, const double scale);
bool getVector(math::Vec<3>* vector, vectorQuery code, uint16 gp, const double scale);
bool getTensor(math::MatSym<3>* tensor, tensorQuery code, uint16 gp, const double scale);
// internal element data
// S[0] - Sx S[1] - Sy S[2] - Sxy
std::vector<math::Vec<3> > S; //S[номер т. интегр.][номер напряжения] - напряжения Пиолы-Кирхгоффа
// C[0] - C11 C[1] - C22 C[2] - C12
std::vector<math::Vec<3> > C; //C[номер т. интегр.][номер деформ.] - компоненты матрицы меры деформации
// O[0] - dU/dx O[1] - dU/dy O[2] - dV/dx O[3] - dV/dy
std::vector<math::Vec<4> > O; //S[номер т. интегр.][номер омеги]
// addition data
static const solidmech::tensorComponents components[3];
static const uint16 num_components;
template <uint16 el_dofs_num>
void assembleK(const math::Mat<el_dofs_num,el_dofs_num> &Ke, const math::Vec<el_dofs_num> &Qe);
};
template <uint16 el_dofs_num>
void ElementPLANE41::assembleK(const math::Mat<el_dofs_num,el_dofs_num> &Ke, const math::Vec<el_dofs_num> &Qe)
{
uint16 dim = 2;
uint16 eds = 8;
uint16 eldofs = 1;
Dof::dofType nodeDofVec[] = {Dof::UX, Dof::UY};
Dof::dofType elementDofVec[] = {Dof::HYDRO_PRESSURE};
assert(getNNodes() * dim + eldofs == el_dofs_num);
// upper diagonal process for nodal dofs
for (uint16 i=0; i < getNNodes(); i++)
for (uint16 j=i; j < getNNodes(); j++)
for (uint16 di=0; di < dim; di++)
for (uint16 dj=0; dj < dim; dj++)
if ((i==j) && (dj>di)) continue;
else
storage->addValueK(nodes[i], nodeDofVec[di],nodes[j],nodeDofVec[dj], Ke[i*dim+di][j*dim+dj]);
//upper diagonal process for nodes-el dofs
for (uint16 i=0; i < getNNodes(); i++)
for(uint16 di=0; di < dim; di++) {
uint32 noEq = storage->getNodeDofEqNumber(nodes[i], nodeDofVec[di]);
for (uint16 dj=0; dj < eldofs; dj++) {
uint32 elEq = storage->getElementDofEqNumber(getElNum(), elementDofVec[dj]);
storage->addValueK(noEq, elEq, Ke[i*dim+di][eds+dj]);
}
}
//upper diagonal process for el-el dofs
for (uint16 di=0; di < eldofs; di++)
for (uint16 dj=di; dj < eldofs; dj++) {
uint32 elEqi = storage->getElementDofEqNumber(getElNum(), elementDofVec[di]);
uint32 elEqj = storage->getElementDofEqNumber(getElNum(), elementDofVec[dj]);
storage->addValueK(elEqi, elEqj, Ke[eds+di][eds+dj]);
}
for (uint16 i=0; i < getNNodes(); i++)
for (uint16 di=0; di < dim; di++)
storage->addValueF(nodes[i], nodeDofVec[di], Qe[i*dim+di]);
for (uint16 di=0; di < eldofs; di++) {
uint32 elEq = storage->getElementDofEqNumber(getElNum(), elementDofVec[di]);
storage->addValueF(elEq, Qe[eds+di]);
}
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "elements/QUADTH.h"
namespace nla3d {
using namespace math;
void ElementQUADTH::pre() {
if (det.size()==0) {
makeJacob();
}
// register element equations
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::TEMP});
}
}
void ElementQUADTH::buildK() {
MatSym<4> Ke; // element stiff. matrix
Ke.zero();
Vec<4> Fe; // rhs of element equations
Fe.zero();
MatSym<2> mat_k;
mat_k.zero();
mat_k.comp(0,0) = k;
mat_k.comp(1,1) = k;
// build Ke
double dWt; //Gaussian quadrature weight
for (uint16 np=0; np < nOfIntPoints(); np++) {
dWt = intWeight(np);
Mat<2,4> matB = make_B(np);
matBTDBprod(matB, mat_k, dWt, Ke);
// for volume flux
if (volFlux != 0.0) {
Fe += formFunc(np) * (volFlux * dWt);
}
}// loop over integration points
assembleK(Ke, Fe, {Dof::TEMP});
}
void ElementQUADTH::buildC() {
MatSym<4> Ce; // element stiff. matrix
Ce.zero();
// build Ke
double dWt; //Gaussian quadrature weight
for (uint16 np=0; np < nOfIntPoints(); np++) {
dWt = intWeight(np);
Vec<4> ff = formFunc(np);
for (uint16 i = 0; i < 4; i++)
for (uint16 j = i; j < 4; j++)
Ce.comp(i, j) += ff[i] * ff[j] * rho * c * dWt;
}// loop over integration points
assembleC(Ce, {Dof::TEMP});
}
inline Mat<2,4> ElementQUADTH::make_B(uint16 np) {
return Mat<2,4>(NiXj[np][0][0], NiXj[np][1][0], NiXj[np][2][0], NiXj[np][3][0],
NiXj[np][0][1], NiXj[np][1][1], NiXj[np][2][1], NiXj[np][3][1]);
}
void ElementQUADTH::update() {
}
void SurfaceLINETH::pre() {
makeJacob();
// register element equations
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::TEMP});
}
// if environment temperature isn't defined for node 2, we suppose that they are equal
if (etemp[1] == 0.0) {
etemp[1] = etemp[0];
}
}
void SurfaceLINETH::buildK() {
MatSym<2> Ke; // element stiff. matrix
Ke.zero();
Vec<2> Fe; // rhs of element equations
Fe.zero();
double dWt; //Gaussian quadrature weight
// flux over surface (line)
if (flux != 0.0) {
for (uint16 np = 0; np < nOfIntPoints(); np++) {
dWt = intWeight(np);
Vec<2> Ns = formFunc(np);
Fe += Ns * (flux * dWt);
}
}
// convection over surface (line)
if (htc != 0.0) {
for (uint16 np = 0; np < nOfIntPoints(); np++) {
dWt = intWeight(np);
Vec<2> Ns = formFunc(np);
MatSym<2> tmp;
tmp.comp(0,0) = Ns[0] * Ns[0] * htc * dWt;
tmp.comp(0,1) = Ns[0] * Ns[1] * htc * dWt;
tmp.comp(1,0) = tmp.comp(0,1);
tmp.comp(1,1) = Ns[1] * Ns[1] * htc * dWt;
//matAATprod(Ns, htc * dWt, tmp);
Ke += tmp;
matBVprod(tmp, etemp, 1.0, Fe);
}
}
assembleK(Ke, Fe, {Dof::TEMP});
}
void SurfaceLINETH::update() {
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
#include "elements/isoparametric.h"
#include "FEStorage.h"
#include "solidmech.h"
namespace nla3d {
//4-node 2D QUAD element for steady thermal analysis
class ElementQUADTH : public ElementIsoParamQUAD {
public:
ElementQUADTH () {
intOrder = 2;
type = ElementType::QUADTH;
}
//solving procedures
void pre();
void buildK();
void buildC();
void buildM() { };
void update();
math::Mat<2,4> make_B (uint16 nPoint); // make derivatives matrix
// conductivity coef ( W/(K m), for example)
double k = 0.0;
double rho = 0.0; // density
double c = 0.0; // capacity
// volume flux
double volFlux = 0.0;
};
class SurfaceLINETH : public ElementIsoParamLINE {
public:
SurfaceLINETH () {
intOrder = 2;
type = ElementType::SurfaceLINETH;
}
//solving procedures
void pre();
void buildK();
void buildC() { };
void buildM() { };
void update();
double flux = 0.0;
double htc = 0.0;
math::Vec<2> etemp = {0.0, 0.0};
};
} // nla3d namespace
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "elements/SOLID81.h"
namespace nla3d {
using namespace math;
using namespace solidmech;
void ElementSOLID81::pre() {
if (det.size()==0) {
makeJacob();
}
S.assign(nOfIntPoints(), Vec<6>(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
C.assign(nOfIntPoints(), Vec<6>(1.0, 0.0, 0.0, 1.0, 0.0, 1.0));
O.assign(nOfIntPoints(), Vec<9>(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
// register element equations
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::UX, Dof::UY, Dof::UZ});
}
storage->addElementDof(getElNum(), {Dof::HYDRO_PRESSURE});
}
void ElementSOLID81::buildK() {
double Kpp = 0.0;
double Fp = 0.0;
Vec<24> Kup;
Vec<24> Fu; //вектор узловых сил элемента
Vec<24> F_ext; //вектор внешних сил (пока не подсчитывается)
Mat_Hyper_Isotrop_General* mat = CHECK_NOTNULL( dynamic_cast<Mat_Hyper_Isotrop_General*> (storage->getMaterial()));
double k = mat->getK();
MatSym<6> matD_d;
Vec<6> vecD_p;
Vec<6> vecC;
Mat<6,24> matB;
MatSym<9> matS;
Mat<6,9> matO;
Mat<9,24> matB_NL;
MatSym<24> Kuu; //матрица жесткости перед вектором перемещений
double p_e = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
double dWt; //множитель при суммировании квадратур Гаусса
Kuu.zero();
for (uint16 np = 0; np < nOfIntPoints(); np++) {
dWt = intWeight(np);
mat->getDdDp_UP(6, solidmech::defaultTensorComponents, C[np].ptr(), p_e, matD_d.ptr(), vecD_p.ptr());
double J = solidmech::J_C(C[np].ptr());
matB.zero();
matS.zero();
matO.zero();
matB_NL.zero();
make_B_L(np, matB);
make_S(np, matS); //matrix 9x9 with 3x3 stress tenros blocks
make_Omega(np, matO);
make_B_NL(np, matB_NL);
// matB = matB + matO * matB_NL * 2
matABprod(matO, matB_NL, 2.0, matB);
// Kuu = Kuu + (matB^T * matD_d * matB) * 0.5*dWt
matBTDBprod(matB, matD_d, 0.5*dWt, Kuu);
// Kuu = Kuu + (matB_NL^T * matS * matB_NL) * dWt
matBTDBprod(matB_NL, matS, dWt, Kuu);
// Fu = Fu + matB^T * S[np] * (-0.5*dWt);
matBTVprod(matB,S[np], -0.5*dWt, Fu);
// Kup = Kup + matB^T * vecD_p * (dWt*0.5);
matBTVprod(matB,vecD_p, 0.5*dWt, Kup);
Fp += -(J - 1 - p_e/k)*dWt;
Kpp += -1.0/k*dWt;
}//прошлись по всем точкам интегрирования
assemble3(Kuu, Kup, Kpp, Fu,Fp);
}
void ElementSOLID81::update()
{
// get nodal solutions from storage
Vec<24> U;
for (uint16 i = 0; i < getNNodes(); i++) {
U[i*3 + 0] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UX);
U[i*3 + 1] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UY);
U[i*3 + 2] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UZ);
}
Mat<9,24> B_NL;
Vec<6> vecC;
double p_e = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
Mat_Hyper_Isotrop_General* mat = CHECK_NOTNULL(dynamic_cast<Mat_Hyper_Isotrop_General*> (storage->getMaterial()));
for (uint16 np = 0; np < nOfIntPoints(); np++) {
B_NL.zero();
make_B_NL(np, B_NL);
O[np].zero();
// O[np] = B_NL * U
matBVprod(B_NL, U, 1.0, O[np]);
C[np][M_XX] = 1.0+2*O[np][0]+pow(O[np][0],2)+pow(O[np][3],2)+pow(O[np][6],2); //C11
C[np][M_YY] = 1.0+2*O[np][4]+pow(O[np][1],2)+pow(O[np][4],2)+pow(O[np][7],2); //C22
C[np][M_ZZ] = 1.0+2*O[np][8]+pow(O[np][2],2)+pow(O[np][5],2)+pow(O[np][8],2); //C33
C[np][M_XY] = O[np][1]+O[np][3]+O[np][0]*O[np][1]+O[np][3]*O[np][4]+O[np][6]*O[np][7]; //C12
C[np][M_YZ] = O[np][5]+O[np][7]+O[np][1]*O[np][2]+O[np][4]*O[np][5]+O[np][7]*O[np][8]; //C23
C[np][M_XZ] = O[np][2]+O[np][6]+O[np][0]*O[np][2]+O[np][3]*O[np][5]+O[np][6]*O[np][8]; //C13
// get new PK2 stresses from update C tensor
mat->getS_UP (6, solidmech::defaultTensorComponents, C[np].ptr(), p_e, S[np].ptr());
}
}
void ElementSOLID81::make_B_L (uint16 np, Mat<6,24> &B)
{
double *B_L = B.ptr();
for (uint16 i=0; i < 8; i++) {
B_L[0*24+(i*3+0)] += 2*NiXj[np][i][0]; // exx
B_L[1*24+(i*3+0)] += 2*NiXj[np][i][1]; // exy
B_L[1*24+(i*3+1)] += 2*NiXj[np][i][0]; // exy
B_L[2*24+(i*3+0)] += 2*NiXj[np][i][2]; // exz
B_L[2*24+(i*3+2)] += 2*NiXj[np][i][0]; // exz
B_L[3*24+(i*3+1)] += 2*NiXj[np][i][1]; // eyy
B_L[4*24+(i*3+1)] += 2*NiXj[np][i][2]; // eyz
B_L[4*24+(i*3+2)] += 2*NiXj[np][i][1]; // eyz
B_L[5*24+(i*3+2)] += 2*NiXj[np][i][2]; // ezz
}
}
void ElementSOLID81::make_B_NL (uint16 np, Mat<9,24> &B)
{
double *B_NL = B.ptr();
for (uint16 i=0; i < 8; i++) {
B_NL[0*24+(i*3+0)] += NiXj[np][i][0];
B_NL[1*24+(i*3+0)] += NiXj[np][i][1];
B_NL[2*24+(i*3+0)] += NiXj[np][i][2];
B_NL[3*24+(i*3+1)] += NiXj[np][i][0];
B_NL[4*24+(i*3+1)] += NiXj[np][i][1];
B_NL[5*24+(i*3+1)] += NiXj[np][i][2];
B_NL[6*24+(i*3+2)] += NiXj[np][i][0];
B_NL[7*24+(i*3+2)] += NiXj[np][i][1];
B_NL[8*24+(i*3+2)] += NiXj[np][i][2];
}
}
void ElementSOLID81::make_S (uint16 np, MatSym<9> &B) {
double *Sp = B.ptr();
Sp[0] += S[np][M_XX];
Sp[1] += S[np][M_XY];
Sp[2] += S[np][M_XZ];
Sp[9] += S[np][M_YY];
Sp[10] += S[np][M_YZ];
Sp[17] += S[np][M_ZZ];
Sp[24] += S[np][M_XX];
Sp[25] += S[np][M_XY];
Sp[26] += S[np][M_XZ];
Sp[30] += S[np][M_YY];
Sp[31] += S[np][M_YZ];
Sp[35] += S[np][M_ZZ];
Sp[39] += S[np][M_XX];
Sp[40] += S[np][M_XY];
Sp[41] += S[np][M_XZ];
Sp[42] += S[np][M_YY];
Sp[43] += S[np][M_YZ];
Sp[44] += S[np][M_ZZ];
}
void ElementSOLID81::make_Omega (uint16 np, Mat<6,9> &B) {
double *Omega = B.ptr();
for (uint16 i=0; i < 3; i++) {
Omega[0*9+(i*3+0)] = O[np][0+i*3]; // exx
Omega[1*9+(i*3+0)] = O[np][1+i*3]; // exy
Omega[1*9+(i*3+1)] = O[np][0+i*3]; // exy
Omega[2*9+(i*3+0)] = O[np][2+i*3]; // exz
Omega[2*9+(i*3+2)] = O[np][0+i*3]; // exz
Omega[3*9+(i*3+1)] = O[np][1+i*3]; // eyy
Omega[4*9+(i*3+1)] = O[np][2+i*3]; // eyz
Omega[4*9+(i*3+2)] = O[np][1+i*3]; // eyz
Omega[5*9+(i*3+2)] = O[np][2+i*3]; // ezz
}
}
bool ElementSOLID81::getScalar(double* scalar, scalarQuery query, uint16 gp, const double scale) {
//see queries in query.h
//gp - needed gauss point
if (gp == GP_MEAN) { //need to average result over the element
double dWtSum = volume();
double dWt;
for (uint16 np = 0; np < nOfIntPoints(); np ++) {
dWt = intWeight(np);
bool ret = getScalar(scalar, query, np, dWt / dWtSum * scale);
if(ret == false) return false;
}
return true;
}
// here we obtain results for particular Gaussian point gp
assert (gp < nOfIntPoints());
Vec<3> tmp;
Mat_Hyper_Isotrop_General* mat;
double J;
switch (query) {
case scalarQuery::SP:
*scalar += storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE) * scale;
return true;
case scalarQuery::WU:
getVector(&tmp, vectorQuery::IC, gp, 1.0);
mat = CHECK_NOTNULL(dynamic_cast<Mat_Hyper_Isotrop_General*>(storage->getMaterial()));
*scalar += mat->W(tmp[0], tmp[1], tmp[2]) * scale;
return true;
case scalarQuery::WP:
J = solidmech::J_C(C[gp].ptr());
mat = CHECK_NOTNULL(dynamic_cast<Mat_Hyper_Isotrop_General*>(storage->getMaterial()));
*scalar += 0.5 * mat->getK() * (J - 1.0) * (J - 1.0) * scale;
return true;
case scalarQuery::VOL:
*scalar += volume() * scale;
return true;
}
return false;
}
bool ElementSOLID81::getVector(math::Vec<3>* vector, vectorQuery query, uint16 gp, const double scale) {
if (gp == GP_MEAN) { //need to average result over the element
double dWtSum = volume();
double dWt;
for (uint16 np = 0; np < nOfIntPoints(); np ++) {
dWt = intWeight(np);
bool ret = getVector(vector, query, np, dWt/dWtSum*scale );
if(ret == false) return false;
}
return true;
}
// here we obtain results for particular Gaussian point gp
assert (gp < nOfIntPoints());
double IC[3];
switch (query) {
case vectorQuery::IC:
solidmech::IC_C(C[gp].ptr(), IC);
(*vector)[0] += IC[0] * scale;
(*vector)[1] += IC[1] * scale;
(*vector)[2] += IC[2] * scale;
return true;
}
return false;
}
//return a tensor in a global coordinate system
bool ElementSOLID81::getTensor(math::MatSym<3>* tensor, tensorQuery query, uint16 gp, const double scale) {
if (gp == GP_MEAN) { //need to average result over the element
double dWtSum = volume();
double dWt;
for (uint16 np = 0; np < nOfIntPoints(); np ++) {
dWt = intWeight(np);
bool ret = getTensor(tensor, query, np, dWt / dWtSum * scale);
if(ret == false) return false;
}
return true;
}
assert (gp < nOfIntPoints());
Mat<3,3> matF;
MatSym<3> matS;
double J;
double cInv[6];
double pe;
switch (query) {
case tensorQuery::COUCHY:
//matF^T
matF.data[0][0] = 1+O[gp][0];
matF.data[1][0] = O[gp][1];
matF.data[2][0] = O[gp][2];
matF.data[0][1] = O[gp][3];
matF.data[1][1] = 1+O[gp][4];
matF.data[2][1] = O[gp][5];
matF.data[0][2] = O[gp][6];
matF.data[1][2] = O[gp][7];
matF.data[2][2] = 1+O[gp][8];
J = solidmech::J_C(C[gp].ptr());
//deviatoric part of S: Sd = S[gp]
//hydrostatic part of S: Sp = p * J * C^(-1)
solidmech::invC_C (C[gp].ptr(), J, cInv);
pe = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
// TODO: it seems that S[gp] contains deviatoric + pressure already..
// we dont need to sum it again
for (uint16 i = 0; i < 6; i++) {
matS.data[i] = S[gp][i] + pe * J * cInv[i];
}
matBTDBprod (matF, matS, 1.0/J*scale, *tensor); //Symmetric Couchy tensor
return true;
case tensorQuery::PK2:
// hydrostatic part of S: Sp = p * J * C^(-1)
J = solidmech::J_C(C[gp].ptr());
solidmech::invC_C (C[gp].ptr(), J, cInv);
pe = storage->getElementDofSolution(getElNum(), Dof::HYDRO_PRESSURE);
for (uint16 i = 0; i < 6; i++) {
tensor->data[i] += (S[gp][i] + pe * J * cInv[i]) * scale;
}
return true;
case tensorQuery::C:
tensor->data[0] += C[gp][M_XX]*scale;
tensor->data[1] += C[gp][M_XY]*scale;
tensor->data[2] += C[gp][M_XZ]*scale;
tensor->data[3] += C[gp][M_YY]*scale;
tensor->data[4] += C[gp][M_YZ]*scale;
tensor->data[5] += C[gp][M_ZZ]*scale;
return true;
case tensorQuery::E:
tensor->data[0] += (C[gp][M_XX]-1.0)*0.5*scale;
tensor->data[1] += C[gp][M_XY]*0.5*scale;
tensor->data[2] += C[gp][M_XZ]*0.5*scale;
tensor->data[3] += (C[gp][M_YY]-1.0)*0.5*scale;
tensor->data[4] += C[gp][M_YZ]*0.5*scale;
tensor->data[5] += (C[gp][M_ZZ]-1.0)*0.5*scale;
return true;
}
return false;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
#include "elements/isoparametric.h"
#include "FEStorage.h"
#include "solidmech.h"
namespace nla3d {
//8-node hexahedron nonlinear element based on mixed approach
class ElementSOLID81 : public ElementIsoParamHEXAHEDRON {
public:
ElementSOLID81 () {
intOrder = 2;
type = ElementType::SOLID81;
}
ElementSOLID81 (const ElementSOLID81& from) {
operator=(from);
}
//solving procedures
void pre();
void buildK();
void update();
void make_B_L (uint16 nPoint, math::Mat<6,24> &B); //функция создает линейную матрицу [B]
void make_B_NL (uint16 nPoint, math::Mat<9,24> &B); //функция создает линейную матрицу [Bomega]
void make_S (uint16 nPoint, math::MatSym<9> &B);
void make_Omega (uint16 nPoint, math::Mat<6,9> &B);
//postproc procedures
bool getScalar(double* scalar, scalarQuery code, uint16 gp, const double scale);
bool getVector(math::Vec<3>* vector, vectorQuery code, uint16 gp, const double scale);
bool getTensor(math::MatSym<3>* tensor, tensorQuery code, uint16 gp, const double scale);
// internal element data
//S[M_XX], S[M_XY], S[M_XZ], S[M_YY], S[M_YZ], S[M_ZZ]
std::vector<math::Vec<6> > S; //S[номер т. интегр.][номер напряжения] - напряжения Пиолы-Кирхгоффа
//C[M_XX], C[M_XY], C[M_XZ], C[M_YY], C[M_YZ], C[M_ZZ]
std::vector<math::Vec<6> > C; //C[номер т. интегр.][номер деформ.] - компоненты тензора меры деформации
// O[0]-dU/dx O[1]-dU/dy O[2]-dU/dz O[3]-dV/dx O[4]-dV/dy O[5]-dV/dz O[6]-dW/dx O[7]-dW/dy O[8]-dW/dz
std::vector<math::Vec<9> > O; //S[номер т. интегр.][номер омеги]
template <uint16 dimM, uint16 dimN>
void assemble2(math::MatSym<dimM> &Kuu, math::Mat<dimM,dimM> &Kup, math::Mat<dimN,dimN> &Kpp, math::Vec<dimM> &Fu, math::Vec<dimN> &Fp);
template <uint16 dimM>
void assemble3(math::MatSym<dimM> &Kuu, math::Vec<dimM> &Kup, double Kpp, math::Vec<dimM> &Fu, double Fp);
};
template <uint16 dimM>
void ElementSOLID81::assemble3(math::MatSym<dimM> &Kuu, math::Vec<dimM> &Kup, double Kpp, math::Vec<dimM> &Fu, double Fp) {
const uint16 dim = 3;
assert (getNNodes() * dim == dimM);
double *Kuu_p = Kuu.ptr();
double *Kup_p = Kup.ptr();
double *Fu_p = Fu.ptr();
Dof::dofType dofVec[] = {Dof::UX, Dof::UY, Dof::UZ};
for (uint16 i=0; i < getNNodes(); i++)
for (uint16 di=0; di < dim; di++)
for (uint16 j=i; j < getNNodes(); j++)
for (uint16 dj=0; dj < dim; dj++)
{
if ((i==j) && (dj<di)) continue;
else
{
storage->addValueK(nodes[i],dofVec[di],nodes[j], dofVec[dj], *Kuu_p);
Kuu_p++;
}
}
//upper diagonal process for nodes-el dofs
uint32 elEq = storage->getElementDofEqNumber(getElNum(), Dof::HYDRO_PRESSURE);
for (uint16 i=0; i < getNNodes(); i++) {
for(uint16 di=0; di < dim; di++) {
uint32 rowEq = storage->getNodeDofEqNumber(nodes[i], dofVec[di]);
storage->addValueK(rowEq, elEq, *Kup_p);
Kup_p++;
}
}
//upper diagonal process for el-el dofs
storage->addValueK(elEq, elEq, Kpp);
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
storage->addValueF(nodes[i], dofVec[di], *Fu_p);
Fu_p++;
}
}
storage->addValueF(elEq, Fp);
}
} // namespace nla3d
#include "elements/TETRA0.h"
using namespace std;
namespace nla3d {
ElementTETRA0::ElementTETRA0 () {
type = ElementType::TETRA0;
}
void ElementTETRA0::pre () {
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::UX, Dof::UY, Dof::UZ});
}
}
// here stiffness matrix is built
void ElementTETRA0::buildK() {
Eigen::MatrixXd matS(4,4);
matS.setZero();
matS<< 1. , storage->getNode(getNodeNumber(0)).pos[0] , storage->getNode(getNodeNumber(0)).pos[1] , storage->getNode(getNodeNumber(0)).pos[2] ,
1. , storage->getNode(getNodeNumber(1)).pos[0] , storage->getNode(getNodeNumber(1)).pos[1] , storage->getNode(getNodeNumber(1)).pos[2] ,
1. , storage->getNode(getNodeNumber(2)).pos[0] , storage->getNode(getNodeNumber(2)).pos[1] , storage->getNode(getNodeNumber(2)).pos[2] ,
1. , storage->getNode(getNodeNumber(3)).pos[0] , storage->getNode(getNodeNumber(3)).pos[1] , storage->getNode(getNodeNumber(3)).pos[2];
vol = matS.determinant()/6.;
// Ke will store element stiffness matrix in global coordinates
math::MatSym<12> matKe;
matKe.zero();
// matB is strain matrix
math::Mat<6,12> matB;
matB.zero();
// matC is 3d elastic matrix
math::MatSym<6> matC;
matC.zero();
// fill here matC
makeC(matC);
// fill here matB
makeB(matB);
math::matBTDBprod(matB, matC, vol, matKe);
// start assemble procedure. Here we should provide element stiffness matrix and an order of
// nodal DoFs in the matrix.
math::Vec<12> Fe;
Fe.zero();
math::Mat<12,6> matBT;
matBT = matB.transpose();
math::Mat<12,6> matBTC;
math::Mat<6,6> matCt = matC.toMat();
matBTC = matBT*matCt;
if (T > 0){
//temp node forces
math::Vec<6> tStrains = {alpha*T,alpha*T,alpha*T,0.,0.,0.};
math::matBVprod(matBTC, tStrains, (-1)*vol, Fe);
}
math::matBVprod(matBTC, strains, (-1)*vol, Fe);
assembleK(matKe, Fe,{Dof::UX, Dof::UY, Dof::UZ});
}
// after solution it's handy to calculate stresses, strains and other stuff in elements.
void ElementTETRA0::update () {
// matB is strain matrix
math::Mat<6,12> matB;
matB.zero();
// matC is 3d elastic matrix
math::MatSym<6> matC;
matC.zero();
// fill here matC
makeC(matC);
// fill here matB
makeB(matB);
// get nodal solutions from storage
math::Vec<12> U;
for (uint16 i = 0; i < getNNodes(); i++) {
U[i*3 + 0] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UX);
U[i*3 + 1] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UY);
U[i*3 + 2] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UZ);
}
// restore strains
math::Vec<6> strains0 = strains;
strains.zero();
math::matBVprod(matB, U, -1.0, strains);
//calc term strains
if (T > 0.){
math::Vec<6> tStrains = {alpha*T,alpha*T,alpha*T,0.,0.,0.};
strains = strains-tStrains;
}
//strains = strains - strains0;
stress.zero();
math::matBVprod(matC, strains, 1.0, stress);
}
void ElementTETRA0::makeB(math::Mat<6,12> &B)
{
double *B_L = B.ptr();
double b[4], c[4], d[4];
//Eigen::MatrixXd mb(3,3), mc(3,3), md(3,3);
int x=0, y = 1, z=2;
double x12 = storage->getNode(getNodeNumber(0)).pos[x] - storage->getNode(getNodeNumber(1)).pos[x];
double x13 = storage->getNode(getNodeNumber(0)).pos[x] - storage->getNode(getNodeNumber(2)).pos[x];
double x14 = storage->getNode(getNodeNumber(0)).pos[x] - storage->getNode(getNodeNumber(3)).pos[x];
double x23 = storage->getNode(getNodeNumber(1)).pos[x] - storage->getNode(getNodeNumber(2)).pos[x];
double x24 = storage->getNode(getNodeNumber(1)).pos[x] - storage->getNode(getNodeNumber(3)).pos[x];
double x34 = storage->getNode(getNodeNumber(2)).pos[x] - storage->getNode(getNodeNumber(3)).pos[x];
double x21 = -1.*x12;
double x31 = -1.*x13;
double x32 = -1.*x23;
double x42 = -1.*x24;
double x43 = -1.*x34;
double y12 = storage->getNode(getNodeNumber(0)).pos[y] - storage->getNode(getNodeNumber(1)).pos[y];
double y13 = storage->getNode(getNodeNumber(0)).pos[y] - storage->getNode(getNodeNumber(2)).pos[y];
double y14 = storage->getNode(getNodeNumber(0)).pos[y] - storage->getNode(getNodeNumber(3)).pos[y];
double y23 = storage->getNode(getNodeNumber(1)).pos[y] - storage->getNode(getNodeNumber(2)).pos[y];
double y24 = storage->getNode(getNodeNumber(1)).pos[y] - storage->getNode(getNodeNumber(3)).pos[y];
double y34 = storage->getNode(getNodeNumber(2)).pos[y] - storage->getNode(getNodeNumber(3)).pos[y];
double y21 = -1.*y12;
double y31 = -1.*y13;
double y32 = -1.*y23;
double y42 = -1.*y24;
double y43 = -1.*y34;
double z12 = storage->getNode(getNodeNumber(0)).pos[z] - storage->getNode(getNodeNumber(1)).pos[z];
double z13 = storage->getNode(getNodeNumber(0)).pos[z] - storage->getNode(getNodeNumber(2)).pos[z];
double z14 = storage->getNode(getNodeNumber(0)).pos[z] - storage->getNode(getNodeNumber(3)).pos[z];
double z23 = storage->getNode(getNodeNumber(1)).pos[z] - storage->getNode(getNodeNumber(2)).pos[z];
double z24 = storage->getNode(getNodeNumber(1)).pos[z] - storage->getNode(getNodeNumber(3)).pos[z];
double z34 = storage->getNode(getNodeNumber(2)).pos[z] - storage->getNode(getNodeNumber(3)).pos[z];
double z21 = -1.*z12;
double z31 = -1.*z13;
double z32 = -1.*z23;
double z42 = -1.*z24;
double z43 = -1.*z34;
b[0] = y42*z32 - y32*z42;
b[1] = y31*z43 - y34*z13;
b[2] = y24*z14 - y14*z24;
b[3] = y13*z21 - y12*z31;
c[0] = x32*z42-x42*z32;
c[1] = x43*z31-x13*z34;
c[2] = x14*z24-x24*z14;
c[3] = x21*z13-x31*z12;
d[0] = x42*y32-x32*y42;
d[1] = x31*y43-x34*y13;
d[2] = x24*y14-x14*y24;
d[3] = x13*y21-x12*y31;
const double A = -1./6./vol;
for (int i = 0; i < 4; i++){
B_L[0 + 3*i] = b[i]*A;
B_L[13 + 3*i] = c[i]*A;
B_L[26 + 3*i] = d[i]*A;
B_L[36 + 3*i] = c[i]*A;
B_L[37 + 3*i] = b[i]*A;
B_L[49 + 3*i] = d[i]*A;
B_L[50 + 3*i] = c[i]*A;
B_L[60 + 3*i] = d[i]*A;
B_L[62 + 3*i] = b[i]*A;
}
}
void ElementTETRA0::makeC (math::MatSym<6> &C) {
const double A = E/((1.+my)*(1.-2.*my));
C.comp(0,0) = (1.-my)*A;
C.comp(0,1) = my*A;
C.comp(0,2) = my*A;
C.comp(1,1) = (1.-my)*A;
C.comp(1,2) = my*A;
C.comp(2,2) = (1.-my)*A;
C.comp(3,3) = (1./2.-my)*A;
C.comp(4,4) = (1./2.-my)*A;
C.comp(5,5) = (1./2.-my)*A;
}
int ElementTETRA0::permute(int i){
if (i > 3)
return i -4;
else
return i;
}
bool ElementTETRA0::getScalar(double* scalar, scalarQuery query, uint16 gp, const double scale) {
if (query == scalarQuery::VOL){
*scalar += vol;
return true;
}
return false;
}
bool ElementTETRA0::getTensor(math::MatSym<3>* tensor, tensorQuery query, uint16 gp, const double scale) {
if (query == tensorQuery::C){
tensor->comp(0,0) += strains[0];
tensor->comp(1,1) += strains[1];
tensor->comp(2,2) += strains[2];
tensor->comp(0,1) += strains[3];
tensor->comp(1,2) += strains[4];
tensor->comp(0,2) += strains[5];
return true;
}
if (query == tensorQuery::E){
tensor->comp(0,0) += stress[0];
tensor->comp(1,1) += stress[1];
tensor->comp(2,2) += stress[2];
tensor->comp(0,1) += stress[3];
tensor->comp(1,2) += stress[4];
tensor->comp(0,2) += stress[5];
return true;
}
return false;
}
} //namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
namespace nla3d {
class ElementTETRA0 : public ElementTETRA {
public:
// ElementTETRA () defines number of nodes in the element, number of dimensions (2D or 3D
// elements). It creates an array for storing global nodes numbers. And also, it registers which
// DoFs it is going to use (Dof::UX, Dof::UY, Dof::UZ in this case).
ElementTETRA0 ();
// pre() - functions that is called just before the solution procedures. pre() should register
// which element DoFs and nodal DoFs will be incorporated in the element stiffness matrix. On this
// step element also need to initialize any variables that it is going to use in solution process
// (strains and stresses in integration points in finite deformations analysis, for example).
// ElementTETRA::pre () registers Dof::UX, Dof::UY, Dof::UZ as DoFs in every node.
void pre();
// buildK() - a central point in element class. Here the element should build element stiffness
// matrix (actually, tangential matrix, as soon as we make non-linear-ready elements). The element
// also responsible for assembling its local stiffness matrix into global system of equations
// matrix. Fro this purpose here is a special procedure in base class Element::assemble(..). Also,
// the element should assemble right hand side (rhs) of equations related to this element
// (especially used in non-linear analysis).
void buildK();
// update() - the function updates internal state of the element based on found solution of
// global equation system. For example, here you can calculate stresses in the element which depends
// on found DoFs solution.
void update();
void makeB (math::Mat<6,12> &B);
void makeC (math::MatSym<6> &C);
// Elastic module
double E = 0.0;
// Poissons coef.
double my = 0.0;
//tensile strength
//stretching
double pE = 0.0;
//compress
double pC = 0.0;
//shear
double pSh = 0.0;
//heat
double alpha = 0.0;
double T = 0.0;
// stresses in the element (calculated after the solving of the global equation system in
// update() function.
//stress[M_XX], stress[M_YY], stress[M_ZZ], stress[M_XY], stress[M_YZ], stress[M_XZ]
math::Vec<6> stress; // Cauchy stresses
//strains[M_XX], strains[M_YY], strains[M_ZZ], strains[M_XY], strains[M_YZ], strains[M_XZ]
math::Vec<6> strains;
double vol;
//postproc procedures
bool getScalar(double* scalar, scalarQuery code, uint16 gp, const double scale);
bool getTensor(math::MatSym<3>* tensor, tensorQuery code, uint16 gp, const double scale);
private:
int permute(int i);
};
} //namespace nla3d
#include "elements/TETRA1.h"
using namespace std;
namespace nla3d {
ElementTETRA1::ElementTETRA1 () {
type = ElementType::TETRA1;
}
void ElementTETRA1::pre () {
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::TEMP});
}
}
// here stiffness matrix is built
void ElementTETRA1::buildK() {
Eigen::MatrixXd matS(4,4);
matS.setZero();
matS<< 1. , storage->getNode(getNodeNumber(0)).pos[0] , storage->getNode(getNodeNumber(0)).pos[1] , storage->getNode(getNodeNumber(0)).pos[2] ,
1. , storage->getNode(getNodeNumber(1)).pos[0] , storage->getNode(getNodeNumber(1)).pos[1] , storage->getNode(getNodeNumber(1)).pos[2] ,
1. , storage->getNode(getNodeNumber(2)).pos[0] , storage->getNode(getNodeNumber(2)).pos[1] , storage->getNode(getNodeNumber(2)).pos[2] ,
1. , storage->getNode(getNodeNumber(3)).pos[0] , storage->getNode(getNodeNumber(3)).pos[1] , storage->getNode(getNodeNumber(3)).pos[2];
vol = matS.determinant()/6.;
// Ke will store element stiffness matrix in global coordinates
math::MatSym<4> matKe;
matKe.zero();
// matB is strain matrix
math::Mat<3,4> matB;
matB.zero();
// matC is 3d elastic matrix
math::MatSym<3> matC;
matC.zero();
// fill here matC
makeC(matC);
// fill here matB
makeB(matB);
math::matBTDBprod(matB, matC, vol, matKe);
// start assemble procedure. Here we should provide element stiffness matrix and an order of
// nodal DoFs in the matrix.
assembleK(matKe, {Dof::TEMP});
}
// after solution it's handy to calculate stresses, strains and other stuff in elements.
void ElementTETRA1::update () {
// matB is strain matrix
math::Mat<3,4> matB;
matB.zero();
// matC is 3d elastic matrix
math::MatSym<3> matC;
matC.zero();
// fill here matC
makeC(matC);
// fill here matB
makeB(matB);
// get nodal solutions from storage
math::Vec<4> U;
for (uint16 i = 0; i < getNNodes(); i++) {
U[i] = storage->getNodeDofSolution(getNodeNumber(i), Dof::TEMP);
}
flux.zero();
gradT.zero();
math::matBVprod(matB, U, 1., gradT);
// restore fluxes
flux = gradT*k;
}
void ElementTETRA1::makeB(math::Mat<3,4> &B)
{
double *B_L = B.ptr();
double b[4], c[4], d[4];
//Eigen::MatrixXd mb(3,3), mc(3,3), md(3,3);
int x=0, y = 1, z=2;
double x12 = storage->getNode(getNodeNumber(0)).pos[x] - storage->getNode(getNodeNumber(1)).pos[x];
double x13 = storage->getNode(getNodeNumber(0)).pos[x] - storage->getNode(getNodeNumber(2)).pos[x];
double x14 = storage->getNode(getNodeNumber(0)).pos[x] - storage->getNode(getNodeNumber(3)).pos[x];
double x23 = storage->getNode(getNodeNumber(1)).pos[x] - storage->getNode(getNodeNumber(2)).pos[x];
double x24 = storage->getNode(getNodeNumber(1)).pos[x] - storage->getNode(getNodeNumber(3)).pos[x];
double x34 = storage->getNode(getNodeNumber(2)).pos[x] - storage->getNode(getNodeNumber(3)).pos[x];
double x21 = -1.*x12;
double x31 = -1.*x13;
double x32 = -1.*x23;
double x42 = -1.*x24;
double x43 = -1.*x34;
double y12 = storage->getNode(getNodeNumber(0)).pos[y] - storage->getNode(getNodeNumber(1)).pos[y];
double y13 = storage->getNode(getNodeNumber(0)).pos[y] - storage->getNode(getNodeNumber(2)).pos[y];
double y14 = storage->getNode(getNodeNumber(0)).pos[y] - storage->getNode(getNodeNumber(3)).pos[y];
double y23 = storage->getNode(getNodeNumber(1)).pos[y] - storage->getNode(getNodeNumber(2)).pos[y];
double y24 = storage->getNode(getNodeNumber(1)).pos[y] - storage->getNode(getNodeNumber(3)).pos[y];
double y34 = storage->getNode(getNodeNumber(2)).pos[y] - storage->getNode(getNodeNumber(3)).pos[y];
double y21 = -1.*y12;
double y31 = -1.*y13;
double y32 = -1.*y23;
double y42 = -1.*y24;
double y43 = -1.*y34;
double z12 = storage->getNode(getNodeNumber(0)).pos[z] - storage->getNode(getNodeNumber(1)).pos[z];
double z13 = storage->getNode(getNodeNumber(0)).pos[z] - storage->getNode(getNodeNumber(2)).pos[z];
double z14 = storage->getNode(getNodeNumber(0)).pos[z] - storage->getNode(getNodeNumber(3)).pos[z];
double z23 = storage->getNode(getNodeNumber(1)).pos[z] - storage->getNode(getNodeNumber(2)).pos[z];
double z24 = storage->getNode(getNodeNumber(1)).pos[z] - storage->getNode(getNodeNumber(3)).pos[z];
double z34 = storage->getNode(getNodeNumber(2)).pos[z] - storage->getNode(getNodeNumber(3)).pos[z];
double z21 = -1.*z12;
double z31 = -1.*z13;
double z32 = -1.*z23;
double z42 = -1.*z24;
double z43 = -1.*z34;
b[0] = y42*z32 - y32*z42;
b[1] = y31*z43 - y34*z13;
b[2] = y24*z14 - y14*z24;
b[3] = y13*z21 - y12*z31;
c[0] = x32*z42-x42*z32;
c[1] = x43*z31-x13*z34;
c[2] = x14*z24-x24*z14;
c[3] = x21*z13-x31*z12;
d[0] = x42*y32-x32*y42;
d[1] = x31*y43-x34*y13;
d[2] = x24*y14-x14*y24;
d[3] = x13*y21-x12*y31;
const double A = -1./6./vol;
B_L[0] = b[0]*A;
B_L[1] = b[1]*A;
B_L[2] = b[2]*A;
B_L[3] = b[3]*A;
B_L[4] = c[0]*A;
B_L[5] = c[1]*A;
B_L[6] = c[2]*A;
B_L[7] = c[3]*A;
B_L[8] = d[0]*A;
B_L[9] = d[1]*A;
B_L[10] = d[2]*A;
B_L[11] = d[3]*A;
}
void ElementTETRA1::makeC (math::MatSym<3> &C) {
C.comp(0,0) = (-1)*k;
C.comp(1,1) = (-1)*k;
C.comp(2,2) = (-1)*k;
}
bool ElementTETRA1::getScalar(double* scalar, scalarQuery query, uint16 gp, const double scale) {
if (query == scalarQuery::VOL){
*scalar += vol;
return true;
}
return false;
}
bool ElementTETRA1::getVector(math::Vec<3>* vector, vectorQuery query, uint16 gp, const double scale) {
switch (query) {
case vectorQuery::FLUX:
(*vector)[0] += flux[0];
(*vector)[1] += flux[1];
(*vector)[2] += flux[2];
return true;
}
return false;
}
} //namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
namespace nla3d {
class ElementTETRA1 : public ElementTETRA {
public:
// ElementTETRA () defines number of nodes in the element, number of dimensions (2D or 3D
// elements). It creates an array for storing global nodes numbers. And also, it registers which
// DoFs it is going to use (Dof::TEMP in this case).
ElementTETRA1 ();
// pre() - functions that is called just before the solution procedures. pre() should register
// which element DoFs and nodal DoFs will be incorporated in the element stiffness matrix. On this
// step element also need to initialize any variables that it is going to use in solution process
// (strains and stresses in integration points in finite deformations analysis, for example).
// ElementTETRA::pre () register Dof::TEMP as DoFs in every node.
void pre();
// buildK() - a central point in element class. Here the element should build element stiffness
// matrix (actually, tangential matrix, as soon as we make non-linear-ready elements). The element
// also responsible for assembling its local stiffness matrix into global system of equations
// matrix. Fro this purpose here is a special procedure in base class Element::assemble(..). Also,
// the element should assemble right hand side (rhs) of equations related to this element
// (especially used in non-linear analysis).
void buildK();
// update() - the function updates internal state of the element based on found solution of
// global equation system. For example, here you can calculate stresses in the element which depends
// on found DoFs solution.
void update();
void makeB (math::Mat<3,4> &B);
void makeC (math::MatSym<3> &C);
// conductivity coef ( W/(K m), for example)
double k = 0.0;
//extension coef
double alpha = 0.0;
// update() function.
// temperature gradient
math::Vec<3> gradT; //
//flux[X], flux[Y], flux[Z]
math::Vec<3> flux; //
double vol;
//postproc procedures
bool getScalar(double* scalar, scalarQuery code, uint16 gp, const double scale);
bool getVector(math::Vec<3>* vector, vectorQuery code, uint16 gp, const double scale);
};
} //namespace nla3d
#include "elements/TRIANGLE4.h"
#include <iostream>
using namespace std;
namespace nla3d {
ElementTRIANGLE4::ElementTRIANGLE4 () {
type = ElementType::TRIANGLE4;
state = PlaneState::Stress;
}
void ElementTRIANGLE4::pre () {
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::UX, Dof::UY});
}
}
// here stiffness matrix is built
void ElementTRIANGLE4::buildK() {
// Ke will store element stiffness matrix in global coordinates
math::MatSym<6> matKe;
matKe.zero();
// matB is strain matrix
math::Mat<3,6> matB;
matB.zero();
// matC is 3d elastic matrix
math::MatSym<3> matC;
matC.zero();
//only for area
Eigen::MatrixXd matS(3,3);
matS.setZero();
matS << 1. , storage->getNode(getNodeNumber(0)).pos[0] , storage->getNode(getNodeNumber(0)).pos[1] ,
1. , storage->getNode(getNodeNumber(1)).pos[0] , storage->getNode(getNodeNumber(1)).pos[1] ,
1. , storage->getNode(getNodeNumber(2)).pos[0] , storage->getNode(getNodeNumber(2)).pos[1];
area = matS.determinant()/2.;
// fill here matC
makeC(matC);
// fill here matB
makeB(matB);
math::matBTDBprod(matB, matC, area, matKe);
// start assemble procedure. Here we should provide element stiffness matrix and an order of
// nodal DoFs in the matrix.
assembleK(matKe, {Dof::UX, Dof::UY});
}
// after solution it's handy to calculate stresses, strains and other stuff in elements.
void ElementTRIANGLE4::update () {
// matB is strain matrix
math::Mat<3,6> matB;
matB.zero();
// matC is 3d elastic matrix
math::MatSym<3> matC;
matC.zero();
// fill here matC
makeC(matC);
// fill here matB
makeB(matB);
// get nodal solutions from storage
math::Vec<6> U;
for (uint16 i = 0; i < getNNodes(); i++) {
U[i*2 + 0] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UX);
U[i*2 + 1] = storage->getNodeDofSolution(getNodeNumber(i), Dof::UY);
}
// restore strains
math::matBVprod(matB, U, 1.0, strains);
// restore stresses
math::matBVprod(matC, strains, 1.0, stress);
}
void ElementTRIANGLE4::makeB(math::Mat<3,6> &B)
{
double *B_L = B.ptr();
double b[3], c[3];
b[0] = storage->getNode(getNodeNumber(1)).pos[1] - storage->getNode(getNodeNumber(2)).pos[1];
b[1] = storage->getNode(getNodeNumber(2)).pos[1] - storage->getNode(getNodeNumber(0)).pos[1];
b[2] = storage->getNode(getNodeNumber(0)).pos[1] - storage->getNode(getNodeNumber(1)).pos[1];
c[0] = storage->getNode(getNodeNumber(2)).pos[0] - storage->getNode(getNodeNumber(1)).pos[0];
c[1] = storage->getNode(getNodeNumber(0)).pos[0] - storage->getNode(getNodeNumber(2)).pos[0];
c[2] = storage->getNode(getNodeNumber(1)).pos[0] - storage->getNode(getNodeNumber(0)).pos[0];
const double A = 1./2./area;
B_L[0*6 + 0] = b[0]*A;
B_L[0*6 + 2] = b[1]*A;
B_L[0*6 + 4] = b[2]*A;
B_L[1*6 + 1] = c[0]*A;
B_L[1*6 + 3] = c[1]*A;
B_L[1*6 + 5] = c[2]*A;
B_L[2*6 + 0] = c[0]*A;
B_L[2*6 + 1] = b[0]*A;
B_L[2*6 + 2] = c[1]*A;
B_L[2*6 + 3] = b[1]*A;
B_L[2*6 + 4] = c[2]*A;
B_L[2*6 + 5] = b[2]*A;
}
void ElementTRIANGLE4::makeC (math::MatSym<3> &C) {
//Plane deformed state
if (state == PlaneState::Strain){
const double A = E*(1.-my)/((1.+my)*(1.-2.*my));
C.comp(0,0) = 1.*A;
C.comp(0,1) = my/(1-my)*A;
C.comp(1,1) = 1.*A;
C.comp(2,2) = (1.-2.*my)/(2.*(1.-my))*A;
}
//Plane stress state
if (state == PlaneState::Stress){
const double A = E*(1.-my*my);
C.comp(0,0) = 1.*A;
C.comp(0,1) = my*A;
C.comp(1,1) = 1.*A;
C.comp(2,2) = (1.-my)/2.*A;
}
}
} //namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
namespace nla3d {
enum PlaneState{
Strain,
Stress
};
class ElementTRIANGLE4 : public ElementTRIANGLE {
public:
// ElementTRIANGLE4 () defines number of nodes in the element, number of dimensions (2D or 3D
// elements). It creates an array for storing global nodes numbers. And also, it registers which
// DoFs it is going to use (Dof::UX, Dof::UY, Dof::UZ in this case).
ElementTRIANGLE4 ();
// pre() - functions that is called just before the solution procedures. pre() should register
// which element DoFs and nodal DoFs will be incorporated in the element stiffness matrix. On this
// step element alsoo need to initialize any variables that it is going to use in solution process
// (strains and stresses in integration points in finite deformations analysis, for example).
// ElementTRIANGLE4::pre () registers Dof::UX, Dof::UY, Dof::UZ as DoFs in every node.
void pre();
// buildK() - a central point in element class. Here the element should build element stiffness
// matrix (actually, tangential matrix, as soon as we make non-linear-ready elements). The element
// also responsible for assembling its local stiffness matrix into global system of equations
// matrix. Fro this purpose here is a special procedure in base class Element::assemble(..). Also,
// the element should assemble right hand side (rhs) of equations related to this element
// (especially used in non-linear analysis).
//
void buildK();
// update() - the function updates internal state of the element based on found solution of
// global equation system. For example, here you can calculate stresses in the element which depends
// on found DoFs solution.
void update();
void makeB (math::Mat<3,6> &B);
void makeC (math::MatSym<3> &C);
// Elastic module
double E = 0.0;
// Poissons coef.
double my = 0.0;
// stresses in the element (calculated after the solving of the global equation system in
// update() function.
//stress[M_X], stress[M_X], stress[M_XY]
math::Vec<3> stress; // Cauchy stresses
//strains[M_X], strains[M_Y], strains[M_XY]
math::Vec<3> strains;
PlaneState state;
private:
double area;
};
} //namespace nla3d
#include "elements/TRUSS3.h"
namespace nla3d {
ElementTRUSS3::ElementTRUSS3 () {
type = ElementType::TRUSS3;
}
void ElementTRUSS3::pre() {
for (uint16 i = 0; i < getNNodes(); i++) {
storage->addNodeDof(getNodeNumber(i), {Dof::UX, Dof::UY, Dof::UZ});
}
}
// here stiffness matrix is building with Eignen's library matrix routines
void ElementTRUSS3::buildK() {
// Ke will store element stiffness matrix
Eigen::MatrixXd Ke(6,6);
// T for transformation matrix (to map governing equations from an element local coordinate system
// into global coordinate system
Eigen::MatrixXd T(2,6);
// K stores element stiffness matrix in a local coordinate system
Eigen::MatrixXd K(2,2);
Ke.setZero();
T.setZero();
K.setZero();
// here we use pointer to FEStorage class where all FE information is stored (from node and
// element tables to solution results). getNode(..).pos used to obtain an class instance for
// particular node an get its position which is Vec<3> data type.
math::Vec<3> deltaPos = storage->getNode(getNodeNumber(1)).pos - storage->getNode(getNodeNumber(0)).pos;
// as long as we need 1.0/length instead of length itself it's useful to store this value in the
// variable inv_length. Such kind of code optimization is quite useful in massively computational
// expensive procedures.
double inv_length = 1.0 / deltaPos.length();
// filling transformation matrix T (see reference theretical material to get it clear. The link to
// theoretical material could be found in TRUSS3.h file). Here Eigen library matrix and its
// operator(). For more information read Eigen manual.
T(0,0) = deltaPos[0] * inv_length;
T(0,1) = deltaPos[1] * inv_length;
T(0,2) = deltaPos[2] * inv_length;
T(1,3) = T(0,0);
T(1,4) = T(0,1);
T(1,5) = T(0,2);
// This is just another way in Eigen to initialize {{1.0, -1.0}, {-1.0, 1.0}} matrix.
K << 1.0, -1.0,
-1.0, 1.0;
K *= A*E*inv_length;
// Transform local stiffness matrix K into the global coordinate sytem (matrix Ke). As far as Ke
// is symmetric it's ok to calculate just upper triangular part of it. For this reason here
// Ke.triangularView<Eigen::Upper> is used.
Ke.triangularView<Eigen::Upper>() = T.transpose() * K * T;
// start assemble procedure. Here we should provide element stiffness matrix and an order of
// nodal DoFs in the matrix.
assembleK(Ke, {Dof::UX, Dof::UY, Dof::UZ});
}
// after solution it's handy to calculate stresses, strains and other stuff in elements. In this
// case a truss stress will be restored
void ElementTRUSS3::update() {
// T for transformation matrix
Eigen::MatrixXd T(2,6);
// B for strain matrix
Eigen::MatrixXd B(1,2);
T.setZero();
B.setZero();
math::Vec<3> deltaPos = storage->getNode(getNodeNumber(1)).pos - storage->getNode(getNodeNumber(0)).pos;
double inv_length = 1.0 / deltaPos.length();
T(0,0) = deltaPos[0] * inv_length;
T(0,1) = deltaPos[1] * inv_length;
T(0,2) = deltaPos[2] * inv_length;
T(1,3) = T(0,0);
T(1,4) = T(0,1);
T(1,5) = T(0,2);
B << -inv_length, inv_length;
// read solution results from FEStorage. Here we need to fill nodal DoFs values into U vector.
Eigen::VectorXd U(6);
for (uint16 i = 0; i < getNNodes(); i++) {
U(i*3 + 0) = storage->getNodeDofSolution(getNodeNumber(i), Dof::UX);
U(i*3 + 1) = storage->getNodeDofSolution(getNodeNumber(i), Dof::UY);
U(i*3 + 2) = storage->getNodeDofSolution(getNodeNumber(i), Dof::UZ);
}
// The formula to calculate truss stresses (see theory reference):
// S = E * B * T * D
//1x1=1x1 * 1x2 * 2x6 * 6x1
S = E * (B * T * U)(0,0);
}
} //namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "elements/element.h"
namespace nla3d {
// ElementTRUSS3 (TRUSS3) is a simplest realisation of 3D truss finite element (FE). Its primiral goal is to
// show how to implement new FE into nla3d library. One can find a lot of information about how to
// build 3D truss element stiffnes matrix in the internet. The author was looking at the material on
// this website:
// http://what-when-how.com/the-finite-element-method/fem-for-trusses-finite-element-method-part-1/
// New FE formulation is encapsulated into a class derived from base Element class. FEs have a lot
// of freedom in nla3d. They are responsible for registration degrees of freedom (DoFs) that they
// will use (see pre() funciton). They fully responsible how to work with materials instances. The
// class ElelementTRUSS3 doesn't use material information at all. Instead of this it uses class
// members A and E as truss properties. The author thinks that FE and material can't be fully
// divided into different parts, as soon as FE stiffness matrix formulation highly depends on which
// material we use (elastic, anisotropic, plastic, hyperelastic, etc). Then elements in nla3d have a
// right to not use Material class (see src/lib/materials/material.h) at all.
class ElementTRUSS3 : public ElementLINE {
public:
// Element in nla3d have to provide several obligatore functions in order to make it possible to use
// it:
// 1. element class constructor to define number of nodes, dimension, and register which DoF types the element
// are going to use.
// ElementTRUSS3 () defines number of nodes in the element, number of dimensions (2D or 3D
// elements). It creates an array for storing global nodes numbers. And also, it registers which
// DoFs it is going to use (Dof::UX, Dof::UY, Dof::UZ in this case).
ElementTRUSS3 ();
// 2. pre() - functions that is called just before the solution procedures. pre() should register
// which element DoFs and nodal DoFs will be incorporated in the element stiffness matrix. On this
// step element alsoo need to initialize any variables that it is going to use in solution process
// (strains and stresses in integration points in finite deformations analysis, for example).
// ElementTRUSS3::pre () registers Dof::UX, Dof::UY, Dof::UZ as DoFs in every node.
void pre();
// 3. buildK() - a central point in element class. Here the element should build element stiffness
// matrix (actually, tangential matrix, as soon as we make non-linear-ready elements). The element
// also responsible for assembling its local stiffness matrix into global system of equations
// matrix. Fro this purpose here is a special procedure in base class Element::assemble(..). Also,
// the element should assemble right hand side (rhs) of equations related to this element
// (especially used in non-linear analysis).
// see ElementTRUSS3::buildK() body for more comments on the particular realisation.
void buildK();
// 4. update() - the function updates internal state of the element based on found solution of
// global equation system. For example, here you can calculate stresses in the element which depends
// on found DoFs solution.
// see ElementTRUSS3::update() body for the insight of the particular realization.
void update();
// stiffness module
double E = 0.0;
// cross-section area
double A = 0.0;
// normal stress in the truss (calculated after the solving of the global equation system in
// update() function.
double S;
};
} //namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "elements/element.h"
namespace nla3d {
Element::Element () {
}
Element::~Element() {
if (nodes) {
delete[] nodes;
nodes = nullptr;
}
}
void Element::print (std::ostream& out) {
out << "E " << getElNum() << ":";
for (uint16 i = 0; i < getNNodes(); i++) {
out << "\t" << getNodeNumber(i);
}
}
Element& Element::operator= (const Element& from)
{
assert(nodes && from.nodes);
memcpy(nodes, from.nodes, sizeof(uint32)*getNNodes());
return *this;
}
void Element::assembleK(Eigen::Ref<Eigen::MatrixXd> Ke,
std::initializer_list<Dof::dofType> _nodeDofs) {
assert (nodes != NULL);
assert (Ke.rows() == Ke.cols());
std::vector<Dof::dofType> nodeDof(_nodeDofs);
uint16 dim = static_cast<uint16> (_nodeDofs.size());
assert (getNNodes() * dim == Ke.rows());
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
for (uint16 j=i; j < getNNodes(); j++) {
for (uint16 dj=0; dj < dim; dj++) {
if ((i==j) && (dj<di)) {
continue;
} else {
storage->addValueK(nodes[i], nodeDof[di], nodes[j], nodeDof[dj],
Ke.selfadjointView<Eigen::Upper>()(i*dim+di, j*dim +dj));
}
}
}
}
}
}
void Element::buildC() {
LOG(FATAL) << "buildC is not implemented";
}
void Element::buildM() {
LOG(FATAL) << "buildM is not implemented";
}
bool Element::getScalar(double* scalar, scalarQuery code, uint16 gp, const double scale) {
// TODO: check that LOG_N_TIMES macro work correctly inside of virtual functions
LOG_N_TIMES(10, WARNING) << "getScalar function is not implemented";
return false;
}
bool Element::getVector(math::Vec<3>* vector, vectorQuery code, uint16 gp, const double scale) {
LOG_N_TIMES(10, WARNING) << "getVector function is not implemented";
return false;
}
bool Element::getTensor(math::MatSym<3>* tensor, tensorQuery code, uint16 gp, const double scale) {
LOG_N_TIMES(10, WARNING) << "getTensor function is not implemented";
return false;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
#include <vector>
#include <math.h>
#include <initializer_list>
#include <Eigen/Dense>
#include "query.h"
#include "Node.h"
#include "math/Vec.h"
#include "math/Mat.h"
namespace nla3d {
//pre-defines
class Material;
class FEStorage;
// Element shapes are taken from vtk-file-formats.pdf
enum ElementShape {
VERTEX = 0,
LINE,
TRIANGLE,
QUAD,
TETRA,
HEXAHEDRON,
WEDGE,
PYRAMID,
QUADRARIC_EDGE,
QUADRATIC_TRIANGLE,
QUADRATIC_QUAD,
QUADRATIC_TETRA,
QUADRARIC_HEXAHEDRON,
UNDEFINED
};
// number of dimensions in shape
static const uint16 _shape_dim[] = {
0, // VERTEX
1, // LINE
2, // TRIANGLE
2, // QUAD
3, // TETRA
3, // HEXAHEDRON
3, // WEDGE
3, // PYRAMID
2, // QUADRARIC_EDGE
2, // QUADRATIC_TRIANGLE
2, // QUADRATIC_QUAD
3, // QUADRATIC_TETRA
3 // QUADRARIC_HEXAHEDRON
};
// number of nodes in shape
static const uint16 _shape_nnodes[] = {
1, // VERTEX
2, // LINE
3, // TRIANGLE
4, // QUAD
4, // TETRA
8, // HEXAHEDRON
6, // WEDGE
5, // PYRAMID
3, // QUADRARIC_EDGE
6, // QUADRATIC_TRIANGLE
8, // QUADRATIC_QUAD
10, // QUADRATIC_TETRA
20 // QUADRARIC_HEXAHEDRON
};
// Element base class
// All FE should be derived from that class. The class provide interface for building stiffness,
// damping and inertia matrices (methods buildK(), buildC(), buildM()), for get element results
// (methods getScalar(...), getVector(...), getTensor(...)), for update element state after solution
// iteration (method update()).
class Element {
public:
Element ();
virtual ~Element();
// get element number, as it is stored in FEStorage. Numbers start from 1.
uint32 getElNum();
// return number of nodes for the element
uint16 getNNodes();
// return number of dimensions (0D, 1D, 2D, 3D) occupied by element shape.
uint16 getDim();
// return element shape
ElementShape getShape();
// return element type
ElementType getType();
// return node number (as it stored in FEStorage) of the i-th node in the element
uint32& getNodeNumber (uint16 num);
// return the FEStorage to which element belongs
FEStorage& getStorage();
// return order of integration scheme used in the particular element for integration over volume
uint16 getIntegrationOrder();
// set the integration order for the element
void setIntegrationOrder(uint16 _nint); // нельзя вызывать после выполнения функции pre() (начало решения)
// heart of the element class
// TODO: comment massively here
virtual void pre()=0;
virtual void buildK()=0;
virtual void buildC();
virtual void buildM();
virtual void update()=0;
// The methods below are getters to receive solution information related to elements (like
// stresses, strains, volume and so on). The first argument is a pointer on return value. Please
// note, that result of the function times scale will be added to the pointer's value (*scalar
// += result * scale, in particular case). query is a value from enum shows with particular
// result should be returned. gp is a number of Gaussian point, if gp == GP_MEAN then the
// result will be averaged over the element based on current integration scheme.
// The methods return true if the query is relevant for the element and false if the element
// can't return asked query code.
virtual bool getScalar(double* scalar, scalarQuery query,
uint16 gp = GP_MEAN, const double scale = 1.0);
virtual bool getVector(math::Vec<3>* vector, vectorQuery query,
uint16 gp = GP_MEAN, const double scale = 1.0);
virtual bool getTensor(math::MatSym<3>* tensor, tensorQuery query,
uint16 gp = GP_MEAN, const double scale = 1.0);
Element& operator= (const Element& from);
//in-out operation:
void print (std::ostream& out);
// some general purpose assemble procedures. Particular element realization could have it own
// assembly procedure.
template <uint16 dimM>
void assembleK(math::MatSym<dimM> &Ke, std::initializer_list<Dof::dofType> _nodeDofs);
template <uint16 dimM>
void assembleC(math::MatSym<dimM> &Ce, std::initializer_list<Dof::dofType> _nodeDofs);
template <uint16 dimM>
void assembleM(math::MatSym<dimM> &Me, std::initializer_list<Dof::dofType> _nodeDofs);
template <uint16 dimM>
void assembleK(math::MatSym<dimM> &Ke, math::Vec<dimM> &Fe,
std::initializer_list<Dof::dofType> _nodeDofs);
void assembleK(Eigen::Ref<Eigen::MatrixXd> Ke, std::initializer_list<Dof::dofType> _nodeDofs);
friend class FEStorage;
protected:
ElementType type = ElementType::UNDEFINED;
ElementShape shape = ElementShape::UNDEFINED;
uint16 intOrder = 0; // number of int points overall
uint32 elNum = 0;
uint32 *nodes = nullptr;
FEStorage* storage = nullptr;
};
//Element geometry class
class ElementLINE : public Element {
public:
ElementLINE() {
shape = ElementShape::LINE;
nodes = new uint32[getNNodes()];
}
ElementLINE& operator= (const ElementLINE& from) {
Element::operator= (from);
return *this;
}
};
class ElementTRIANGLE : public Element {
public:
ElementTRIANGLE() {
shape = ElementShape::TRIANGLE;
nodes = new uint32[getNNodes()];
}
ElementTRIANGLE& operator= (const ElementTRIANGLE& from) {
Element::operator= (from);
return *this;
}
};
class ElementQUAD : public Element {
public:
ElementQUAD () {
shape = ElementShape::QUAD;
nodes = new uint32[getNNodes()];
}
ElementQUAD& operator= (const ElementQUAD& from) {
Element::operator= (from);
return *this;
}
};
class ElementTETRA : public Element {
public:
ElementTETRA() {
shape = ElementShape::TETRA;
nodes = new uint32[getNNodes()];
}
ElementTETRA& operator= (const ElementTETRA& from) {
Element::operator= (from);
return *this;
}
};
class ElementHEXAHEDRON : public Element {
public:
ElementHEXAHEDRON() {
shape = ElementShape::HEXAHEDRON;
nodes = new uint32[getNNodes()];
}
ElementHEXAHEDRON& operator= (const ElementHEXAHEDRON& from) {
Element::operator= (from);
return *this;
}
};
} // namespace nla3d
// 'dirty' hack to avoid include loops (element-vs-festorage)
#include "FEStorage.h"
namespace nla3d {
template <uint16 dimM>
void Element::assembleK(math::MatSym<dimM> &Ke, std::initializer_list<Dof::dofType> _nodeDofs) {
assert (nodes != NULL);
double* Ke_p = Ke.ptr();
std::vector<Dof::dofType> nodeDof(_nodeDofs);
uint16 dim = static_cast<uint16> (_nodeDofs.size());
assert (getNNodes() * dim == dimM);
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
for (uint16 j=i; j < getNNodes(); j++) {
for (uint16 dj=0; dj < dim; dj++) {
if ((i==j) && (dj<di)) {
continue;
} else {
storage->addValueK(nodes[i], nodeDof[di], nodes[j], nodeDof[dj], *Ke_p);
Ke_p++;
}
}
}
}
}
}
template <uint16 dimM>
void Element::assembleC(math::MatSym<dimM> &Ce, std::initializer_list<Dof::dofType> _nodeDofs) {
assert (nodes != NULL);
double* Ce_p = Ce.ptr();
std::vector<Dof::dofType> nodeDof(_nodeDofs);
uint16 dim = static_cast<uint16> (_nodeDofs.size());
assert (getNNodes() * dim == dimM);
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
for (uint16 j=i; j < getNNodes(); j++) {
for (uint16 dj=0; dj < dim; dj++) {
if ((i==j) && (dj<di)) {
continue;
} else {
storage->addValueC(nodes[i], nodeDof[di], nodes[j], nodeDof[dj], *Ce_p);
Ce_p++;
}
}
}
}
}
}
template <uint16 dimM>
void Element::assembleM(math::MatSym<dimM> &Me, std::initializer_list<Dof::dofType> _nodeDofs) {
assert (nodes != NULL);
double* Me_p = Me.ptr();
std::vector<Dof::dofType> nodeDof(_nodeDofs);
uint16 dim = static_cast<uint16> (_nodeDofs.size());
assert (getNNodes() * dim == dimM);
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
for (uint16 j=i; j < getNNodes(); j++) {
for (uint16 dj=0; dj < dim; dj++) {
if ((i==j) && (dj<di)) {
continue;
} else {
storage->addValueC(nodes[i], nodeDof[di], nodes[j], nodeDof[dj], *Me_p);
Me_p++;
}
}
}
}
}
}
template <uint16 dimM>
void Element::assembleK(math::MatSym<dimM> &Ke, math::Vec<dimM> &Fe, std::initializer_list<Dof::dofType> _nodeDofs) {
assert (nodes != NULL);
double* Ke_p = Ke.ptr();
std::vector<Dof::dofType> nodeDof(_nodeDofs);
uint16 dim = static_cast<uint16> (_nodeDofs.size());
assert (getNNodes() * dim == dimM);
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
for (uint16 j=i; j < getNNodes(); j++) {
for (uint16 dj=0; dj < dim; dj++) {
if ((i==j) && (dj<di)) {
continue;
} else {
storage->addValueK(nodes[i], nodeDof[di], nodes[j], nodeDof[dj], *Ke_p);
Ke_p++;
}
}
}
}
}
double* Fe_p = Fe.ptr();
for (uint16 i=0; i < getNNodes(); i++) {
for (uint16 di=0; di < dim; di++) {
storage->addValueF(nodes[i], nodeDof[di], *Fe_p);
Fe_p++;
}
}
}
inline uint16 Element::getNNodes() {
return _shape_nnodes[shape];
}
inline uint16 Element::getDim() {
return _shape_dim[shape];
}
inline ElementShape Element::getShape() {
return shape;
}
inline ElementType Element::getType() {
return type;
}
// & is used here because this function is called such this:
// el->getNodeNumber(0) = 1234;
inline uint32& Element::getNodeNumber(uint16 num) {
assert(num < getNNodes());
assert(nodes);
return nodes[num];
}
inline uint16 Element::getIntegrationOrder() {
return intOrder;
}
inline void Element::setIntegrationOrder(uint16 _nint) {
intOrder = _nint;
}
inline FEStorage& Element::getStorage() {
return *storage;
}
inline uint32 Element::getElNum() {
return elNum;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "elements/element.h"
#include "isoparametric.h"
namespace nla3d {
void ElementIsoParamLINE::makeJacob() {
const uint16 dim = 2;
const uint16 nodes_num = 4;
// bound user-provided intOrder
intOrder = (intOrder < 1) ? 1 : intOrder;
intOrder = (intOrder > 3) ? 3 : intOrder;
// we store integration scheme index in i_int
// thus last change of intOrder will not affect the solution procedure
i_int = intOrder-1;
math::Vec<3> n1pos = storage->getNode(getNodeNumber(0)).pos;
math::Vec<3> n2pos = storage->getNode(getNodeNumber(1)).pos;
det = (n2pos-n1pos).length() * 0.5;
}
double ElementIsoParamLINE::volume() {
double volume = 0.0;
// NOTE: actually straight line can be integrated in constant time
for (uint16 np = 0; np < _np_line[i_int]; np++)
volume += intWeight(np);
return volume;
}
math::Vec<2> ElementIsoParamLINE::formFunc(double r) {
return math::Vec<2>(0.5 * (1.0 - r),
0.5 * (1.0 + r));
}
math::Vec<2> ElementIsoParamLINE::formFunc(uint16 np) {
return formFunc(_table_line[i_int][np].r);
}
math::Mat<2, 1> ElementIsoParamLINE::formFuncDeriv(double r) {
return math::Mat<2, 1>(-0.5, 0.5);
}
void ElementIsoParamQUAD::makeJacob() {
const uint16 dim = 2;
const uint16 nodes_num = 4;
// bound user-provided intOrder
intOrder = (intOrder < 1) ? 1 : intOrder;
intOrder = (intOrder > 3) ? 3 : intOrder;
// we store integration scheme index in i_int
// thus last change of intOrder will not affect the solution procedure
i_int = intOrder-1;
math::Mat<dim, dim> Jacob; //Jacob inv matrix
det.clear();
det.assign(_np_quad[i_int], 0.0);
NiXj.clear();
NiXj.resize(_np_quad[i_int]);
double inv_det;
math::Mat<nodes_num, dim> dN; // form function derivatives
math::Mat<dim, dim> J;
for (uint16 np=0; np < _np_quad[i_int]; np++) {
QuadPt2D q = _table_quad[i_int][np];
dN = formFuncDeriv(q.r, q.s);
J.zero();
for (uint16 nod = 0; nod < nodes_num; nod++) {
math::Vec<3> pos = storage->getNode(getNodeNumber(nod)).pos;
for (uint16 i = 0; i < dim; i++)
for (uint16 j = 0; j < dim; j++)
J[i][j] += dN[nod][i] * pos[j];
}
det[np] = J.det(); // determinant of Jacob matrix
// check for geometry form error
LOG_IF(det[np] < 1.0e-20, ERROR) << "Determinant is too small (" << det[np] << ") in element = " << elNum;
// обращение матрицы Якоби
inv_det = 1.0/det[np];
Jacob = J.inv(det[np]);
// производные функций формы по глоб. координатам
for (uint16 nod = 0; nod < nodes_num; nod++)
for (uint16 i=0; i < dim; i++)
for (uint16 j=0; j< dim; j++)
NiXj[np][nod][i] += Jacob[i][j] * dN[nod][j];
}
}
math::Vec<4> ElementIsoParamQUAD::formFunc(double r, double s) {
return math::Vec<4>(0.25 * (1.0 - r) * (1.0 - s),
0.25 * (1.0 + r) * (1.0 - s),
0.25 * (1.0 + r) * (1.0 + s),
0.25 * (1.0 - r) * (1.0 + s));
}
math::Vec<4> ElementIsoParamQUAD::formFunc(uint16 np) {
QuadPt2D q = _table_quad[i_int][np];
return formFunc(q.r, q.s);
}
math::Mat<4, 2> ElementIsoParamQUAD::formFuncDeriv(double r, double s) {
return math::Mat<4, 2> (-0.25 * (1.0 - s), -0.25 * (1.0 - r),
+0.25 * (1.0 - s), -0.25 * (1.0 + r),
+0.25 * (1.0 + s), +0.25 * (1.0 + r),
-0.25 * (1.0 + s), +0.25 * (1.0 - r));
}
double ElementIsoParamQUAD::volume() {
double volume = 0.0;
for (uint16 np = 0; np < _np_quad[i_int]; np++)
volume += intWeight(np);
return volume;
}
void ElementIsoParamHEXAHEDRON::makeJacob() {
const uint16 dim = 3;
const uint16 nodes_num = 8;
// bound user-provided intOrder
intOrder = (intOrder < 1) ? 1 : intOrder;
intOrder = (intOrder > 3) ? 3 : intOrder;
i_int = intOrder-1;
math::Mat<dim, dim> Jacob; //Jacob inv matrix
det.clear();
det.assign(_np_hexahedron[i_int], 0.0);
NiXj.clear();
NiXj.resize(_np_hexahedron[i_int]);
double inv_det;
math::Mat<nodes_num, dim> dN; // form function derivatives
math::Mat<dim, dim> J;
for (uint16 np=0; np < _np_hexahedron[i_int]; np++) {
QuadPt3D q = _table_hexahedron[i_int][np];
dN = formFuncDeriv(q.r, q.s, q.t);
J.zero();
for (uint16 nod = 0; nod < nodes_num; nod++) {
math::Vec<3> pos = storage->getNode(getNodeNumber(nod)).pos;
for (uint16 i = 0; i < dim; i++)
for (uint16 j = 0; j < dim; j++)
J[i][j] += dN[nod][i] * pos[j];
}
det[np] = J.det(); // determinant of Jacob matrix
// check for geometry form error
LOG_IF(det[np] < 1.0e-20, ERROR) << "Determinant is too small (" << det[np] << ") in element = " << elNum;
// обращение матрицы Якоби
inv_det = 1.0/det[np];
Jacob = J.inv(det[np]);
// производные функций формы по глоб. координатам
for (uint16 nod = 0; nod < nodes_num; nod++)
for (uint16 i=0; i < dim; i++)
for (uint16 j=0; j< dim; j++)
NiXj[np][nod][i] += Jacob[i][j] * dN[nod][j];
}
}
math::Vec<8> ElementIsoParamHEXAHEDRON::formFunc(double r, double s, double t) {
return math::Vec<8>(0.125 * (1.0 - r) * (1.0 - s) * (1.0 - t),
0.125 * (1.0 + r) * (1.0 - s) * (1.0 - t),
0.125 * (1.0 + r) * (1.0 + s) * (1.0 - t),
0.125 * (1.0 - r) * (1.0 + s) * (1.0 - t),
0.125 * (1.0 - r) * (1.0 - s) * (1.0 + t),
0.125 * (1.0 + r) * (1.0 - s) * (1.0 + t),
0.125 * (1.0 + r) * (1.0 + s) * (1.0 + t),
0.125 * (1.0 - r) * (1.0 + s) * (1.0 + t));
}
math::Vec<8> ElementIsoParamHEXAHEDRON::formFunc(uint16 np) {
QuadPt3D q = _table_hexahedron[i_int][np];
return formFunc(q.r, q.s, q.t);
}
math::Mat<8, 3> ElementIsoParamHEXAHEDRON::formFuncDeriv(double r, double s, double t) {
return math::Mat<8, 3> (-0.125 * (1.0 - s) * (1.0 - t), -0.125 * (1.0 - r) * (1.0 - t), -0.125 * (1.0 - r) * (1.0 - s),
+0.125 * (1.0 - s) * (1.0 - t), -0.125 * (1.0 + r) * (1.0 - t), -0.125 * (1.0 + r) * (1.0 - s),
+0.125 * (1.0 + s) * (1.0 - t), +0.125 * (1.0 + r) * (1.0 - t), -0.125 * (1.0 + r) * (1.0 + s),
-0.125 * (1.0 + s) * (1.0 - t), +0.125 * (1.0 - r) * (1.0 - t), -0.125 * (1.0 - r) * (1.0 + s),
-0.125 * (1.0 - s) * (1.0 + t), -0.125 * (1.0 - r) * (1.0 + t), +0.125 * (1.0 - r) * (1.0 - s),
+0.125 * (1.0 - s) * (1.0 + t), -0.125 * (1.0 + r) * (1.0 + t), +0.125 * (1.0 + r) * (1.0 - s),
+0.125 * (1.0 + s) * (1.0 + t), +0.125 * (1.0 + r) * (1.0 + t), +0.125 * (1.0 + r) * (1.0 + s),
-0.125 * (1.0 + s) * (1.0 + t), +0.125 * (1.0 - r) * (1.0 + t), +0.125 * (1.0 - r) * (1.0 + s));
}
double ElementIsoParamHEXAHEDRON::volume() {
double volume = 0.0;
for (uint16 np = 0; np < _np_hexahedron[i_int]; np++)
volume += intWeight(np);
return volume;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
// Formulations for isoparametric FE for different shapes. Isoparametic formulation means usage of the
// same shape functions for geometry interpolation and for field (displacement, for instance)
// interpolation.
// These derived classes specify next things:
// 1. Specify form functions
// 2. Specify integration rule (Gauss quadrature)
// 3. Specify field derivatives in local and global coordinates
namespace nla3d {
// structures for convenient keeping of quadrature constants
struct QuadPt1D {
double r;
double w;
};
struct QuadPt2D {
double r;
double s;
double w;
};
struct QuadPt3D {
double r;
double s;
double t;
double w;
};
// gauss quadrature for 1D line from (-1) to (1)
// 1st order
const static QuadPt1D _quad_line_o1[] = {
{+0.0000000000000000, +2.0000000000000000}
};
// 2nd order
const static QuadPt1D _quad_line_o2[] = {
{-0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, +1.0000000000000000}
};
// 3rd order
const static QuadPt1D _quad_line_o3[] = {
{-0.7745966692414834, +0.5555555555555556},
{+0.0000000000000000, +0.8888888888888888},
{+0.7745966692414834, +0.5555555555555556}
};
// array of number of quadrature points in integration scheme
const static uint16 _np_line[] = {
sizeof(_quad_line_o1) / sizeof(QuadPt1D),
sizeof(_quad_line_o2) / sizeof(QuadPt1D),
sizeof(_quad_line_o3) / sizeof(QuadPt1D)
};
const static QuadPt1D* _table_line[] = {
_quad_line_o1,
_quad_line_o2,
_quad_line_o3
};
// gauss quadrature for 2D rect from (-1,-1) to (1,1)
// 1st order
const static QuadPt2D _quad_quad_o1[] = {
{+0.0000000000000000, +0.0000000000000000, +4.0000000000000000}
};
// 2nd order
const static QuadPt2D _quad_quad_o2[] = {
{-0.5773502691896258, -0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, -0.5773502691896258, +1.0000000000000000},
{-0.5773502691896258, +0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, +0.5773502691896258, +1.0000000000000000}
};
// 3rd order
const static QuadPt2D _quad_quad_o3[] = {
{-0.7745966692414834, -0.7745966692414834, +0.3086419753086420},
{+0.0000000000000000, -0.7745966692414834, +0.4938271604938271},
{+0.7745966692414834, -0.7745966692414834, +0.3086419753086420},
{-0.7745966692414834, +0.0000000000000000, +0.4938271604938271},
{+0.0000000000000000, +0.0000000000000000, +0.7901234567901234},
{+0.7745966692414834, +0.0000000000000000, +0.4938271604938271},
{-0.7745966692414834, +0.7745966692414834, +0.3086419753086420},
{+0.0000000000000000, +0.7745966692414834, +0.4938271604938271},
{+0.7745966692414834, +0.7745966692414834, +0.3086419753086420}
};
// array of number of quadrature points in integration scheme
const static uint16 _np_quad[] = {
sizeof(_quad_quad_o1) / sizeof(QuadPt2D),
sizeof(_quad_quad_o2) / sizeof(QuadPt2D),
sizeof(_quad_quad_o3) / sizeof(QuadPt2D)
};
const static QuadPt2D* _table_quad[] = {
_quad_quad_o1,
_quad_quad_o2,
_quad_quad_o3
};
// gauss quadrature for 3D brick from (-1,-1,-1) to (1,1,1)
// 1st order
const static QuadPt3D _quad_hexahedron_o1[] = {
{+0.0000000000000000, +0.0000000000000000, +0.0000000000000000, +8.0000000000000000}
};
// 2nd order
const static QuadPt3D _quad_hexahedron_o2[] = {
{-0.5773502691896258, -0.5773502691896258, -0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, -0.5773502691896258, -0.5773502691896258, +1.0000000000000000},
{-0.5773502691896258, +0.5773502691896258, -0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, +0.5773502691896258, -0.5773502691896258, +1.0000000000000000},
{-0.5773502691896258, -0.5773502691896258, +0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, -0.5773502691896258, +0.5773502691896258, +1.0000000000000000},
{-0.5773502691896258, +0.5773502691896258, +0.5773502691896258, +1.0000000000000000},
{+0.5773502691896258, +0.5773502691896258, +0.5773502691896258, +1.0000000000000000}
};
// 3rd order
const static QuadPt3D _quad_hexahedron_o3[] = {
{-0.7745966692414834, -0.7745966692414834, -0.7745966692414834, +0.1714677640603567},
{+0.0000000000000000, -0.7745966692414834, -0.7745966692414834, +0.2743484224965707},
{+0.7745966692414834, -0.7745966692414834, -0.7745966692414834, +0.1714677640603567},
{-0.7745966692414834, +0.0000000000000000, -0.7745966692414834, +0.2743484224965707},
{+0.0000000000000000, +0.0000000000000000, -0.7745966692414834, +0.4389574759945130},
{+0.7745966692414834, +0.0000000000000000, -0.7745966692414834, +0.2743484224965707},
{-0.7745966692414834, +0.7745966692414834, -0.7745966692414834, +0.1714677640603567},
{+0.0000000000000000, +0.7745966692414834, -0.7745966692414834, +0.2743484224965707},
{+0.7745966692414834, +0.7745966692414834, -0.7745966692414834, +0.1714677640603567},
{-0.7745966692414834, -0.7745966692414834, +0.0000000000000000, +0.2743484224965707},
{+0.0000000000000000, -0.7745966692414834, +0.0000000000000000, +0.4389574759945130},
{+0.7745966692414834, -0.7745966692414834, +0.0000000000000000, +0.2743484224965707},
{-0.7745966692414834, +0.0000000000000000, +0.0000000000000000, +0.4389574759945130},
{+0.0000000000000000, +0.0000000000000000, +0.0000000000000000, +0.7023319615912207},
{+0.7745966692414834, +0.0000000000000000, +0.0000000000000000, +0.4389574759945130},
{-0.7745966692414834, +0.7745966692414834, +0.0000000000000000, +0.2743484224965707},
{+0.0000000000000000, +0.7745966692414834, +0.0000000000000000, +0.4389574759945130},
{+0.7745966692414834, +0.7745966692414834, +0.0000000000000000, +0.2743484224965707},
{-0.7745966692414834, -0.7745966692414834, +0.7745966692414834, +0.1714677640603567},
{+0.0000000000000000, -0.7745966692414834, +0.7745966692414834, +0.2743484224965707},
{+0.7745966692414834, -0.7745966692414834, +0.7745966692414834, +0.1714677640603567},
{-0.7745966692414834, +0.0000000000000000, +0.7745966692414834, +0.2743484224965707},
{+0.0000000000000000, +0.0000000000000000, +0.7745966692414834, +0.4389574759945130},
{+0.7745966692414834, +0.0000000000000000, +0.7745966692414834, +0.2743484224965707},
{-0.7745966692414834, +0.7745966692414834, +0.7745966692414834, +0.1714677640603567},
{+0.0000000000000000, +0.7745966692414834, +0.7745966692414834, +0.2743484224965707},
{+0.7745966692414834, +0.7745966692414834, +0.7745966692414834, +0.1714677640603567}
};
const static uint16 _np_hexahedron[] = {
sizeof(_quad_hexahedron_o1) / sizeof(QuadPt3D),
sizeof(_quad_hexahedron_o2) / sizeof(QuadPt3D),
sizeof(_quad_hexahedron_o3) / sizeof(QuadPt3D)
};
const static QuadPt3D* _table_hexahedron[] = {
_quad_hexahedron_o1,
_quad_hexahedron_o2,
_quad_hexahedron_o3
};
class ElementIsoParamLINE : public ElementLINE {
public:
double det; //Jacobian
void makeJacob();
double intWeight(uint16 np);
double volume();
void np2rst(uint16 np, double *xi); //by number of gauss point find local coordinates
uint16 nOfIntPoints();
// get form function values in local point (r, s)
math::Vec<2> formFunc(double r);
// get form function values in integration point np
math::Vec<2> formFunc(uint16 np);
math::Mat<2, 1> formFuncDeriv(double r);
protected:
uint16 i_int = 0; // index of integration scheme
};
class ElementIsoParamQUAD : public ElementQUAD {
public:
std::vector<math::Mat<4, 2> > NiXj; //derivates form function / local coordinates
std::vector<double> det; //Jacobian
double sideDet[4]; //Jacobian for side integration
// function to calculate all staff for isoparametric FE
void makeJacob();
double intWeight(uint16 np);
double volume();
void np2rst(uint16 np, double *xi); //by number of gauss point find local coordinates
uint16 nOfIntPoints();
// get form function values in local point (r, s)
math::Vec<4> formFunc(double r, double s);
// get form function values in integration point np
math::Vec<4> formFunc(uint16 np);
math::Mat<4, 2> formFuncDeriv(double r, double s);
protected:
uint16 i_int = 0; // index of integration scheme
};
class ElementIsoParamHEXAHEDRON : public ElementHEXAHEDRON {
public:
std::vector<math::Mat<8, 3> > NiXj; //derivates form function / local coordinates
std::vector<double> det; //Jacobian
// function to calculate all staff for isoparametric FE
void makeJacob();
double intWeight(uint16 np);
double volume();
void np2rst(uint16 np, double *xi); //by number of gauss point find local coordinates
uint16 nOfIntPoints();
// get form function values in local point (r, s, t)
math::Vec<8> formFunc(double r, double s, double t);
// get form function values in integration point np
math::Vec<8> formFunc(uint16 np);
// get form function derivatives (vs. r,s,t) in local point (r,s,t)
math::Mat<8, 3> formFuncDeriv(double r, double s, double t);
protected:
uint16 i_int = 0; // index of integration scheme
};
inline double ElementIsoParamLINE::intWeight(uint16 np) {
assert(np < _np_line[i_int]);
assert(det > 0.0);
return _table_line[i_int][np].w * det;
}
inline void ElementIsoParamLINE::np2rst (uint16 np, double *v) {
assert(np < _np_line[i_int]);
v[0] = _table_line[i_int][np].r;
}
inline uint16 ElementIsoParamLINE::nOfIntPoints() {
return _np_line[i_int];
}
inline double ElementIsoParamQUAD::intWeight(uint16 np) {
assert(np < _np_quad[i_int]);
assert(det.size() != 0);
return _table_quad[i_int][np].w * det[np];
}
inline void ElementIsoParamQUAD::np2rst (uint16 np, double *v) {
assert(np < _np_quad[i_int]);
v[0] = _table_quad[i_int][np].r;
v[1] = _table_quad[i_int][np].s;
}
inline uint16 ElementIsoParamQUAD::nOfIntPoints() {
return _np_quad[i_int];
}
inline double ElementIsoParamHEXAHEDRON::intWeight(uint16 np) {
assert(np < _np_hexahedron[i_int]);
assert(det.size() != 0);
return _table_hexahedron[i_int][np].w * det[np];
}
inline void ElementIsoParamHEXAHEDRON::np2rst (uint16 np, double *v) {
assert(np < _np_hexahedron[i_int]);
v[0] = _table_hexahedron[i_int][np].r;
v[1] = _table_hexahedron[i_int][np].s;
v[2] = _table_hexahedron[i_int][np].t;
}
inline uint16 ElementIsoParamHEXAHEDRON::nOfIntPoints() {
return _np_hexahedron[i_int];
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
namespace nla3d {
enum class scalarQuery {
UNDEF = 0,
SP,
W,
WU,
WP,
VOL,
LAST
};
const char* const scalarQueryLabels[] = {"UNDEFINED", "S_P", "W", "WU", "WP", "VOL", "LAST"};
static_assert((int)scalarQuery::LAST == sizeof(scalarQueryLabels)/sizeof(scalarQueryLabels[0]) - 1,
"scalarQuery enumeration and scalarQueryLabels must have the same number of entries");
enum class vectorQuery {
UNDEF,
IC,
FLUX,
LAST
};
const char* const vectorQueryLabels[] = {"UNDEFINEDS", "IC","FLUX","LAST"};
static_assert((int)vectorQuery::LAST == sizeof(vectorQueryLabels)/sizeof(vectorQueryLabels[0]) - 1,
"vectorQuery enumeration and vectorQueryLabels must have the same number of entries");
enum class tensorQuery {
UNDEF,
// usual stress tensor
COUCHY,
// second Piola-Kirchgoff stress tensor (symmetric 3x3)
PK2,
// Lagrange deformations
E,
// C = F^T F
C,
LAST
};
const char* const tensorQueryLabels[] = {"UNDEFINED","COUCHY", "PK2", "E", "C", "LAST"};
static_assert((int)tensorQuery::LAST == sizeof(tensorQueryLabels)/sizeof(tensorQueryLabels[0]) - 1,
"tensorQuery enumeration and tensorQueryLabels must have the same number of entries");
// means averaged values of the element
const uint16 GP_MEAN = 100;
inline char const* query2label(scalarQuery query) {
assert(query >= scalarQuery::UNDEF && query < scalarQuery::LAST);
return scalarQueryLabels[(int) query];
}
inline char const* query2label(vectorQuery query) {
assert(query >= vectorQuery::UNDEF && query < vectorQuery::LAST);
return vectorQueryLabels[(int) query];
}
inline char const* query2label(tensorQuery query) {
assert(query >= tensorQuery::UNDEF && query < tensorQuery::LAST);
return tensorQueryLabels[(int) query];
}
} // namespace nla3d
#------------------------------------------------------------------------------#
# This makefile was generated by 'cbp2make' tool rev.147 #
#------------------------------------------------------------------------------#
WORKDIR = `pwd`
CC = gcc
CXX = g++
AR = ar
LD = g++
WINDRES = windres
INC =
CFLAGS =
RESINC =
LIBDIR =
LIB =
LDFLAGS =
INC_DEBUG = $(INC) -I../../cuda_nla3d -I../math -I../nla3d -I../easylogging -I/usr/include/eigen3
CFLAGS_DEBUG = $(CFLAGS) -Wall -m64 -g -ftemplate-backtrace-limit=0 -fPIC
RESINC_DEBUG = $(RESINC)
RCFLAGS_DEBUG = $(RCFLAGS)
LIBDIR_DEBUG = $(LIBDIR)
LIB_DEBUG = $(LIB)
LDFLAGS_DEBUG = $(LDFLAGS) -m64
OBJDIR_DEBUG = obj/Debug
DEP_DEBUG =
OUT_DEBUG = bin/Debug/libnla3d.a
INC_RELEASE = $(INC) -I../../cuda_nla3d -I../math -I../nla3d -I../easylogging -I/usr/include/eigen3
CFLAGS_RELEASE = $(CFLAGS) -O2 -Wall -m64 -ftemplate-backtrace-limit=0 -fPIC
RESINC_RELEASE = $(RESINC)
RCFLAGS_RELEASE = $(RCFLAGS)
LIBDIR_RELEASE = $(LIBDIR)
LIB_RELEASE = $(LIB)
LDFLAGS_RELEASE = $(LDFLAGS) -s -m64
OBJDIR_RELEASE = obj/Release
DEP_RELEASE =
OUT_RELEASE = bin/Release/libnla3d.a
OBJ_DEBUG = $(OBJDIR_DEBUG)/elements/SOLID81.o $(OBJDIR_DEBUG)/elements/TETRA0.o $(OBJDIR_DEBUG)/elements/TETRA1.o $(OBJDIR_DEBUG)/elements/TRIANGLE4.o $(OBJDIR_DEBUG)/elements/TRUSS3.o $(OBJDIR_DEBUG)/elements/element.o $(OBJDIR_DEBUG)/elements/isoparametric.o $(OBJDIR_DEBUG)/materials/MaterialFactory.o $(OBJDIR_DEBUG)/materials/material.o $(OBJDIR_DEBUG)/materials/materials_hyperelastic.o $(OBJDIR_DEBUG)/solidmech.o $(OBJDIR_DEBUG)/PostProcessor.o $(OBJDIR_DEBUG)/FEComponent.o $(OBJDIR_DEBUG)/FESolver.o $(OBJDIR_DEBUG)/FEStorage.o $(OBJDIR_DEBUG)/Mpc.o $(OBJDIR_DEBUG)/Node.o $(OBJDIR_DEBUG)/Dof.o $(OBJDIR_DEBUG)/ReactionProcessor.o $(OBJDIR_DEBUG)/VtkProcessor.o $(OBJDIR_DEBUG)/elements/ElementFactory.o $(OBJDIR_DEBUG)/elements/PLANE41.o $(OBJDIR_DEBUG)/elements/QUADTH.o
OBJ_RELEASE = $(OBJDIR_RELEASE)/elements/SOLID81.o $(OBJDIR_RELEASE)/elements/TETRA0.o $(OBJDIR_RELEASE)/elements/TETRA1.o $(OBJDIR_RELEASE)/elements/TRIANGLE4.o $(OBJDIR_RELEASE)/elements/TRUSS3.o $(OBJDIR_RELEASE)/elements/element.o $(OBJDIR_RELEASE)/elements/isoparametric.o $(OBJDIR_RELEASE)/materials/MaterialFactory.o $(OBJDIR_RELEASE)/materials/material.o $(OBJDIR_RELEASE)/materials/materials_hyperelastic.o $(OBJDIR_RELEASE)/solidmech.o $(OBJDIR_RELEASE)/PostProcessor.o $(OBJDIR_RELEASE)/FEComponent.o $(OBJDIR_RELEASE)/FESolver.o $(OBJDIR_RELEASE)/FEStorage.o $(OBJDIR_RELEASE)/Mpc.o $(OBJDIR_RELEASE)/Node.o $(OBJDIR_RELEASE)/Dof.o $(OBJDIR_RELEASE)/ReactionProcessor.o $(OBJDIR_RELEASE)/VtkProcessor.o $(OBJDIR_RELEASE)/elements/ElementFactory.o $(OBJDIR_RELEASE)/elements/PLANE41.o $(OBJDIR_RELEASE)/elements/QUADTH.o
all: debug release
clean: clean_debug clean_release
before_debug:
test -d bin/Debug || mkdir -p bin/Debug
test -d $(OBJDIR_DEBUG)/elements || mkdir -p $(OBJDIR_DEBUG)/elements
test -d $(OBJDIR_DEBUG)/materials || mkdir -p $(OBJDIR_DEBUG)/materials
test -d $(OBJDIR_DEBUG) || mkdir -p $(OBJDIR_DEBUG)
after_debug:
debug: before_debug out_debug after_debug
out_debug: before_debug $(OBJ_DEBUG) $(DEP_DEBUG)
$(AR) rcs $(OUT_DEBUG) $(OBJ_DEBUG)
$(OBJDIR_DEBUG)/elements/SOLID81.o: elements/SOLID81.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/SOLID81.cpp -o $(OBJDIR_DEBUG)/elements/SOLID81.o
$(OBJDIR_DEBUG)/elements/TETRA0.o: elements/TETRA0.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/TETRA0.cpp -o $(OBJDIR_DEBUG)/elements/TETRA0.o
$(OBJDIR_DEBUG)/elements/TETRA1.o: elements/TETRA1.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/TETRA1.cpp -o $(OBJDIR_DEBUG)/elements/TETRA1.o
$(OBJDIR_DEBUG)/elements/TRIANGLE4.o: elements/TRIANGLE4.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/TRIANGLE4.cpp -o $(OBJDIR_DEBUG)/elements/TRIANGLE4.o
$(OBJDIR_DEBUG)/elements/TRUSS3.o: elements/TRUSS3.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/TRUSS3.cpp -o $(OBJDIR_DEBUG)/elements/TRUSS3.o
$(OBJDIR_DEBUG)/elements/element.o: elements/element.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/element.cpp -o $(OBJDIR_DEBUG)/elements/element.o
$(OBJDIR_DEBUG)/elements/isoparametric.o: elements/isoparametric.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/isoparametric.cpp -o $(OBJDIR_DEBUG)/elements/isoparametric.o
$(OBJDIR_DEBUG)/materials/MaterialFactory.o: materials/MaterialFactory.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c materials/MaterialFactory.cpp -o $(OBJDIR_DEBUG)/materials/MaterialFactory.o
$(OBJDIR_DEBUG)/materials/material.o: materials/material.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c materials/material.cpp -o $(OBJDIR_DEBUG)/materials/material.o
$(OBJDIR_DEBUG)/materials/materials_hyperelastic.o: materials/materials_hyperelastic.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c materials/materials_hyperelastic.cpp -o $(OBJDIR_DEBUG)/materials/materials_hyperelastic.o
$(OBJDIR_DEBUG)/solidmech.o: solidmech.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c solidmech.cpp -o $(OBJDIR_DEBUG)/solidmech.o
$(OBJDIR_DEBUG)/PostProcessor.o: PostProcessor.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c PostProcessor.cpp -o $(OBJDIR_DEBUG)/PostProcessor.o
$(OBJDIR_DEBUG)/FEComponent.o: FEComponent.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c FEComponent.cpp -o $(OBJDIR_DEBUG)/FEComponent.o
$(OBJDIR_DEBUG)/FESolver.o: FESolver.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c FESolver.cpp -o $(OBJDIR_DEBUG)/FESolver.o
$(OBJDIR_DEBUG)/FEStorage.o: FEStorage.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c FEStorage.cpp -o $(OBJDIR_DEBUG)/FEStorage.o
$(OBJDIR_DEBUG)/Mpc.o: Mpc.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c Mpc.cpp -o $(OBJDIR_DEBUG)/Mpc.o
$(OBJDIR_DEBUG)/Node.o: Node.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c Node.cpp -o $(OBJDIR_DEBUG)/Node.o
$(OBJDIR_DEBUG)/Dof.o: Dof.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c Dof.cpp -o $(OBJDIR_DEBUG)/Dof.o
$(OBJDIR_DEBUG)/ReactionProcessor.o: ReactionProcessor.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c ReactionProcessor.cpp -o $(OBJDIR_DEBUG)/ReactionProcessor.o
$(OBJDIR_DEBUG)/VtkProcessor.o: VtkProcessor.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c VtkProcessor.cpp -o $(OBJDIR_DEBUG)/VtkProcessor.o
$(OBJDIR_DEBUG)/elements/ElementFactory.o: elements/ElementFactory.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/ElementFactory.cpp -o $(OBJDIR_DEBUG)/elements/ElementFactory.o
$(OBJDIR_DEBUG)/elements/PLANE41.o: elements/PLANE41.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/PLANE41.cpp -o $(OBJDIR_DEBUG)/elements/PLANE41.o
$(OBJDIR_DEBUG)/elements/QUADTH.o: elements/QUADTH.cpp
$(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c elements/QUADTH.cpp -o $(OBJDIR_DEBUG)/elements/QUADTH.o
clean_debug:
rm -f $(OBJ_DEBUG) $(OUT_DEBUG)
rm -rf bin/Debug
rm -rf $(OBJDIR_DEBUG)/elements
rm -rf $(OBJDIR_DEBUG)/materials
rm -rf $(OBJDIR_DEBUG)
before_release:
test -d bin/Release || mkdir -p bin/Release
test -d $(OBJDIR_RELEASE)/elements || mkdir -p $(OBJDIR_RELEASE)/elements
test -d $(OBJDIR_RELEASE)/materials || mkdir -p $(OBJDIR_RELEASE)/materials
test -d $(OBJDIR_RELEASE) || mkdir -p $(OBJDIR_RELEASE)
after_release:
release: before_release out_release after_release
out_release: before_release $(OBJ_RELEASE) $(DEP_RELEASE)
$(AR) rcs $(OUT_RELEASE) $(OBJ_RELEASE)
$(OBJDIR_RELEASE)/elements/SOLID81.o: elements/SOLID81.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/SOLID81.cpp -o $(OBJDIR_RELEASE)/elements/SOLID81.o
$(OBJDIR_RELEASE)/elements/TETRA0.o: elements/TETRA0.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/TETRA0.cpp -o $(OBJDIR_RELEASE)/elements/TETRA0.o
$(OBJDIR_RELEASE)/elements/TETRA1.o: elements/TETRA1.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/TETRA1.cpp -o $(OBJDIR_RELEASE)/elements/TETRA1.o
$(OBJDIR_RELEASE)/elements/TRIANGLE4.o: elements/TRIANGLE4.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/TRIANGLE4.cpp -o $(OBJDIR_RELEASE)/elements/TRIANGLE4.o
$(OBJDIR_RELEASE)/elements/TRUSS3.o: elements/TRUSS3.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/TRUSS3.cpp -o $(OBJDIR_RELEASE)/elements/TRUSS3.o
$(OBJDIR_RELEASE)/elements/element.o: elements/element.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/element.cpp -o $(OBJDIR_RELEASE)/elements/element.o
$(OBJDIR_RELEASE)/elements/isoparametric.o: elements/isoparametric.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/isoparametric.cpp -o $(OBJDIR_RELEASE)/elements/isoparametric.o
$(OBJDIR_RELEASE)/materials/MaterialFactory.o: materials/MaterialFactory.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c materials/MaterialFactory.cpp -o $(OBJDIR_RELEASE)/materials/MaterialFactory.o
$(OBJDIR_RELEASE)/materials/material.o: materials/material.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c materials/material.cpp -o $(OBJDIR_RELEASE)/materials/material.o
$(OBJDIR_RELEASE)/materials/materials_hyperelastic.o: materials/materials_hyperelastic.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c materials/materials_hyperelastic.cpp -o $(OBJDIR_RELEASE)/materials/materials_hyperelastic.o
$(OBJDIR_RELEASE)/solidmech.o: solidmech.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c solidmech.cpp -o $(OBJDIR_RELEASE)/solidmech.o
$(OBJDIR_RELEASE)/PostProcessor.o: PostProcessor.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c PostProcessor.cpp -o $(OBJDIR_RELEASE)/PostProcessor.o
$(OBJDIR_RELEASE)/FEComponent.o: FEComponent.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c FEComponent.cpp -o $(OBJDIR_RELEASE)/FEComponent.o
$(OBJDIR_RELEASE)/FESolver.o: FESolver.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c FESolver.cpp -o $(OBJDIR_RELEASE)/FESolver.o
$(OBJDIR_RELEASE)/FEStorage.o: FEStorage.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c FEStorage.cpp -o $(OBJDIR_RELEASE)/FEStorage.o
$(OBJDIR_RELEASE)/Mpc.o: Mpc.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c Mpc.cpp -o $(OBJDIR_RELEASE)/Mpc.o
$(OBJDIR_RELEASE)/Node.o: Node.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c Node.cpp -o $(OBJDIR_RELEASE)/Node.o
$(OBJDIR_RELEASE)/Dof.o: Dof.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c Dof.cpp -o $(OBJDIR_RELEASE)/Dof.o
$(OBJDIR_RELEASE)/ReactionProcessor.o: ReactionProcessor.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c ReactionProcessor.cpp -o $(OBJDIR_RELEASE)/ReactionProcessor.o
$(OBJDIR_RELEASE)/VtkProcessor.o: VtkProcessor.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c VtkProcessor.cpp -o $(OBJDIR_RELEASE)/VtkProcessor.o
$(OBJDIR_RELEASE)/elements/ElementFactory.o: elements/ElementFactory.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/ElementFactory.cpp -o $(OBJDIR_RELEASE)/elements/ElementFactory.o
$(OBJDIR_RELEASE)/elements/PLANE41.o: elements/PLANE41.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/PLANE41.cpp -o $(OBJDIR_RELEASE)/elements/PLANE41.o
$(OBJDIR_RELEASE)/elements/QUADTH.o: elements/QUADTH.cpp
$(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c elements/QUADTH.cpp -o $(OBJDIR_RELEASE)/elements/QUADTH.o
clean_release:
rm -f $(OBJ_RELEASE) $(OUT_RELEASE)
rm -rf bin/Release
rm -rf $(OBJDIR_RELEASE)/elements
rm -rf $(OBJDIR_RELEASE)/materials
rm -rf $(OBJDIR_RELEASE)
.PHONY: before_debug after_debug clean_debug before_release after_release clean_release
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "materials/MaterialFactory.h"
namespace nla3d {
const char* const MaterialFactory::matModelLabels[]={"UNDEFINED",
"Neo-Hookean",
"Biderman",
"Mooney-Rivlin"
};
MaterialFactory::matId MaterialFactory::matName2matId (std::string matName) {
for (uint16 i = 0; i < MaterialFactory::LAST; i++) {
if (matName.compare(MaterialFactory::matModelLabels[i]) == 0) {
return (MaterialFactory::matId) i;
}
}
return MaterialFactory::NOT_DEFINED;
}
Material* MaterialFactory::createMaterial (std::string matName) {
uint16 matId = matName2matId(matName);
Material* mat;
if (matId == MaterialFactory::NOT_DEFINED) {
LOG(ERROR) << "Can't find material " << matName;
return nullptr;
}
switch (matId) {
case MaterialFactory::NEO_HOOKEAN_COMP:
mat = new Mat_Comp_Neo_Hookean();
break;
case MaterialFactory::BIDERMAN_COMP:
mat = new Mat_Comp_Biderman();
break;
case MaterialFactory::MOONEYRIVLIN_COMP:
mat = new Mat_Comp_MooneyRivlin();
break;
default:
LOG(ERROR) << "Don't have a material with id = " << matId;
return nullptr;
}
mat->code = matId;
return mat;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "materials/materials_hyperelastic.h"
namespace nla3d {
class MaterialFactory {
public:
enum matId {
NOT_DEFINED,
NEO_HOOKEAN_COMP,
BIDERMAN_COMP,
MOONEYRIVLIN_COMP,
LAST
};
static const char* const matModelLabels[];
static matId matName2matId (std::string matName);
static Material* createMaterial (std::string matName);
};
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "materials/material.h"
namespace nla3d {
const double Material::I[6] = {1.0,0.0,0.0,1.0,0.0,1.0};
//---------------------------------------------------------
//----------------MATERIAL ABSTRACT CLASS------------------
//---------------------------------------------------------
Material::Material (uint16 num_c)
{
code = 0;
numC = num_c;
MC = new double[num_c];
}
std::string Material::toString() {
std::string str;
str += getName();
for (size_t i=0; i < getNumC(); i++) {
str += " " + MC_names[i] + " = " + toStr(MC[i]);
}
return str;
}
double& Material::Ci (const std::string& nameConst) {
for (size_t i = 0; i < getNumC(); i++) {
if (nameConst.compare(MC_names[i]) == 0) {
return MC[i];
}
}
LOG(ERROR) << "Dan't find a material constant with name " << nameConst;
double dummy;
return dummy;
}
void Material::register_mat_const(uint16 num, ...) {
numC = num;
va_list vlist;
va_start(vlist, num);
MC_names.clear();
MC_names.reserve(numC);
for (uint16 i=0; i < num; i++) {
MC_names.push_back(va_arg(vlist,char*));
}
MC = new double[num];
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include <string>
#include "math/Vec.h"
#include "math/Mat.h"
namespace nla3d {
class MaterialFactory;
// Material must have named material constants
class Material {
public:
Material () : MC(NULL), numC(0), code(0) {
}
Material (uint16 num_c);
~Material() // TODO: discover the virtual destructor
{
if (MC) delete[] MC;
MC = NULL;
}
virtual std::string toString();
std::string getName();
uint16 getCode ();
// constants getters
double& Ci (uint16 i);
double& Ci (const std::string& nameConst);
uint16 getNumC ();
static const double I[6];
friend class MaterialFactory;
protected:
void register_mat_const(uint16 num, ...);
double* MC;
std::vector<std::string> MC_names;
uint16 numC;
uint16 code;
std::string name;
};
// ---=== FUNCTIONS ===--- //
inline std::string Material::getName()
{
return name;
}
inline uint16 Material::getCode ()
{
return code;
}
inline double& Material::Ci (uint16 i)
{
assert(i < numC);
return MC[i];
}
inline uint16 Material::getNumC ()
{
return numC;
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "materials/materials_hyperelastic.h"
namespace nla3d {
using namespace solidmech;
const double Mat_Hyper_Isotrop_General::II[6][6] = { {1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.5,0.0,0.0,0.0,0.0},
{0.0,0.0,0.5,0.0,0.0,0.0},
{0.0,0.0,0.0,1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.5,0.0},
{0.0,0.0,0.0,0.0,0.0,1.0} };
//---------------------------------------------------------
//---------------Mat_Hyper_Isotrop_General-----------------
//---------------------------------------------------------
void Mat_Hyper_Isotrop_General::getS_U (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, double *S) {
LOG(FATAL) << "Not now";
}
void Mat_Hyper_Isotrop_General::getD_U (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, double *D) {
LOG(FATAL) << "Not now";
}
//C - all times we must have 6 components
void Mat_Hyper_Isotrop_General::getS_UP (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, const double press, double *S) {
double alpha[2];
double IC[3];
solidmech::IC_C(C, IC);
double _13I1C = 1.0/3.0*IC[0];
double _23I2C = 2.0/3.0*IC[1];
double J = IC[2];
double oo = 1.0/(J*J);
double pp = pow(J,-2.0/3.0);
double pppp = pp*pp;
double C_inv[6];
solidmech::invC_C(C, J, C_inv);
W_first_derivatives(IC[0]*pp, IC[1]*pppp, 0.0, alpha); //only first derivatives
double ko1 = 2*alpha[AL_1]*pp;
double ko2 = 2*alpha[AL_2]*pppp;
double A[] = {I[0]-_13I1C*C_inv[0], I[1]-_13I1C*C_inv[1], I[2]-_13I1C*C_inv[2],
I[3]-_13I1C*C_inv[3], I[4]-_13I1C*C_inv[4], I[5]-_13I1C*C_inv[5]};
double B[] = {IC[0]*I[0]-C[0]-_23I2C*C_inv[0], IC[0]*I[1]-C[1]-_23I2C*C_inv[1], IC[0]*I[2]-C[2]-_23I2C*C_inv[2],
IC[0]*I[3]-C[3]-_23I2C*C_inv[3], IC[0]*I[4]-C[4]-_23I2C*C_inv[4], IC[0]*I[5]-C[5]-_23I2C*C_inv[5]};
solidmech::tensorComponents ij;
for (uint16 i=0; i < ncomp; i++)
{
ij = comps[i];
S[i] = ko1*A[ij]+ko2*B[ij]+press*J*C_inv[ij];
}
}
void Mat_Hyper_Isotrop_General::getDdDp_UP (uint16 ncomp, const solidmech::tensorComponents* comps,
const double* C, const double press, double *Dd, double *Dp) {
double alpha[5];
double IC[3];
solidmech::IC_C(C, IC);
double _13I1C = 1.0/3.0*IC[0];
double _23I2C = 2.0/3.0*IC[1];
double J = IC[2];
double pp = pow(J,-2.0/3.0);
double oo = pow(J,-2);
double pppp = pp*pp;
double C_inv[6];
solidmech::invC_C(C, J, C_inv);
/*
AL_1 = 0
AL_2 = 1
AL_11 = 2
AL_12 = 3
AL_22 = 4
*/
W_second_derivatives(IC[0]*pp, IC[1]*pppp, 1.0, alpha);
double A[] = {I[0]-_13I1C*C_inv[0], I[1]-_13I1C*C_inv[1], I[2]-_13I1C*C_inv[2],
I[3]-_13I1C*C_inv[3], I[4]-_13I1C*C_inv[4], I[5]-_13I1C*C_inv[5]};
double B[] = {IC[0]*I[0]-C[0]-_23I2C*C_inv[0], IC[0]*I[1]-C[1]-_23I2C*C_inv[1], IC[0]*I[2]-C[2]-_23I2C*C_inv[2],
IC[0]*I[3]-C[3]-_23I2C*C_inv[3], IC[0]*I[4]-C[4]-_23I2C*C_inv[4], IC[0]*I[5]-C[5]-_23I2C*C_inv[5]};
double IIt[6][6] = {{-C_inv[M_XX]*C_inv[M_XX], -C_inv[M_XX]*C_inv[M_XY], -C_inv[M_XX]*C_inv[M_XZ], -C_inv[M_XY]*C_inv[M_XY], -C_inv[M_XY]*C_inv[M_XZ], -C_inv[M_XZ]*C_inv[M_XZ]},
// 1211 = 11*12, 1212 = 0.5*(11*22 + 12*12), 1213 = 0.5*(11*23+13*12), 1222 = 12*22, 1223 = 0.5*(12*23+13*22), 1233 = 13*23
{-C_inv[M_XX]*C_inv[M_XY], -0.5*(C_inv[M_XX]*C_inv[M_YY]+C_inv[M_XY]*C_inv[M_XY]), -0.5*(C_inv[M_XX]*C_inv[M_YZ]+C_inv[M_XZ]*C_inv[M_XY]), -C_inv[M_XY]*C_inv[M_YY], -0.5*(C_inv[M_XY]*C_inv[M_YZ]+C_inv[M_XZ]*C_inv[M_YY]), -C_inv[M_XZ]*C_inv[M_YZ]},
// 1311 = 11*13, 1312 = 0.5*(11*23+12*13), 1313 = 0.5*(11*33+13*13), 1322 = 12*23, 1323 = 0.5*(12*33+13*23), 1333 = 13*33
{-C_inv[M_XX]*C_inv[M_XZ], -0.5*(C_inv[M_XX]*C_inv[M_YZ]+C_inv[M_XY]*C_inv[M_XZ]), -0.5*(C_inv[M_XX]*C_inv[M_ZZ]+C_inv[M_XZ]*C_inv[M_XZ]), -C_inv[M_XY]*C_inv[M_YZ], -0.5*(C_inv[M_XY]*C_inv[M_ZZ]+C_inv[M_XZ]*C_inv[M_YZ]), -C_inv[M_XZ]*C_inv[M_ZZ]},
// 2211 = 12*12, 2212 = 12*22, 2213 = 12*23, 2222 = 22*22, 2223 = 22*23, 2233 = 23*23
{-C_inv[M_XY]*C_inv[M_XY], -C_inv[M_XY]*C_inv[M_YY], -C_inv[M_XY]*C_inv[M_YZ], -C_inv[M_YY]*C_inv[M_YY], -C_inv[M_YY]*C_inv[M_YZ], -C_inv[M_YZ]*C_inv[M_YZ]},
// 2311 = 12*13, 2312 = 0.5*(12*23+22*13), 2313 = 0.5*(12*33+23*13), 2322 = 22*23, 2323 = 0.5*(22*33+23*23), 2333 = 23*33
{-C_inv[M_XY]*C_inv[M_XZ], -0.5*(C_inv[M_XY]*C_inv[M_YZ]+C_inv[M_YY]*C_inv[M_XZ]), -0.5*(C_inv[M_XY]*C_inv[M_ZZ]+C_inv[M_YZ]*C_inv[M_XZ]), -C_inv[M_YY]*C_inv[M_YZ], -0.5*(C_inv[M_YY]*C_inv[M_ZZ]+C_inv[M_YZ]*C_inv[M_YZ]), -C_inv[M_YZ]*C_inv[M_ZZ]},
// 3311 = 13*13, 3312 = 13*23, 3313 = 13*33, 3322 = 23*23, 3323 = 23*33, 3333 = 33*33
{-C_inv[M_XZ]*C_inv[M_XZ], -C_inv[M_XZ]*C_inv[M_YZ], -C_inv[M_XZ]*C_inv[M_ZZ], -C_inv[M_YZ]*C_inv[M_YZ], -C_inv[M_YZ]*C_inv[M_ZZ], -C_inv[M_ZZ]*C_inv[M_ZZ]}
};
uint16 ind = 0;
solidmech::tensorComponents ij;
solidmech::tensorComponents kl;
for (uint16 i=0; i < ncomp; i++)
{
ij = comps[i];
for (uint16 j=i; j < ncomp; j++)
{
kl = comps[j];
Dd[ind]=4.0*alpha[AL_11]*pppp*A[ij]*A[kl]+4.0*alpha[AL_12]*oo*(B[ij]*A[kl]+A[ij]*B[kl])+4.0*alpha[AL_22]*pppp*pppp*B[ij]*B[kl] -
4.0/3.0*alpha[AL_1]*pp*(C_inv[ij]*A[kl]+A[ij]*C_inv[kl]+_13I1C*C_inv[ij]*C_inv[kl]+IC[0]*IIt[ij][kl]) -
8.0/3.0*alpha[AL_2]*pppp*(C_inv[ij]*B[kl]+B[ij]*C_inv[kl]+_23I2C*C_inv[ij]*C_inv[kl]-3.0/2.0*I[ij]*I[kl]+3.0/2.0*II[ij][kl]+IC[1]*IIt[ij][kl])+
press*J*(C_inv[ij]*C_inv[kl]+2*IIt[ij][kl]);
Dd[ind]=Dd[ind]/2.0;
ind++;
}
Dp[i] = J*C_inv[ij];
}
}
//---------------------------------------------------------
//------------------Mat_Comp_Neo_Hookean-------------------
//---------------------------------------------------------
void Mat_Comp_Neo_Hookean::W_first_derivatives (double I1, double I2, double I3, double* alpha)
{
alpha[AL_1] = 0.5*MC[C_G];
alpha[AL_2] = 0.0;
}
void Mat_Comp_Neo_Hookean::W_second_derivatives (double I1, double I2, double I3, double* alpha)
{
alpha[AL_1] = 0.5*MC[C_G];
alpha[AL_2] = 0.0;
alpha[AL_11] = 0.0;
alpha[AL_22] = 0.0;
alpha[AL_12] = 0.0;
}
double Mat_Comp_Neo_Hookean::W (double I1, double I2, double I3) {
return 0.5*MC[C_G]*(I1-3.0);
}
double Mat_Comp_Neo_Hookean::getK() {
return MC[C_K];
}
//---------------------------------------------------------
//-------------------Mat_Comp_Biderman---------------------
//---------------------------------------------------------
void Mat_Comp_Biderman::W_first_derivatives (double I1, double I2, double I3, double* alpha) {
alpha[AL_1] = MC[C_C10]+2*MC[C_C20]*(I1-3.0)+3*MC[C_C30]*(I1-3.0)*(I1-3.0);
alpha[AL_2] = MC[C_C01];
}
void Mat_Comp_Biderman::W_second_derivatives (double I1, double I2, double I3, double* alpha) {
alpha[AL_1] = MC[C_C10]+2*MC[C_C20]*(I1-3.0)+3*MC[C_C30]*(I1-3.0)*(I1-3.0);
alpha[AL_2] = MC[C_C01];
alpha[AL_11] = 2*MC[C_C20]+6*MC[C_C30]*(I1-3.0);
alpha[AL_22] = 0.0;
alpha[AL_12] = 0.0;
}
double Mat_Comp_Biderman::W (double I1, double I2, double I3) {
return MC[C_C10]*(I1-3.0) + MC[C_C20]*(I1-3.0)*(I1-3.0) + MC[C_C30]*(I1-3.0)*(I1-3.0)*(I1-3.0) + MC[C_C01]*(I2-3.0);
}
double Mat_Comp_Biderman::getK() {
return MC[C_K];
}
//---------------------------------------------------------
//-----------------Mat_Comp_MooneyRivlin-------------------
//---------------------------------------------------------
void Mat_Comp_MooneyRivlin::W_first_derivatives (double I1, double I2, double I3, double* alpha)
{
alpha[AL_1] = MC[C_C10];
alpha[AL_2] = MC[C_C01];
}
void Mat_Comp_MooneyRivlin::W_second_derivatives (double I1, double I2, double I3, double* alpha)
{
alpha[AL_1] = MC[C_C10];
alpha[AL_2] = MC[C_C01];
alpha[AL_11] = 0.0;
alpha[AL_22] = 0.0;
alpha[AL_12] = 0.0;
}
double Mat_Comp_MooneyRivlin::W (double I1, double I2, double I3) {
return MC[C_C10]*(I1-3.0) + MC[C_C01]*(I2-3.0);
}
double Mat_Comp_MooneyRivlin::getK() {
return MC[C_K];
}
} // namespace nla3d
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "materials/material.h"
#include "solidmech.h"
#include <Eigen/Dense>
namespace nla3d {
//----------------------------------------------//
//-----------Mat_Hyper_Isotrop_General----------//
//----------------------------------------------//
class Mat_Hyper_Isotrop_General : public Material {
public:
//Mat_Hyper_Isotrop_General () {
//}
// constants for derives in double array
enum mat_func_deriv {
AL_1 = 0,
AL_2 = 1,
AL_11 = 2,
AL_12 = 3,
AL_22 = 4
};
//for non linear U elements
void getS_U (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, double *S);
void getD_U (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, double *D);
//for nonlinear U-P elements
void getS_UP (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, const double press, double *S);
void getDdDp_UP (uint16 ncomp, const solidmech::tensorComponents* comps, const double* C, const double press, double *Dd, double *Dp);
virtual void W_first_derivatives (double I1, double I2, double I3, double *alpha) = 0;
virtual void W_second_derivatives (double I1, double I2, double I3, double *alpha) = 0;
virtual double W (double I1, double I2, double I3) = 0;
virtual double getK() = 0;
static const double II[6][6];
};
class Mat_Comp_Neo_Hookean : public Mat_Hyper_Isotrop_General
{
public:
enum Mat_Comp_Neo_Hookean_CONST {
C_G = 0,
C_K
};
Mat_Comp_Neo_Hookean () {
register_mat_const(2,"G","K");
name = "Neo-Hookean";
}
void W_first_derivatives (double I1, double I2, double I3, double *alpha);
void W_second_derivatives (double I1, double I2, double I3, double *alpha);
double W (double I1, double I2, double I3);
double getK();
};
class Mat_Comp_Biderman : public Mat_Hyper_Isotrop_General
{
public:
enum Mat_Comp_Biderman_CONST {
C_C10 = 0,
C_C20,
C_C30,
C_C01,
C_K
};
Mat_Comp_Biderman () {
register_mat_const(5,"C10","C20","C30","C01","K");
name = "Biderman";
}
void W_first_derivatives (double I1, double I2, double I3, double *alpha);
void W_second_derivatives (double I1, double I2, double I3, double *alpha);
double W (double I1, double I2, double I3);
double getK();
};
class Mat_Comp_MooneyRivlin : public Mat_Hyper_Isotrop_General
{
public:
enum Mat_Comp_MooneyRivlin_CONST {
C_C10 = 0,
C_C01,
C_K
};
Mat_Comp_MooneyRivlin () {
register_mat_const(3,"C10","C01","K");
name = "Money-Rivlin";
}
void W_first_derivatives (double I1, double I2, double I3, double *alpha);
void W_second_derivatives (double I1, double I2, double I3, double *alpha);
double W (double I1, double I2, double I3);
double getK();
};
} // namespace nla3d
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="nla3d" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin/Debug/nla3d" prefix_auto="1" extension_auto="1" />
<Option working_dir="" />
<Option object_output="obj/Debug/" />
<Option type="2" />
<Option compiler="gcc" />
<Option createDefFile="1" />
<Compiler>
<Add option="-Wall" />
<Add option="-m64" />
<Add option="-g" />
<Add option="-ftemplate-backtrace-limit=0" />
<Add option="-fPIC" />
<Add directory="../../cuda_nla3d" />
<Add directory="../math" />
<Add directory="../nla3d" />
<Add directory="../easylogging" />
<Add directory="/usr/include/eigen3" />
</Compiler>
<Linker>
<Add option="-m64" />
</Linker>
</Target>
<Target title="Release">
<Option output="bin/Release/nla3d" prefix_auto="1" extension_auto="1" />
<Option working_dir="" />
<Option object_output="obj/Release/" />
<Option type="2" />
<Option compiler="gcc" />
<Option createDefFile="1" />
<Compiler>
<Add option="-O2" />
<Add option="-Wall" />
<Add option="-m64" />
<Add option="-ftemplate-backtrace-limit=0" />
<Add option="-fPIC" />
<Add directory="../../cuda_nla3d" />
<Add directory="../math" />
<Add directory="../nla3d" />
<Add directory="../easylogging" />
<Add directory="/usr/include/eigen3" />
</Compiler>
<Linker>
<Add option="-s" />
<Add option="-m64" />
</Linker>
</Target>
</Build>
<Unit filename="Dof.cpp" />
<Unit filename="Dof.h" />
<Unit filename="FEComponent.cpp" />
<Unit filename="FEComponent.h" />
<Unit filename="FESolver.cpp" />
<Unit filename="FESolver.h" />
<Unit filename="FEStorage.cpp" />
<Unit filename="FEStorage.h" />
<Unit filename="Mpc.cpp" />
<Unit filename="Mpc.h" />
<Unit filename="Node.cpp" />
<Unit filename="Node.h" />
<Unit filename="PostProcessor.cpp" />
<Unit filename="PostProcessor.h" />
<Unit filename="ReactionProcessor.cpp" />
<Unit filename="ReactionProcessor.h" />
<Unit filename="VtkProcessor.cpp" />
<Unit filename="VtkProcessor.h" />
<Unit filename="elements/ElementFactory.cpp" />
<Unit filename="elements/ElementFactory.h" />
<Unit filename="elements/PLANE41.cpp" />
<Unit filename="elements/PLANE41.h" />
<Unit filename="elements/QUADTH.cpp" />
<Unit filename="elements/QUADTH.h" />
<Unit filename="elements/SOLID81.cpp" />
<Unit filename="elements/SOLID81.h" />
<Unit filename="elements/TETRA0.cpp" />
<Unit filename="elements/TETRA0.h" />
<Unit filename="elements/TETRA1.cpp" />
<Unit filename="elements/TETRA1.h" />
<Unit filename="elements/TRIANGLE4.cpp" />
<Unit filename="elements/TRIANGLE4.h" />
<Unit filename="elements/TRUSS3.cpp" />
<Unit filename="elements/TRUSS3.h" />
<Unit filename="elements/element.cpp" />
<Unit filename="elements/element.h" />
<Unit filename="elements/isoparametric.cpp" />
<Unit filename="elements/isoparametric.h" />
<Unit filename="elements/query.h" />
<Unit filename="materials/MaterialFactory.cpp" />
<Unit filename="materials/MaterialFactory.h" />
<Unit filename="materials/material.cpp" />
<Unit filename="materials/material.h" />
<Unit filename="materials/materials_hyperelastic.cpp" />
<Unit filename="materials/materials_hyperelastic.h" />
<Unit filename="solidmech.cpp" />
<Unit filename="solidmech.h" />
<Extensions>
<code_completion />
<envvars />
<lib_finder disable_auto="1" />
<debugger />
</Extensions>
</Project>
</CodeBlocks_project_file>
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#include "solidmech.h"
namespace nla3d {
namespace solidmech {
double J_C (const double* C) {
return sqrt( C[M_XX]*(C[M_YY]*C[M_ZZ]-C[M_YZ]*C[M_YZ])
- C[M_XY]*(C[M_XY]*C[M_ZZ]-C[M_YZ]*C[M_XZ])
+ C[M_XZ]*(C[M_XY]*C[M_YZ]-C[M_YY]*C[M_XZ]) );
}
void invC_C (const double* C, const double J, double* invC) {
double oo = 1.0/(J*J);
invC[M_XX] = oo*(C[M_YY]*C[M_ZZ]-C[M_YZ]*C[M_YZ]);
invC[M_XY] = oo*(C[M_XZ]*C[M_YZ]-C[M_XY]*C[M_ZZ]);
invC[M_XZ] = oo*(C[M_XY]*C[M_YZ]-C[M_XZ]*C[M_YY]);
invC[M_YY] = oo*(C[M_XX]*C[M_ZZ]-C[M_XZ]*C[M_XZ]);
invC[M_YZ] = oo*(C[M_XY]*C[M_XZ]-C[M_XX]*C[M_YZ]);
invC[M_ZZ] = oo*(C[M_XX]*C[M_YY]-C[M_XY]*C[M_XY]);
}
void E_C (const double* C, double* E) {
E[M_XX] += (C[M_XX]-1.0)*0.5;
E[M_XY] += C[M_XY]*0.5;
E[M_XZ] += C[M_XZ]*0.5;
E[M_YY] += (C[M_YY]-1.0)*0.5;
E[M_YZ] += C[M_YZ]*0.5;
E[M_ZZ] += (C[M_ZZ]-1.0)*0.5;
}
void IC_C (const double* C, double* IC) {
IC[0] = C[M_XX]+C[M_YY]+C[M_ZZ];
//IC2 with star = 0.5 * IC[0]^2 - C:C
IC[1] = C[M_XX]*C[M_YY] + C[M_YY]*C[M_ZZ] + C[M_XX]*C[M_ZZ] - C[M_XY]*C[M_XY] - C[M_YZ]*C[M_YZ] - C[M_XZ]*C[M_XZ];
IC[2] = J_C(C);
}
} // namespace nla3d
} // namespace solidmech
// This file is a part of nla3d project. For information about authors and
// licensing go to project's repository on github:
// https://github.com/dmitryikh/nla3d
#pragma once
#include "sys.h"
namespace nla3d {
namespace solidmech {
// labels for tensor components stored in 1-dim array
enum tensorComponents {
M_XX = 0,
M_XY = 1,
M_XZ = 2,
M_YY = 3,
M_YZ = 4,
M_ZZ = 5
};
// global order of tensor components in 1-dim array
const tensorComponents defaultTensorComponents[] = {M_XX, M_XY, M_XZ, M_YY, M_YZ, M_ZZ};
const int LeviCivita[3][3][3] = {
{ {0,0,0},
{0,0,1},
{0,-1,0} },
{ {0,0,-1},
{0,0,0},
{1,0,0} },
{ {0,1,0},
{-1,0,0},
{0,0,0} } };
const int I[3][3] = {
{1,0,0},
{0,1,0},
{0,0,1}};
const char* const labelsTensorComponent[]={"XX","XY", "XZ", "YY", "YZ", "ZZ"};
// 3x3 symmetric tensor is stored in 1-dim array like order:
// C = [CXX, CXY, CXZ, CYY, CYZ, CZZ]
//
// get volume deformation from right Cauchy–Green deformation tensor C = F^T * F
double J_C(const double* C);
// get inverse tensor from right Cauchy–Green deformation tensor C = F^T * F
void invC_C(const double* C, const double J, double* C_inv);
// get Green strain tensor (Largrangian) E = 0.5* (C - I)
void E_C(const double* C, double* E);
// get 3 Invariants from right Cauchy-Green deformation tensor C = F^T * F
void IC_C(const double* C, double* IC);
} // namespace solidmech
} //namespace nla3d
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment