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