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: Radu Serban, Antonio Recuero
13 // =============================================================================
14 //
15 // ANCF toroidal tire.
16 // This is a concrete ANCF tire class which uses a semi-toroidal tire mesh
17 // composed of single-layer ANCF shell elements.
18 //
19 // =============================================================================
20 
21 #include "chrono_vehicle/wheeled_vehicle/tire/ANCFToroidalTire.h"
22 
23 using namespace chrono::fea;
24 
25 namespace chrono {
26 namespace vehicle {
27 
ANCFToroidalTire(const std::string & name)28 ANCFToroidalTire::ANCFToroidalTire(const std::string& name)
29     : ChANCFTire(name),
30       m_rim_radius(0.35),
31       m_height(0.195),
32       m_thickness(0.014),
33       m_div_circumference(60),
34       m_div_width(12),
35       m_default_pressure(320.0e3),
36       m_alpha(0.15) {
37     // default contact material
38     m_mat = chrono_types::make_shared<ChMaterialSurfaceSMC>();
39 }
40 
CreateMesh(const ChFrameMoving<> & wheel_frame,VehicleSide side)41 void ANCFToroidalTire::CreateMesh(const ChFrameMoving<>& wheel_frame, VehicleSide side) {
42     // Create an isotropic material (shared by all elements)
43     auto mat = chrono_types::make_shared<ChMaterialShellANCF>(500, 9.0e7, 0.3);
44 
45     // Create the mesh nodes.
46     // The nodes are first created in the wheel local frame, assuming Y as the tire axis,
47     // and are then transformed to the global frame.
48     for (int i = 0; i < m_div_circumference; i++) {
49         double phi = (CH_C_2PI * i) / m_div_circumference;
50 
51         for (int j = 0; j <= m_div_width; j++) {
52             double theta = -CH_C_PI_2 + (CH_C_PI * j) / m_div_width;
53 
54             double x = (m_rim_radius + m_height * cos(theta)) * cos(phi);
55             double y = m_height * sin(theta);
56             double z = (m_rim_radius + m_height * cos(theta)) * sin(phi);
57             ChVector<> loc = wheel_frame.TransformPointLocalToParent(ChVector<>(x, y, z));
58 
59             double nx = cos(theta) * cos(phi);
60             double ny = sin(theta);
61             double nz = cos(theta) * sin(phi);
62             ChVector<> dir = wheel_frame.TransformDirectionLocalToParent(ChVector<>(nx, ny, nz));
63 
64             auto node = chrono_types::make_shared<ChNodeFEAxyzD>(loc, dir);
65             node->SetMass(0);
66             m_mesh->AddNode(node);
67         }
68     }
69 
70     // Element dimensions
71     double dz = m_thickness;
72     double dx = CH_C_2PI * (m_rim_radius + m_height) / (2 * m_div_circumference);
73     double dy = CH_C_PI * m_height / m_div_width;
74 
75     // Create the ANCF shell elements
76     for (int i = 0; i < m_div_circumference; i++) {
77         for (int j = 0; j < m_div_width; j++) {
78             // Adjacent nodes
79             int inode0, inode1, inode2, inode3;
80             inode1 = j + i * (m_div_width + 1);
81             inode2 = j + 1 + i * (m_div_width + 1);
82             if (i == m_div_circumference - 1) {
83                 inode0 = j;
84                 inode3 = j + 1;
85             } else {
86                 inode0 = j + (i + 1) * (m_div_width + 1);
87                 inode3 = j + 1 + (i + 1) * (m_div_width + 1);
88             }
89 
90             auto node0 = std::dynamic_pointer_cast<ChNodeFEAxyzD>(m_mesh->GetNode(inode0));
91             auto node1 = std::dynamic_pointer_cast<ChNodeFEAxyzD>(m_mesh->GetNode(inode1));
92             auto node2 = std::dynamic_pointer_cast<ChNodeFEAxyzD>(m_mesh->GetNode(inode2));
93             auto node3 = std::dynamic_pointer_cast<ChNodeFEAxyzD>(m_mesh->GetNode(inode3));
94 
95             // Create the element and set its nodes.
96             auto element = chrono_types::make_shared<ChElementShellANCF_3423>();
97             element->SetNodes(node0, node1, node2, node3);
98 
99             // Set element dimensions
100             element->SetDimensions(dx, dy);
101 
102             // Add a single layers with a fiber angle of 0 degrees.
103             element->AddLayer(dz, 0 * CH_C_DEG_TO_RAD, mat);
104 
105             // Set other element properties
106             element->SetAlphaDamp(m_alpha);
107 
108             // Add element to mesh
109             m_mesh->AddElement(element);
110         }
111     }
112 }
113 
GetConnectedNodes() const114 std::vector<std::shared_ptr<ChNodeFEAbase>> ANCFToroidalTire::GetConnectedNodes() const {
115     std::vector<std::shared_ptr<fea::ChNodeFEAbase>> nodes;
116 
117     for (int i = 0; i < m_div_circumference; i++) {
118         for (int j = 0; j <= m_div_width; j++) {
119             int index = j + i * (m_div_width + 1);
120             if (index % (m_div_width + 1) == 0) {
121                 nodes.push_back(std::dynamic_pointer_cast<ChNodeFEAbase>(m_mesh->GetNode(index)));
122                 nodes.push_back(std::dynamic_pointer_cast<ChNodeFEAbase>(m_mesh->GetNode(index + m_div_width)));
123             }
124         }
125     }
126 
127     return nodes;
128 }
129 
CreateContactMaterial()130 void ANCFToroidalTire::CreateContactMaterial() {
131     m_contact_mat = m_mat;
132 }
133 
134 }  // end namespace vehicle
135 }  // end namespace chrono
136