1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2021 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: Radu Serban
13 // =============================================================================
14 // Face of a hexahedron element
15 // =============================================================================
16 
17 #ifndef CH_HEXAHEDRON_FACE_H
18 #define CH_HEXAHEDRON_FACE_H
19 
20 #include "chrono/fea/ChElementHexahedron.h"
21 
22 namespace chrono {
23 namespace fea {
24 
25 /// @addtogroup fea_elements
26 /// @{
27 
28 /// Face of a hexahedron-shaped element.
29 /// Corner nodes, obtainable with GetNodeN(), are in counterclockwise order seen from the outside.
30 /// <pre>
31 ///         v
32 ///         ^
33 /// 3 o-----+-----o 2
34 ///   |     |     |
35 /// --+-----+-----+-> u
36 ///   |     |     |
37 /// 0 o-----+-----o 1
38 /// </pre>
39 class ChApi ChHexahedronFace : public ChLoadableUV {
40   public:
41     /// Construct the specified face (0 <= id <= 5) on the given hexahedral element.
ChHexahedronFace(std::shared_ptr<ChElementHexahedron> element,char id)42     ChHexahedronFace(std::shared_ptr<ChElementHexahedron> element, char id) : m_face_id(id), m_element(element) {}
43 
~ChHexahedronFace()44     ~ChHexahedronFace() {}
45 
46     // Get the specified face node (0 <= i <= 3).
GetNodeN(int i)47     std::shared_ptr<ChNodeFEAxyz> GetNodeN(int i) const {
48         static int iface0[] = {0, 3, 2, 1};
49         static int iface1[] = {0, 1, 5, 4};
50         static int iface2[] = {1, 2, 6, 5};
51         static int iface3[] = {2, 3, 7, 6};
52         static int iface4[] = {3, 0, 4, 7};
53         static int iface5[] = {4, 5, 6, 7};
54 
55         switch (m_face_id) {
56             case 0:
57                 return m_element->GetHexahedronNode(iface0[i]);
58             case 1:
59                 return m_element->GetHexahedronNode(iface1[i]);
60             case 2:
61                 return m_element->GetHexahedronNode(iface2[i]);
62             case 3:
63                 return m_element->GetHexahedronNode(iface3[i]);
64             case 4:
65                 return m_element->GetHexahedronNode(iface4[i]);
66             case 5:
67                 return m_element->GetHexahedronNode(iface5[i]);
68         }
69         return nullptr;
70     }
71 
72     /// Fills the N shape function vector (size 4).
ShapeFunctions(ChVectorN<double,4> & N,double x,double y)73     void ShapeFunctions(ChVectorN<double, 4>& N, double x, double y) {
74         N(0) = 0.25 * (1 - x) * (1 - y);
75         N(1) = 0.25 * (1 + x) * (1 - y);
76         N(2) = 0.25 * (1 + x) * (1 + y);
77         N(3) = 0.25 * (1 - x) * (1 + y);
78     }
79 
80     // Functions for ChLoadable interface
81 
82     /// Get the number of DOFs affected by this element (position part).
LoadableGet_ndof_x()83     virtual int LoadableGet_ndof_x() override { return 4 * 3; }
84 
85     /// Get the number of DOFs affected by this element (speed part).
LoadableGet_ndof_w()86     virtual int LoadableGet_ndof_w() override { return 4 * 3; }
87 
88     /// Get all the DOFs packed in a single vector (position part).
LoadableGetStateBlock_x(int block_offset,ChState & mD)89     virtual void LoadableGetStateBlock_x(int block_offset, ChState& mD) override {
90         mD.segment(block_offset + 0, 3) = GetNodeN(0)->GetPos().eigen();
91         mD.segment(block_offset + 3, 3) = GetNodeN(1)->GetPos().eigen();
92         mD.segment(block_offset + 6, 3) = GetNodeN(2)->GetPos().eigen();
93         mD.segment(block_offset + 9, 3) = GetNodeN(3)->GetPos().eigen();
94     }
95 
96     /// Get all the DOFs packed in a single vector (speed part).
LoadableGetStateBlock_w(int block_offset,ChStateDelta & mD)97     virtual void LoadableGetStateBlock_w(int block_offset, ChStateDelta& mD) override {
98         mD.segment(block_offset + 0, 3) = GetNodeN(0)->GetPos_dt().eigen();
99         mD.segment(block_offset + 3, 3) = GetNodeN(1)->GetPos_dt().eigen();
100         mD.segment(block_offset + 6, 3) = GetNodeN(2)->GetPos_dt().eigen();
101         mD.segment(block_offset + 9, 3) = GetNodeN(3)->GetPos_dt().eigen();
102     }
103 
104     /// Increment all DOFs using a delta.
LoadableStateIncrement(const unsigned int off_x,ChState & x_new,const ChState & x,const unsigned int off_v,const ChStateDelta & Dv)105     virtual void LoadableStateIncrement(const unsigned int off_x,
106                                         ChState& x_new,
107                                         const ChState& x,
108                                         const unsigned int off_v,
109                                         const ChStateDelta& Dv) override {
110         for (int i = 0; i < 4; ++i) {
111             GetNodeN(i)->NodeIntStateIncrement(off_x + i * 3, x_new, x, off_v + i * 3, Dv);
112         }
113     }
114 
115     /// Number of coordinates in the interpolated field: here the {x,y,z} displacement.
Get_field_ncoords()116     virtual int Get_field_ncoords() override { return 3; }
117 
118     /// Get the number of DOFs sub-blocks.
GetSubBlocks()119     virtual int GetSubBlocks() override { return 4; }
120 
121     /// Get the offset of the specified sub-block of DOFs in global vector.
GetSubBlockOffset(int nblock)122     virtual unsigned int GetSubBlockOffset(int nblock) override { return GetNodeN(nblock)->NodeGetOffset_w(); }
123 
124     /// Get the size of the specified sub-block of DOFs in global vector.
GetSubBlockSize(int nblock)125     virtual unsigned int GetSubBlockSize(int nblock) override { return 3; }
126 
127     /// Check if the specified sub-block of DOFs is active.
IsSubBlockActive(int nblock)128     virtual bool IsSubBlockActive(int nblock) const override { return !GetNodeN(nblock)->GetFixed(); }
129 
130     /// Get the pointers to the contained ChVariables, appending to the mvars vector.
LoadableGetVariables(std::vector<ChVariables * > & mvars)131     virtual void LoadableGetVariables(std::vector<ChVariables*>& mvars) override {
132         for (int i = 0; i < 4; ++i)
133             mvars.push_back(&GetNodeN(i)->Variables());
134     };
135 
136     /// Evaluate N'*F , where N is some type of shape function evaluated at U,V coordinates of the surface, each ranging
137     /// in -1..+1 F is a load, N'*F is the resulting generalized load. Returns also det[J] with J=[dx/du,..], which may
138     /// be useful in Gauss quadrature.
ComputeNF(const double U,const double V,ChVectorDynamic<> & Qi,double & detJ,const ChVectorDynamic<> & F,ChVectorDynamic<> * state_x,ChVectorDynamic<> * state_w)139     virtual void ComputeNF(const double U,              ///< parametric coordinate in surface
140                            const double V,              ///< parametric coordinate in surface
141                            ChVectorDynamic<>& Qi,       ///< result of N'*F, maybe with offset block_offset
142                            double& detJ,                ///< det[J]
143                            const ChVectorDynamic<>& F,  ///< Input F vector, size is = n.field coords.
144                            ChVectorDynamic<>* state_x,  ///< if != 0, update state (pos. part) to this, then evaluate Q
145                            ChVectorDynamic<>* state_w   ///< if != 0, update state (speed part) to this, then evaluate Q
146                            ) override {
147         ChVectorN<double, 4> N;
148         ShapeFunctions(N, U, V);
149 
150         //***TODO*** exact det of jacobian at u,v
151         detJ = ((GetNodeN(0)->GetPos() - GetNodeN(1)->GetPos()) - (GetNodeN(2)->GetPos() - GetNodeN(3)->GetPos()))
152                    .Length() *
153                ((GetNodeN(1)->GetPos() - GetNodeN(2)->GetPos()) - (GetNodeN(3)->GetPos() - GetNodeN(0)->GetPos()))
154                    .Length();
155         // (approximate detJ, ok only for rectangular face)
156 
157         for (int i = 0; i < 4; i++) {
158             Qi.segment(3 * i, 3) = N(i) * F.segment(0, 3);
159         }
160     }
161 
162     /// If true, use quadrature over u,v in [0..1] range as triangle volumetric coords.
IsTriangleIntegrationNeeded()163     virtual bool IsTriangleIntegrationNeeded() override { return false; }
164 
165     /// Get the normal to the surface at the parametric coordinate u,v.
166     /// Normal must be considered pointing outside in case the surface is a boundary to a volume.
ComputeNormal(const double U,const double V)167     virtual ChVector<> ComputeNormal(const double U, const double V) override {
168         ChVector<> p0 = GetNodeN(0)->GetPos();
169         ChVector<> p1 = GetNodeN(1)->GetPos();
170         ChVector<> p2 = GetNodeN(2)->GetPos();
171         return Vcross(p1 - p0, p2 - p0).GetNormalized();
172     }
173 
174   private:
175     char m_face_id;                                  ///< id of the face on the hexahedron
176     std::shared_ptr<ChElementHexahedron> m_element;  ///< associated hexahedron element
177 };
178 
179 /// @} fea_elements
180 
181 }  // namespace fea
182 }  // namespace chrono
183 
184 #endif
185