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: Alessandro Tasora, Radu Serban
13 // =============================================================================
14 
15 #include <algorithm>
16 #include <fstream>
17 #include <functional>
18 #include <iostream>
19 #include <sstream>
20 #include <string>
21 
22 #include "chrono/core/ChMath.h"
23 #include "chrono/physics/ChLoad.h"
24 #include "chrono/physics/ChObject.h"
25 #include "chrono/physics/ChSystem.h"
26 
27 #include "chrono/fea/ChElementTetraCorot_4.h"
28 #include "chrono/fea/ChMesh.h"
29 #include "chrono/fea/ChNodeFEAxyz.h"
30 #include "chrono/fea/ChNodeFEAxyzrot.h"
31 
32 namespace chrono {
33 namespace fea {
34 
ChMesh(const ChMesh & other)35 ChMesh::ChMesh(const ChMesh& other) : ChIndexedNodes(other) {
36     vnodes = other.vnodes;
37     velements = other.velements;
38 
39     n_dofs = other.n_dofs;
40     n_dofs_w = other.n_dofs_w;
41 
42     vcontactsurfaces = other.vcontactsurfaces;
43     vmeshsurfaces = other.vmeshsurfaces;
44 
45     automatic_gravity_load = other.automatic_gravity_load;
46     num_points_gravity = other.num_points_gravity;
47 
48     ncalls_internal_forces = 0;
49     ncalls_KRMload = 0;
50 }
51 
SetupInitial()52 void ChMesh::SetupInitial() {
53     n_dofs = 0;
54     n_dofs_w = 0;
55 
56     for (unsigned int i = 0; i < vnodes.size(); i++) {
57         if (!vnodes[i]->GetFixed()) {
58             //    - count the degrees of freedom
59             n_dofs += vnodes[i]->Get_ndof_x();
60             n_dofs_w += vnodes[i]->Get_ndof_w();
61         }
62     }
63 
64     for (unsigned int i = 0; i < velements.size(); i++) {
65         //    - precompute matrices, such as the [Kl] local stiffness of each element, if needed, etc.
66         velements[i]->SetupInitial(GetSystem());
67     }
68 }
69 
Relax()70 void ChMesh::Relax() {
71     for (unsigned int i = 0; i < vnodes.size(); i++) {
72         //    - "relaxes" the structure by setting all X0 = 0, and null speeds
73         vnodes[i]->Relax();
74     }
75 }
76 
SetNoSpeedNoAcceleration()77 void ChMesh::SetNoSpeedNoAcceleration() {
78     for (unsigned int i = 0; i < vnodes.size(); i++) {
79         //    -  set null speeds, null accelerations
80         vnodes[i]->SetNoSpeedNoAcceleration();
81     }
82 }
83 
AddNode(std::shared_ptr<ChNodeFEAbase> m_node)84 void ChMesh::AddNode(std::shared_ptr<ChNodeFEAbase> m_node) {
85     m_node->SetIndex(static_cast<unsigned int>(vnodes.size()) + 1);
86     vnodes.push_back(m_node);
87 
88     // If the mesh is already added to a system, mark the system uninitialized and out-of-date
89     if (system) {
90         system->is_initialized = false;
91         system->is_updated = false;
92     }
93 }
94 
AddElement(std::shared_ptr<ChElementBase> m_elem)95 void ChMesh::AddElement(std::shared_ptr<ChElementBase> m_elem) {
96     velements.push_back(m_elem);
97 
98     // If the mesh is already added to a system, mark the system uninitialized and out-of-date
99     if (system) {
100         system->is_initialized = false;
101         system->is_updated = false;
102     }
103 }
104 
ClearElements()105 void ChMesh::ClearElements() {
106     velements.clear();
107     vcontactsurfaces.clear();
108 
109     // If the mesh is already added to a system, mark the system out-of-date
110     if (system) {
111         system->is_updated = false;
112     }
113 }
114 
ClearNodes()115 void ChMesh::ClearNodes() {
116     velements.clear();
117     vnodes.clear();
118     vcontactsurfaces.clear();
119 
120     // If the mesh is already added to a system, mark the system out-of-date
121     if (system) {
122         system->is_updated = false;
123     }
124 }
125 
AddContactSurface(std::shared_ptr<ChContactSurface> m_surf)126 void ChMesh::AddContactSurface(std::shared_ptr<ChContactSurface> m_surf) {
127     m_surf->SetMesh(this);
128     vcontactsurfaces.push_back(m_surf);
129 }
130 
ClearContactSurfaces()131 void ChMesh::ClearContactSurfaces() {
132     vcontactsurfaces.clear();
133 }
134 
AddMeshSurface(std::shared_ptr<ChMeshSurface> m_surf)135 void ChMesh::AddMeshSurface(std::shared_ptr<ChMeshSurface> m_surf) {
136     m_surf->SetMesh(this);
137     vmeshsurfaces.push_back(m_surf);
138 }
139 
140 /// This recomputes the number of DOFs, constraints, as well as state offsets of contained items
Setup()141 void ChMesh::Setup() {
142     n_dofs = 0;
143     n_dofs_w = 0;
144 
145     for (unsigned int i = 0; i < vnodes.size(); i++) {
146         // Set node offsets in state vectors (based on the offsets of the containing mesh)
147         vnodes[i]->NodeSetOffset_x(GetOffset_x() + n_dofs);
148         vnodes[i]->NodeSetOffset_w(GetOffset_w() + n_dofs_w);
149         // Count the degrees of freedom (consider only nodes that are not fixed)
150         if (!vnodes[i]->GetFixed()) {
151             n_dofs += vnodes[i]->Get_ndof_x();
152             n_dofs_w += vnodes[i]->Get_ndof_w();
153         }
154     }
155 }
156 
157 // Updates all time-dependant variables, if any...
158 // Ex: maybe the elasticity can increase in time, etc.
Update(double m_time,bool update_assets)159 void ChMesh::Update(double m_time, bool update_assets) {
160     // Parent class update
161     ChIndexedNodes::Update(m_time, update_assets);
162 
163     for (unsigned int i = 0; i < velements.size(); i++) {
164         //    - update auxiliary stuff, ex. update element's rotation matrices if corotational..
165         velements[i]->Update();
166     }
167 }
168 
SyncCollisionModels()169 void ChMesh::SyncCollisionModels() {
170     for (unsigned int j = 0; j < vcontactsurfaces.size(); j++) {
171         vcontactsurfaces[j]->SurfaceSyncCollisionModels();
172     }
173 }
174 
AddCollisionModelsToSystem()175 void ChMesh::AddCollisionModelsToSystem() {
176     assert(GetSystem());
177     SyncCollisionModels();
178     for (unsigned int j = 0; j < vcontactsurfaces.size(); j++) {
179         vcontactsurfaces[j]->SurfaceAddCollisionModelsToSystem(GetSystem());
180     }
181 }
182 
RemoveCollisionModelsFromSystem()183 void ChMesh::RemoveCollisionModelsFromSystem() {
184     assert(GetSystem());
185     for (unsigned int j = 0; j < vcontactsurfaces.size(); j++) {
186         vcontactsurfaces[j]->SurfaceRemoveCollisionModelsFromSystem(GetSystem());
187     }
188 }
189 
190 //// STATE BOOKKEEPING FUNCTIONS
191 
IntStateGather(const unsigned int off_x,ChState & x,const unsigned int off_v,ChStateDelta & v,double & T)192 void ChMesh::IntStateGather(const unsigned int off_x,
193                             ChState& x,
194                             const unsigned int off_v,
195                             ChStateDelta& v,
196                             double& T) {
197     unsigned int local_off_x = 0;
198     unsigned int local_off_v = 0;
199     for (unsigned int j = 0; j < vnodes.size(); j++) {
200         if (!vnodes[j]->GetFixed()) {
201             vnodes[j]->NodeIntStateGather(off_x + local_off_x, x, off_v + local_off_v, v, T);
202             local_off_x += vnodes[j]->Get_ndof_x();
203             local_off_v += vnodes[j]->Get_ndof_w();
204         }
205     }
206 
207     T = GetChTime();
208 }
209 
IntStateScatter(const unsigned int off_x,const ChState & x,const unsigned int off_v,const ChStateDelta & v,const double T,bool full_update)210 void ChMesh::IntStateScatter(const unsigned int off_x,
211                              const ChState& x,
212                              const unsigned int off_v,
213                              const ChStateDelta& v,
214                              const double T,
215                              bool full_update) {
216     unsigned int local_off_x = 0;
217     unsigned int local_off_v = 0;
218     for (unsigned int j = 0; j < vnodes.size(); j++) {
219         if (!vnodes[j]->GetFixed()) {
220             vnodes[j]->NodeIntStateScatter(off_x + local_off_x, x, off_v + local_off_v, v, T);
221             local_off_x += vnodes[j]->Get_ndof_x();
222             local_off_v += vnodes[j]->Get_ndof_w();
223         }
224     }
225 
226     Update(T, full_update);
227 }
228 
IntStateGatherAcceleration(const unsigned int off_a,ChStateDelta & a)229 void ChMesh::IntStateGatherAcceleration(const unsigned int off_a, ChStateDelta& a) {
230     unsigned int local_off_a = 0;
231     for (unsigned int j = 0; j < vnodes.size(); j++) {
232         if (!vnodes[j]->GetFixed()) {
233             vnodes[j]->NodeIntStateGatherAcceleration(off_a + local_off_a, a);
234             local_off_a += vnodes[j]->Get_ndof_w();
235         }
236     }
237 }
238 
IntStateScatterAcceleration(const unsigned int off_a,const ChStateDelta & a)239 void ChMesh::IntStateScatterAcceleration(const unsigned int off_a, const ChStateDelta& a) {
240     unsigned int local_off_a = 0;
241     for (unsigned int j = 0; j < vnodes.size(); j++) {
242         if (!vnodes[j]->GetFixed()) {
243             vnodes[j]->NodeIntStateScatterAcceleration(off_a + local_off_a, a);
244             local_off_a += vnodes[j]->Get_ndof_w();
245         }
246     }
247 }
248 
IntStateIncrement(const unsigned int off_x,ChState & x_new,const ChState & x,const unsigned int off_v,const ChStateDelta & Dv)249 void ChMesh::IntStateIncrement(const unsigned int off_x,
250                                ChState& x_new,
251                                const ChState& x,
252                                const unsigned int off_v,
253                                const ChStateDelta& Dv) {
254     unsigned int local_off_x = 0;
255     unsigned int local_off_v = 0;
256     for (unsigned int j = 0; j < vnodes.size(); j++) {
257         if (!vnodes[j]->GetFixed()) {
258             vnodes[j]->NodeIntStateIncrement(off_x + local_off_x, x_new, x, off_v + local_off_v, Dv);
259             local_off_x += vnodes[j]->Get_ndof_x();
260             local_off_v += vnodes[j]->Get_ndof_w();
261         }
262     }
263     for (unsigned int ie = 0; ie < velements.size(); ie++) {
264         velements[ie]->EleDoIntegration();
265     }
266 }
267 
IntLoadResidual_F(const unsigned int off,ChVectorDynamic<> & R,const double c)268 void ChMesh::IntLoadResidual_F(const unsigned int off, ChVectorDynamic<>& R, const double c) {
269     // nodes applied forces
270     unsigned int local_off_v = 0;
271     for (unsigned int j = 0; j < vnodes.size(); j++) {
272         if (!vnodes[j]->GetFixed()) {
273             vnodes[j]->NodeIntLoadResidual_F(off + local_off_v, R, c);
274             local_off_v += vnodes[j]->Get_ndof_w();
275         }
276     }
277 
278     int nthreads = GetSystem()->nthreads_chrono;
279 
280     // elements internal forces
281     timer_internal_forces.start();
282     //***PARALLEL FOR***, must use omp atomic to avoid race condition in writing to R
283 #pragma omp parallel for schedule(dynamic, 4) num_threads(nthreads)
284     for (int ie = 0; ie < velements.size(); ie++) {
285         velements[ie]->EleIntLoadResidual_F(R, c);
286     }
287     timer_internal_forces.stop();
288     ncalls_internal_forces++;
289 
290     // elements gravity forces
291     if (automatic_gravity_load) {
292         //***PARALLEL FOR***, must use omp atomic to avoid race condition in writing to R
293 #pragma omp parallel for schedule(dynamic, 4) num_threads(nthreads)
294         for (int ie = 0; ie < velements.size(); ie++) {
295             velements[ie]->EleIntLoadResidual_F_gravity(R, GetSystem()->Get_G_acc(), c);
296         }
297     }
298 
299     // nodes gravity forces
300     local_off_v = 0;
301     if (automatic_gravity_load && this->system) {
302         //#pragma omp parallel for schedule(dynamic, 4) num_threads(nthreads)
303         //***PARALLEL FOR***, (no need here to use omp atomic to avoid race condition in writing to R)
304         for (int in = 0; in < vnodes.size(); in++) {
305             if (!vnodes[in]->GetFixed()) {
306                 if (auto mnode = std::dynamic_pointer_cast<ChNodeFEAxyz>(vnodes[in])) {
307                     ChVector<> fg = c * mnode->GetMass() * this->system->Get_G_acc();
308                     R.segment(off + local_off_v, 3) += fg.eigen();
309                 }
310                 // odd stuf here... the ChNodeFEAxyzrot is not inherited from ChNodeFEAxyz so must trap it too:
311                 if (auto mnode = std::dynamic_pointer_cast<ChNodeFEAxyzrot>(vnodes[in])) {
312                     ChVector<> fg = c * mnode->GetMass() * this->system->Get_G_acc();
313                     R.segment(off + local_off_v, 3) += fg.eigen();
314                 }
315                 local_off_v += vnodes[in]->Get_ndof_w();
316             }
317         }
318     }
319 }
320 
ComputeMassProperties(double & mass,ChVector<> & com,ChMatrix33<> & inertia)321 void ChMesh::ComputeMassProperties(double& mass,           // ChMesh object mass
322                                    ChVector<>& com,        // ChMesh center of gravity
323                                    ChMatrix33<>& inertia)  // ChMesh inertia tensor
324 {
325     mass = 0;
326     com = ChVector<>(0);
327     inertia = ChMatrix33<>(1);
328 
329     // Initialize all nodal total masses to zero
330     for (unsigned int j = 0; j < vnodes.size(); j++) {
331         vnodes[j]->m_TotalMass = 0.0;
332     }
333     // Loop over all elements and calculate contribution to nodal mass
334     for (unsigned int ie = 0; ie < velements.size(); ie++) {
335         velements[ie]->ComputeNodalMass();
336     }
337     // Loop over all the nodes of the mesh to obtain total object mass
338     for (unsigned int j = 0; j < vnodes.size(); j++) {
339         mass += vnodes[j]->m_TotalMass;
340     }
341 }
IntLoadResidual_Mv(const unsigned int off,ChVectorDynamic<> & R,const ChVectorDynamic<> & w,const double c)342 void ChMesh::IntLoadResidual_Mv(const unsigned int off,      ///< offset in R residual
343                                 ChVectorDynamic<>& R,        ///< result: the R residual, R += c*M*v
344                                 const ChVectorDynamic<>& w,  ///< the w vector
345                                 const double c               ///< a scaling factor
346 ) {
347     // nodal masses
348     unsigned int local_off_v = 0;
349     for (unsigned int j = 0; j < vnodes.size(); j++) {
350         if (!vnodes[j]->GetFixed()) {
351             vnodes[j]->NodeIntLoadResidual_Mv(off + local_off_v, R, w, c);
352             local_off_v += vnodes[j]->Get_ndof_w();
353         }
354     }
355 
356     // internal masses
357     for (unsigned int ie = 0; ie < velements.size(); ie++) {
358         velements[ie]->EleIntLoadResidual_Mv(R, w, c);
359     }
360 }
361 
IntToDescriptor(const unsigned int off_v,const ChStateDelta & v,const ChVectorDynamic<> & R,const unsigned int off_L,const ChVectorDynamic<> & L,const ChVectorDynamic<> & Qc)362 void ChMesh::IntToDescriptor(const unsigned int off_v,
363                              const ChStateDelta& v,
364                              const ChVectorDynamic<>& R,
365                              const unsigned int off_L,
366                              const ChVectorDynamic<>& L,
367                              const ChVectorDynamic<>& Qc) {
368     unsigned int local_off_v = 0;
369     for (unsigned int j = 0; j < vnodes.size(); j++) {
370         if (!vnodes[j]->GetFixed()) {
371             vnodes[j]->NodeIntToDescriptor(off_v + local_off_v, v, R);
372             local_off_v += vnodes[j]->Get_ndof_w();
373         }
374     }
375 }
376 
IntFromDescriptor(const unsigned int off_v,ChStateDelta & v,const unsigned int off_L,ChVectorDynamic<> & L)377 void ChMesh::IntFromDescriptor(const unsigned int off_v,
378                                ChStateDelta& v,
379                                const unsigned int off_L,
380                                ChVectorDynamic<>& L) {
381     unsigned int local_off_v = 0;
382     for (unsigned int j = 0; j < vnodes.size(); j++) {
383         if (!vnodes[j]->GetFixed()) {
384             vnodes[j]->NodeIntFromDescriptor(off_v + local_off_v, v);
385             local_off_v += vnodes[j]->Get_ndof_w();
386         }
387     }
388 }
389 
390 //// SOLVER FUNCTIONS
391 
InjectKRMmatrices(ChSystemDescriptor & mdescriptor)392 void ChMesh::InjectKRMmatrices(ChSystemDescriptor& mdescriptor) {
393     for (unsigned int ie = 0; ie < velements.size(); ie++)
394         velements[ie]->InjectKRMmatrices(mdescriptor);
395 }
396 
KRMmatricesLoad(double Kfactor,double Rfactor,double Mfactor)397 void ChMesh::KRMmatricesLoad(double Kfactor, double Rfactor, double Mfactor) {
398     int nthreads = GetSystem()->nthreads_chrono;
399 
400     timer_KRMload.start();
401 #pragma omp parallel for num_threads(nthreads)
402     for (int ie = 0; ie < velements.size(); ie++)
403         velements[ie]->KRMmatricesLoad(Kfactor, Rfactor, Mfactor);
404     timer_KRMload.stop();
405     ncalls_KRMload++;
406 }
407 
VariablesFbReset()408 void ChMesh::VariablesFbReset() {
409     for (unsigned int ie = 0; ie < vnodes.size(); ie++)
410         vnodes[ie]->VariablesFbReset();
411 }
412 
VariablesFbLoadForces(double factor)413 void ChMesh::VariablesFbLoadForces(double factor) {
414     // applied nodal forces
415     for (unsigned int in = 0; in < vnodes.size(); in++)
416         vnodes[in]->VariablesFbLoadForces(factor);
417 
418     // internal forces
419     for (unsigned int ie = 0; ie < velements.size(); ie++)
420         velements[ie]->VariablesFbLoadInternalForces(factor);
421 }
422 
VariablesQbLoadSpeed()423 void ChMesh::VariablesQbLoadSpeed() {
424     for (unsigned int ie = 0; ie < vnodes.size(); ie++)
425         vnodes[ie]->VariablesQbLoadSpeed();
426 }
427 
VariablesFbIncrementMq()428 void ChMesh::VariablesFbIncrementMq() {
429     // nodal masses
430     for (unsigned int ie = 0; ie < vnodes.size(); ie++)
431         vnodes[ie]->VariablesFbIncrementMq();
432 
433     // internal masses
434     for (unsigned int ie = 0; ie < velements.size(); ie++)
435         velements[ie]->VariablesFbIncrementMq();
436 }
437 
VariablesQbSetSpeed(double step)438 void ChMesh::VariablesQbSetSpeed(double step) {
439     for (unsigned int ie = 0; ie < vnodes.size(); ie++)
440         vnodes[ie]->VariablesQbSetSpeed(step);
441 }
442 
VariablesQbIncrementPosition(double step)443 void ChMesh::VariablesQbIncrementPosition(double step) {
444     for (unsigned int ie = 0; ie < vnodes.size(); ie++)
445         vnodes[ie]->VariablesQbIncrementPosition(step);
446 }
447 
InjectVariables(ChSystemDescriptor & mdescriptor)448 void ChMesh::InjectVariables(ChSystemDescriptor& mdescriptor) {
449     for (unsigned int ie = 0; ie < vnodes.size(); ie++)
450         vnodes[ie]->InjectVariables(mdescriptor);
451 }
452 
453 }  // end namespace fea
454 }  // end namespace chrono
455