1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Andrea Favali, Alessandro Tasora, Radu Serban
13 // =============================================================================
14 
15 #include "chrono/fea/ChElementTetraCorot_4.h"
16 
17 namespace chrono {
18 namespace fea {
19 
ChElementTetraCorot_4()20 ChElementTetraCorot_4::ChElementTetraCorot_4() : Volume(0) {
21     nodes.resize(4);
22     this->MatrB.setZero(6, 12);
23     this->StiffnessMatrix.setZero(12, 12);
24 }
25 
~ChElementTetraCorot_4()26 ChElementTetraCorot_4::~ChElementTetraCorot_4() {}
27 
SetNodes(std::shared_ptr<ChNodeFEAxyz> nodeA,std::shared_ptr<ChNodeFEAxyz> nodeB,std::shared_ptr<ChNodeFEAxyz> nodeC,std::shared_ptr<ChNodeFEAxyz> nodeD)28 void ChElementTetraCorot_4::SetNodes(std::shared_ptr<ChNodeFEAxyz> nodeA,
29                                      std::shared_ptr<ChNodeFEAxyz> nodeB,
30                                      std::shared_ptr<ChNodeFEAxyz> nodeC,
31                                      std::shared_ptr<ChNodeFEAxyz> nodeD) {
32     nodes[0] = nodeA;
33     nodes[1] = nodeB;
34     nodes[2] = nodeC;
35     nodes[3] = nodeD;
36     std::vector<ChVariables*> mvars;
37     mvars.push_back(&nodes[0]->Variables());
38     mvars.push_back(&nodes[1]->Variables());
39     mvars.push_back(&nodes[2]->Variables());
40     mvars.push_back(&nodes[3]->Variables());
41     Kmatr.SetVariables(mvars);
42 }
43 
Update()44 void ChElementTetraCorot_4::Update() {
45     // parent class update:
46     ChElementGeneric::Update();
47     // always keep updated the rotation matrix A:
48     this->UpdateRotation();
49 }
50 
ShapeFunctions(ShapeVector & N,double r,double s,double t)51 void ChElementTetraCorot_4::ShapeFunctions(ShapeVector& N, double r, double s, double t) {
52     N(0) = 1.0 - r - s - t;
53     N(1) = r;
54     N(2) = s;
55     N(3) = t;
56 }
57 
GetStateBlock(ChVectorDynamic<> & mD)58 void ChElementTetraCorot_4::GetStateBlock(ChVectorDynamic<>& mD) {
59     mD.setZero(this->GetNdofs());
60     mD.segment(0, 3) = (A.transpose() * nodes[0]->pos - nodes[0]->GetX0()).eigen();
61     mD.segment(3, 3) = (A.transpose() * nodes[1]->pos - nodes[1]->GetX0()).eigen();
62     mD.segment(6, 3) = (A.transpose() * nodes[2]->pos - nodes[2]->GetX0()).eigen();
63     mD.segment(9, 3) = (A.transpose() * nodes[3]->pos - nodes[3]->GetX0()).eigen();
64 }
65 
ComputeVolume()66 double ChElementTetraCorot_4::ComputeVolume() {
67     ChVector<> B1, C1, D1;
68     B1.Sub(nodes[1]->pos, nodes[0]->pos);
69     C1.Sub(nodes[2]->pos, nodes[0]->pos);
70     D1.Sub(nodes[3]->pos, nodes[0]->pos);
71     ChMatrixDynamic<> M(3, 3);
72     M.col(0) = B1.eigen();
73     M.col(1) = C1.eigen();
74     M.col(2) = D1.eigen();
75     M.transposeInPlace();
76     Volume = std::abs(M.determinant() / 6);
77     return Volume;
78 }
79 
ComputeStiffnessMatrix()80 void ChElementTetraCorot_4::ComputeStiffnessMatrix() {
81     // M = [ X0_0 X0_1 X0_2 X0_3 ] ^-1
82     //     [ 1    1    1    1    ]
83     ChMatrixNM<double, 4, 4> tmp;
84     tmp.block(0, 0, 3, 1) = nodes[0]->GetX0().eigen();
85     tmp.block(0, 1, 3, 1) = nodes[1]->GetX0().eigen();
86     tmp.block(0, 2, 3, 1) = nodes[2]->GetX0().eigen();
87     tmp.block(0, 3, 3, 1) = nodes[3]->GetX0().eigen();
88     tmp.row(3).setConstant(1.0);
89     mM = tmp.inverse();
90 
91     ////MatrB.Reset(6, 12);
92     MatrB(0) = mM(0);
93     MatrB(3) = mM(4);
94     MatrB(6) = mM(8);
95     MatrB(9) = mM(12);
96     MatrB(13) = mM(1);
97     MatrB(16) = mM(5);
98     MatrB(19) = mM(9);
99     MatrB(22) = mM(13);
100     MatrB(26) = mM(2);
101     MatrB(29) = mM(6);
102     MatrB(32) = mM(10);
103     MatrB(35) = mM(14);
104     MatrB(36) = mM(1);
105     MatrB(37) = mM(0);
106     MatrB(39) = mM(5);
107     MatrB(40) = mM(4);
108     MatrB(42) = mM(9);
109     MatrB(43) = mM(8);
110     MatrB(45) = mM(13);
111     MatrB(46) = mM(12);
112     MatrB(49) = mM(2);
113     MatrB(50) = mM(1);
114     MatrB(52) = mM(6);
115     MatrB(53) = mM(5);
116     MatrB(55) = mM(10);
117     MatrB(56) = mM(9);
118     MatrB(58) = mM(14);
119     MatrB(59) = mM(13);
120     MatrB(60) = mM(2);
121     MatrB(62) = mM(0);
122     MatrB(63) = mM(6);
123     MatrB(65) = mM(4);
124     MatrB(66) = mM(10);
125     MatrB(68) = mM(8);
126     MatrB(69) = mM(14);
127     MatrB(71) = mM(12);
128 
129     StiffnessMatrix = Volume * MatrB.transpose() * Material->Get_StressStrainMatrix() * MatrB;
130 
131     // ***TEST*** SYMMETRIZE TO AVOID ROUNDOFF ASYMMETRY
132     for (int row = 0; row < StiffnessMatrix.rows() - 1; ++row)
133         for (int col = row + 1; col < StiffnessMatrix.cols(); ++col)
134             StiffnessMatrix(row, col) = StiffnessMatrix(col, row);
135 
136     double max_err = 0;
137     int err_r = -1;
138     int err_c = -1;
139     for (int row = 0; row < StiffnessMatrix.rows(); ++row)
140         for (int col = 0; col < StiffnessMatrix.cols(); ++col) {
141             double diff = fabs(StiffnessMatrix(row, col) - StiffnessMatrix(col, row));
142             if (diff > max_err) {
143                 max_err = diff;
144                 err_r = row;
145                 err_c = col;
146             }
147         }
148     if (max_err > 1e-10)
149         GetLog() << "NONSYMMETRIC local stiffness matrix! err " << max_err << " at " << err_r << "," << err_c << "\n";
150 }
151 
SetupInitial(ChSystem * system)152 void ChElementTetraCorot_4::SetupInitial(ChSystem* system) {
153     ComputeVolume();
154     ComputeStiffnessMatrix();
155 }
156 
UpdateRotation()157 void ChElementTetraCorot_4::UpdateRotation() {
158     // P = [ p_0  p_1  p_2  p_3 ]
159     //     [ 1    1    1    1   ]
160     ChMatrixNM<double, 4, 4> P;
161     P.block(0, 0, 3, 1) = nodes[0]->pos.eigen();
162     P.block(0, 1, 3, 1) = nodes[1]->pos.eigen();
163     P.block(0, 2, 3, 1) = nodes[2]->pos.eigen();
164     P.block(0, 3, 3, 1) = nodes[3]->pos.eigen();
165     P.row(3).setConstant(1.0);
166 
167     ChMatrix33<double> F;
168     // F=P*mM (only upper-left 3x3 block!)
169     double sum;
170     for (int colres = 0; colres < 3; ++colres)
171         for (int row = 0; row < 3; ++row) {
172             sum = 0;
173             for (int col = 0; col < 4; ++col)
174                 sum += (P(row, col)) * (mM(col, colres));
175             F(row, colres) = sum;
176         }
177     ChMatrix33<> S;
178     double det = ChPolarDecomposition<>::Compute(F, this->A, S, 1E-6);
179     if (det < 0)
180         this->A *= -1.0;
181 
182     // GetLog() << "FEM rotation: \n" << A << "\n" ;
183 }
184 
ComputeKRMmatricesGlobal(ChMatrixRef H,double Kfactor,double Rfactor,double Mfactor)185 void ChElementTetraCorot_4::ComputeKRMmatricesGlobal(ChMatrixRef H, double Kfactor, double Rfactor, double Mfactor) {
186     assert((H.rows() == 12) && (H.cols() == 12));
187 
188     // warp the local stiffness matrix K in order to obtain global
189     // tangent stiffness CKCt:
190     ChMatrixDynamic<> CK(12, 12);
191     ChMatrixDynamic<> CKCt(12, 12);  // the global, corotated, K matrix
192     ChMatrixCorotation::ComputeCK(StiffnessMatrix, this->A, 4, CK);
193     ChMatrixCorotation::ComputeKCt(CK, this->A, 4, CKCt);
194 
195     // ***TEST*** SYMMETRIZE TO AVOID ROUNDOFF ASYMMETRY
196     for (int row = 0; row < CKCt.rows() - 1; ++row)
197         for (int col = row + 1; col < CKCt.cols(); ++col)
198             CKCt(row, col) = CKCt(col, row);
199 
200     //***DEBUG***
201     double max_err = 0;
202     int err_r = -1;
203     int err_c = -1;
204     for (int row = 0; row < StiffnessMatrix.rows(); ++row)
205         for (int col = 0; col < StiffnessMatrix.cols(); ++col) {
206             double diff = fabs(StiffnessMatrix(row, col) - StiffnessMatrix(col, row));
207             if (diff > max_err) {
208                 max_err = diff;
209                 err_r = row;
210                 err_c = col;
211             }
212         }
213     if (max_err > 1e-10)
214         GetLog() << "NONSYMMETRIC local stiffness matrix! err " << max_err << " at " << err_r << "," << err_c << "\n";
215     max_err = 0;
216     err_r = -1;
217     err_c = -1;
218     double maxval = 0;
219     for (int row = 0; row < CKCt.rows(); ++row)
220         for (int col = 0; col < CKCt.cols(); ++col) {
221             double diff = fabs(CKCt(row, col) - CKCt(col, row));
222             if (diff > max_err) {
223                 max_err = diff;
224                 err_r = row;
225                 err_c = col;
226             }
227             if (CKCt(row, col) > maxval)
228                 maxval = CKCt(row, col);
229         }
230     if (max_err > 1e-10)
231         GetLog() << "NONSYMMETRIC corotated matrix! err " << max_err << " at " << err_r << "," << err_c
232                  << ",   maxval=" << maxval << "\n";
233 
234     // For K stiffness matrix and R damping matrix:
235     double mkfactor = Kfactor + Rfactor * this->GetMaterial()->Get_RayleighDampingK();
236     H = mkfactor * CKCt;
237 
238     // For M mass matrix:
239     if (Mfactor) {
240         double lumped_node_mass = (this->GetVolume() * this->Material->Get_density()) / 4.0;
241         for (int id = 0; id < 12; id++) {
242             double amfactor = Mfactor + Rfactor * this->GetMaterial()->Get_RayleighDampingM();
243             H(id, id) += amfactor * lumped_node_mass;
244         }
245     }
246     //***TO DO*** better per-node lumping, or 12x12 consistent mass matrix.
247 }
248 
ComputeInternalForces(ChVectorDynamic<> & Fi)249 void ChElementTetraCorot_4::ComputeInternalForces(ChVectorDynamic<>& Fi) {
250     assert(Fi.size() == 12);
251 
252     // set up vector of nodal displacements (in local element system) u_l = R*p - p0
253     ChVectorDynamic<> displ(12);
254     this->GetStateBlock(displ);  // nodal displacements, local
255 
256     // [local Internal Forces] = [Klocal] * displ + [Rlocal] * displ_dt
257     ChVectorDynamic<> FiK_local = StiffnessMatrix * displ;
258 
259     displ.segment(0, 3) = (A.transpose() * nodes[0]->pos_dt).eigen();  // nodal speeds, local
260     displ.segment(3, 3) = (A.transpose() * nodes[1]->pos_dt).eigen();
261     displ.segment(6, 3) = (A.transpose() * nodes[2]->pos_dt).eigen();
262     displ.segment(9, 3) = (A.transpose() * nodes[3]->pos_dt).eigen();
263     ChMatrixDynamic<> FiR_local = Material->Get_RayleighDampingK() * StiffnessMatrix * displ;
264 
265     double lumped_node_mass = (this->GetVolume() * this->Material->Get_density()) / 4.0;
266     displ *= lumped_node_mass * this->Material->Get_RayleighDampingM();
267     FiR_local += displ;
268 
269     //***TO DO*** better per-node lumping, or 12x12 consistent mass matrix.
270 
271     FiK_local += FiR_local;
272     FiK_local *= -1.0;
273 
274     // Fi = C * Fi_local  with C block-diagonal rotations A
275     ChMatrixCorotation::ComputeCK(FiK_local, this->A, 4, Fi);
276 }
277 
GetStrain()278 ChStrainTensor<> ChElementTetraCorot_4::GetStrain() {
279     // set up vector of nodal displacements (in local element system) u_l = R*p - p0
280     ChVectorDynamic<> displ(12);
281     this->GetStateBlock(displ);  // nodal displacements, local
282 
283     ChStrainTensor<> mstrain = MatrB * displ;
284     return mstrain;
285 }
286 
GetStress()287 ChStressTensor<> ChElementTetraCorot_4::GetStress() {
288     ChStressTensor<> mstress = this->Material->Get_StressStrainMatrix() * this->GetStrain();
289     return mstress;
290 }
291 
ComputeNodalMass()292 void ChElementTetraCorot_4::ComputeNodalMass() {
293     nodes[0]->m_TotalMass += this->GetVolume() * this->Material->Get_density() / 4.0;
294     nodes[1]->m_TotalMass += this->GetVolume() * this->Material->Get_density() / 4.0;
295     nodes[2]->m_TotalMass += this->GetVolume() * this->Material->Get_density() / 4.0;
296     nodes[3]->m_TotalMass += this->GetVolume() * this->Material->Get_density() / 4.0;
297 }
298 
LoadableGetStateBlock_x(int block_offset,ChState & mD)299 void ChElementTetraCorot_4::LoadableGetStateBlock_x(int block_offset, ChState& mD) {
300     mD.segment(block_offset + 0, 3) = nodes[0]->GetPos().eigen();
301     mD.segment(block_offset + 3, 3) = nodes[1]->GetPos().eigen();
302     mD.segment(block_offset + 6, 3) = nodes[2]->GetPos().eigen();
303     mD.segment(block_offset + 9, 3) = nodes[3]->GetPos().eigen();
304 }
305 
LoadableGetStateBlock_w(int block_offset,ChStateDelta & mD)306 void ChElementTetraCorot_4::LoadableGetStateBlock_w(int block_offset, ChStateDelta& mD) {
307     mD.segment(block_offset + 0, 3) = nodes[0]->GetPos_dt().eigen();
308     mD.segment(block_offset + 3, 3) = nodes[1]->GetPos_dt().eigen();
309     mD.segment(block_offset + 6, 3) = nodes[2]->GetPos_dt().eigen();
310     mD.segment(block_offset + 9, 3) = nodes[3]->GetPos_dt().eigen();
311 }
312 
LoadableStateIncrement(const unsigned int off_x,ChState & x_new,const ChState & x,const unsigned int off_v,const ChStateDelta & Dv)313 void ChElementTetraCorot_4::LoadableStateIncrement(const unsigned int off_x,
314                                                    ChState& x_new,
315                                                    const ChState& x,
316                                                    const unsigned int off_v,
317                                                    const ChStateDelta& Dv) {
318     for (int i = 0; i < 4; ++i) {
319         nodes[i]->NodeIntStateIncrement(off_x + i * 3, x_new, x, off_v + i * 3, Dv);
320     }
321 }
322 
LoadableGetVariables(std::vector<ChVariables * > & mvars)323 void ChElementTetraCorot_4::LoadableGetVariables(std::vector<ChVariables*>& mvars) {
324     for (int i = 0; i < nodes.size(); ++i)
325         mvars.push_back(&this->nodes[i]->Variables());
326 }
327 
ComputeNF(const double U,const double V,const double W,ChVectorDynamic<> & Qi,double & detJ,const ChVectorDynamic<> & F,ChVectorDynamic<> * state_x,ChVectorDynamic<> * state_w)328 void ChElementTetraCorot_4::ComputeNF(const double U,
329                                       const double V,
330                                       const double W,
331                                       ChVectorDynamic<>& Qi,
332                                       double& detJ,
333                                       const ChVectorDynamic<>& F,
334                                       ChVectorDynamic<>* state_x,
335                                       ChVectorDynamic<>* state_w) {
336     // evaluate shape functions (in compressed vector), btw. not dependent on state
337     // note: U,V,W in 0..1 range, thanks to IsTetrahedronIntegrationNeeded() {return true;}
338     ShapeVector N;
339     this->ShapeFunctions(N, U, V, W);
340 
341     detJ = 6 * this->GetVolume();
342 
343     Qi(0) = N(0) * F(0);
344     Qi(1) = N(0) * F(1);
345     Qi(2) = N(0) * F(2);
346     Qi(3) = N(1) * F(0);
347     Qi(4) = N(1) * F(1);
348     Qi(5) = N(1) * F(2);
349     Qi(6) = N(2) * F(0);
350     Qi(7) = N(2) * F(1);
351     Qi(8) = N(2) * F(2);
352     Qi(9) = N(3) * F(0);
353     Qi(10) = N(3) * F(1);
354     Qi(11) = N(3) * F(2);
355 }
356 
357 // -----------------------------------------------------------------------------
358 
ChElementTetraCorot_4_P()359 ChElementTetraCorot_4_P::ChElementTetraCorot_4_P() : Volume(0) {
360     nodes.resize(4);
361     this->MatrB.setZero(3, 4);
362     this->StiffnessMatrix.setZero(4, 4);
363 }
364 
SetNodes(std::shared_ptr<ChNodeFEAxyzP> nodeA,std::shared_ptr<ChNodeFEAxyzP> nodeB,std::shared_ptr<ChNodeFEAxyzP> nodeC,std::shared_ptr<ChNodeFEAxyzP> nodeD)365 void ChElementTetraCorot_4_P::SetNodes(std::shared_ptr<ChNodeFEAxyzP> nodeA,
366                                        std::shared_ptr<ChNodeFEAxyzP> nodeB,
367                                        std::shared_ptr<ChNodeFEAxyzP> nodeC,
368                                        std::shared_ptr<ChNodeFEAxyzP> nodeD) {
369     nodes[0] = nodeA;
370     nodes[1] = nodeB;
371     nodes[2] = nodeC;
372     nodes[3] = nodeD;
373     std::vector<ChVariables*> mvars;
374     mvars.push_back(&nodes[0]->Variables());
375     mvars.push_back(&nodes[1]->Variables());
376     mvars.push_back(&nodes[2]->Variables());
377     mvars.push_back(&nodes[3]->Variables());
378     Kmatr.SetVariables(mvars);
379 }
380 
Update()381 void ChElementTetraCorot_4_P::Update() {
382     // parent class update:
383     ChElementGeneric::Update();
384     // always keep updated the rotation matrix A:
385     this->UpdateRotation();
386 }
387 
ShapeFunctions(ShapeVector & N,double z0,double z1,double z2)388 void ChElementTetraCorot_4_P::ShapeFunctions(ShapeVector& N, double z0, double z1, double z2) {
389     N(0) = z0;
390     N(1) = z1;
391     N(2) = z2;
392     N(3) = 1.0 - z0 - z1 - z2;
393 }
394 
GetStateBlock(ChVectorDynamic<> & mD)395 void ChElementTetraCorot_4_P::GetStateBlock(ChVectorDynamic<>& mD) {
396     mD.setZero(this->GetNdofs());
397     mD(0) = nodes[0]->GetP();
398     mD(1) = nodes[1]->GetP();
399     mD(2) = nodes[2]->GetP();
400     mD(3) = nodes[3]->GetP();
401 }
402 
ComputeVolume()403 double ChElementTetraCorot_4_P::ComputeVolume() {
404     ChVector<> B1, C1, D1;
405     B1.Sub(nodes[1]->GetPos(), nodes[0]->GetPos());
406     C1.Sub(nodes[2]->GetPos(), nodes[0]->GetPos());
407     D1.Sub(nodes[3]->GetPos(), nodes[0]->GetPos());
408     ChMatrixDynamic<> M(3, 3);
409     M.col(0) = B1.eigen();
410     M.col(1) = C1.eigen();
411     M.col(2) = D1.eigen();
412     M.transposeInPlace();
413     Volume = std::abs(M.determinant() / 6);
414     return Volume;
415 }
416 
ComputeStiffnessMatrix()417 void ChElementTetraCorot_4_P::ComputeStiffnessMatrix() {
418     // M = [ X0_0 X0_1 X0_2 X0_3 ] ^-1
419     //     [ 1    1    1    1    ]
420     ChMatrixNM<double, 4, 4> tmp;
421     tmp.block(0, 0, 3, 1) = nodes[0]->GetPos().eigen();
422     tmp.block(0, 1, 3, 1) = nodes[1]->GetPos().eigen();
423     tmp.block(0, 2, 3, 1) = nodes[2]->GetPos().eigen();
424     tmp.block(0, 3, 3, 1) = nodes[3]->GetPos().eigen();
425     tmp.row(3).setConstant(1.0);
426     mM = tmp.inverse();
427 
428     ////MatrB.Reset(3, 4);
429     MatrB(0, 0) = mM(0);
430     MatrB(0, 1) = mM(4);
431     MatrB(0, 2) = mM(8);
432     MatrB(0, 3) = mM(12);
433     MatrB(1, 0) = mM(1);
434     MatrB(1, 1) = mM(5);
435     MatrB(1, 2) = mM(9);
436     MatrB(1, 3) = mM(13);
437     MatrB(2, 0) = mM(2);
438     MatrB(2, 1) = mM(6);
439     MatrB(2, 2) = mM(10);
440     MatrB(2, 3) = mM(14);
441 
442     StiffnessMatrix = Volume * MatrB.transpose() * Material->Get_ConstitutiveMatrix() * MatrB;
443 }
444 
SetupInitial(ChSystem * system)445 void ChElementTetraCorot_4_P::SetupInitial(ChSystem* system) {
446     ComputeVolume();
447     ComputeStiffnessMatrix();
448 }
449 
UpdateRotation()450 void ChElementTetraCorot_4_P::UpdateRotation() {
451     // P = [ p_0  p_1  p_2  p_3 ]
452     //     [ 1    1    1    1   ]
453     ChMatrixNM<double, 4, 4> P;
454     P.block(0, 0, 3, 1) = nodes[0]->GetPos().eigen();
455     P.block(0, 1, 3, 1) = nodes[1]->GetPos().eigen();
456     P.block(0, 2, 3, 1) = nodes[2]->GetPos().eigen();
457     P.block(0, 3, 3, 1) = nodes[3]->GetPos().eigen();
458     P.row(3).setConstant(1.0);
459 
460     ChMatrix33<double> F;
461     // F=P*mM (only upper-left 3x3 block!)
462     double sum;
463     for (int colres = 0; colres < 3; ++colres)
464         for (int row = 0; row < 3; ++row) {
465             sum = 0;
466             for (int col = 0; col < 4; ++col)
467                 sum += (P(row, col)) * (mM(col, colres));
468             F(row, colres) = sum;
469         }
470     ChMatrix33<> S;
471     double det = ChPolarDecomposition<>::Compute(F, this->A, S, 1E-6);
472     if (det < 0)
473         this->A *= -1.0;
474 }
475 
ComputeKRMmatricesGlobal(ChMatrixRef H,double Kfactor,double Rfactor,double Mfactor)476 void ChElementTetraCorot_4_P::ComputeKRMmatricesGlobal(ChMatrixRef H, double Kfactor, double Rfactor, double Mfactor) {
477     assert((H.rows() == 4) && (H.cols() == 4));
478 
479     // For K  matrix (jacobian d/dT of  c dT/dt + div [C] grad T = f )
480     H = Kfactor * StiffnessMatrix;
481 
482     // For R  matrix: (jacobian d/d\dot(T) of  c dT/dt + div [C] grad T = f )
483     if (Rfactor)
484         if (this->GetMaterial()->Get_DtMultiplier()) {
485             // lumped approx. integration of c
486             double lumped_node_c = (this->GetVolume() * this->GetMaterial()->Get_DtMultiplier()) / 4.0;
487             for (int id = 0; id < 4; id++) {
488                 H(id, id) += Rfactor * lumped_node_c;
489             }
490         }
491     //***TO DO*** better per-node lumping, or 4x4 consistent c integration as per mass matrices.
492 
493     // For M mass matrix: NONE in Poisson equation c dT/dt + div [C] grad T = f
494 }
495 
ComputeInternalForces(ChVectorDynamic<> & Fi)496 void ChElementTetraCorot_4_P::ComputeInternalForces(ChVectorDynamic<>& Fi) {
497     assert(Fi.size() == 4);
498 
499     // set up vector of nodal fields
500     ChVectorDynamic<> displ(4);
501     this->GetStateBlock(displ);
502 
503     // [local Internal Forces] = [Klocal] * P
504     ChVectorDynamic<> FiK_local = StiffnessMatrix * displ;
505 
506     //***TO DO*** derivative terms? + [Rlocal] * P_dt ???? ***NO because Poisson  rho dP/dt + div [C] grad P = 0
507 
508     FiK_local *= -1.0;
509 
510     // ChMatrixCorotation::ComputeCK(FiK_local, this->A, 4, Fi);  ***corotation NOT NEEDED
511 
512     Fi = FiK_local;
513 }
514 
GetPgradient()515 ChVectorN<double, 3> ChElementTetraCorot_4_P::GetPgradient() {
516     // set up vector of nodal displacements (in local element system) u_l = R*p - p0
517     ChVectorDynamic<> displ(4);
518     this->GetStateBlock(displ);
519 
520     return MatrB * displ;
521 }
522 
LoadableGetStateBlock_x(int block_offset,ChState & mD)523 void ChElementTetraCorot_4_P::LoadableGetStateBlock_x(int block_offset, ChState& mD) {
524     mD(block_offset) = this->nodes[0]->GetP();
525     mD(block_offset + 1) = this->nodes[1]->GetP();
526     mD(block_offset + 2) = this->nodes[2]->GetP();
527     mD(block_offset + 3) = this->nodes[3]->GetP();
528 }
529 
LoadableGetStateBlock_w(int block_offset,ChStateDelta & mD)530 void ChElementTetraCorot_4_P::LoadableGetStateBlock_w(int block_offset, ChStateDelta& mD) {
531     mD(block_offset) = this->nodes[0]->GetP_dt();
532     mD(block_offset + 1) = this->nodes[1]->GetP_dt();
533     mD(block_offset + 2) = this->nodes[2]->GetP_dt();
534     mD(block_offset + 3) = this->nodes[3]->GetP_dt();
535 }
536 
LoadableStateIncrement(const unsigned int off_x,ChState & x_new,const ChState & x,const unsigned int off_v,const ChStateDelta & Dv)537 void ChElementTetraCorot_4_P::LoadableStateIncrement(const unsigned int off_x,
538                                                      ChState& x_new,
539                                                      const ChState& x,
540                                                      const unsigned int off_v,
541                                                      const ChStateDelta& Dv) {
542     for (int i = 0; i < 4; ++i) {
543         nodes[i]->NodeIntStateIncrement(off_x + i * 1, x_new, x, off_v + i * 1, Dv);
544     }
545 }
546 
LoadableGetVariables(std::vector<ChVariables * > & mvars)547 void ChElementTetraCorot_4_P::LoadableGetVariables(std::vector<ChVariables*>& mvars) {
548     for (int i = 0; i < nodes.size(); ++i)
549         mvars.push_back(&this->nodes[i]->Variables());
550 }
551 
ComputeNF(const double U,const double V,const double W,ChVectorDynamic<> & Qi,double & detJ,const ChVectorDynamic<> & F,ChVectorDynamic<> * state_x,ChVectorDynamic<> * state_w)552 void ChElementTetraCorot_4_P::ComputeNF(const double U,
553                                         const double V,
554                                         const double W,
555                                         ChVectorDynamic<>& Qi,
556                                         double& detJ,
557                                         const ChVectorDynamic<>& F,
558                                         ChVectorDynamic<>* state_x,
559                                         ChVectorDynamic<>* state_w) {
560     // evaluate shape functions (in compressed vector), btw. not dependant on state
561     ShapeVector N;
562     this->ShapeFunctions(N, U, V,
563                          W);  // note: U,V,W in 0..1 range, thanks to IsTetrahedronIntegrationNeeded() {return true;}
564 
565     detJ = 6 * this->GetVolume();
566 
567     Qi(0) = N(0) * F(0);
568     Qi(1) = N(1) * F(0);
569     Qi(2) = N(2) * F(0);
570     Qi(3) = N(3) * F(0);
571 }
572 
573 }  // end namespace fea
574 }  // end namespace chrono
575