1 // ============================================================================= 2 // PROJECT CHRONO - http://projectchrono.org 3 // 4 // Copyright (c) 2020 projectchrono.org 5 // All right 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 // 15 // Base class for a vehicle co-simulation node. 16 // 17 // ============================================================================= 18 19 #ifndef CH_VEHCOSIM_BASENODE_H 20 #define CH_VEHCOSIM_BASENODE_H 21 22 #include <fstream> 23 #include <string> 24 #include <iostream> 25 #include <vector> 26 27 #include <mpi.h> 28 29 #include "chrono/core/ChTimer.h" 30 #include "chrono/core/ChVector.h" 31 #include "chrono/core/ChQuaternion.h" 32 #include "chrono_vehicle/ChApiVehicle.h" 33 34 #include "chrono_thirdparty/filesystem/path.h" 35 36 #define MBS_NODE_RANK 0 37 #define TERRAIN_NODE_RANK 1 38 #define TIRE_NODE_RANK(i) (i+2) 39 40 namespace chrono { 41 namespace vehicle { 42 43 /** @addtogroup vehicle_cosim 44 * 45 * The vehicle co-simulation module provides an MPI_based framework for co-simulating a multibody system representing a 46 * wheeled mechanism with various terrain models and optionally various tire models. It implements a 3-way explicit 47 * force-displacement co-simulation approach. The three different types of nodes present in a co-simulation are as 48 * follows: 49 * - MBS node, a single MPI rank which simulates the multibody system up to the wheel spindles. 50 * - Tire nodes, a number of MPI ranks (equal to the number of wheels), each simulating one of the tires. 51 * - Terrain node(s), one or more MPI ranks which simulate the deformable terrain 52 * 53 * The inter-node communication at each synchronization time is as follows: 54 * - MBS node sends spindle body state to corresponding Tire nodes 55 * - Tire nodes send spindle forces to MBS node 56 * - Tire nodes send tire state (rigid body state or deformable mesh state) to Terrain node 57 * - Terrain node sends tire forces (single resultant force or distributed vertex forces) to corresponding Tire node 58 * 59 * The communication interface between Tire and Terrain nodes can be of one of two types: 60 * - ChVehicleCosimBaseNode::InterfaceType::BODY, in which data (force-displacement) for a single rigid body is 61 * exchanged 62 * - ChVehicleCosimBaseNode::InterfaceType::MESH, in which data (force-displacement) for a deformable mesh is exchanged 63 */ 64 65 /// @addtogroup vehicle_cosim 66 /// @{ 67 68 // ============================================================================= 69 70 namespace cosim { 71 72 /// Initialize the co-simulation framework. 73 /// This function creates an MPI communicator that includes all nodes designated of type TERRAIN. 74 /// Calling this framework initialization function is optional. If invoked, it *must* be called on all ranks. 75 /// Returns MPI_SUCCESS if successful and MPI_ERR_OTHER if there are not enough ranks. 76 CH_VEHICLE_API int InitializeFramework(int num_tires); 77 78 /// Return true if the co-simulation framework was initialized and false otherwise. 79 CH_VEHICLE_API bool IsFrameworkInitialized(); 80 81 /// Return the MPI communicator for distributed terrain simulation. 82 /// This intra-communicator is created if more than one node is designated of type TERRAIN. 83 /// On a TERRAIN node, the rank within the intra-communicator is accessible through MPI_Comm_rank. 84 CH_VEHICLE_API MPI_Comm GetTerrainIntracommunicator(); 85 86 }; // namespace cosim 87 88 // ============================================================================= 89 90 /// Base class for a co-simulation node. 91 class CH_VEHICLE_API ChVehicleCosimBaseNode { 92 public: 93 /// Type of node participating in co-simulation 94 enum class NodeType { 95 MBS, ///< node performing multibody dynamics (vehicle) 96 TERRAIN, ///< node performing terrain simulation 97 TIRE ///< node performing tire simulation (if outside MBS) 98 }; 99 100 /// Type of the tire-terrain communication interface. 101 /// - A BODY interface assumes communication is done at the wheel spindle level. At a synchronization time, the 102 /// terrain node receives the full state of the spindle body and must send forces acting on the spindle, for each 103 /// tire present in the simulation. This type of interface should be used for a rigid tire or when the terrain node 104 /// also performs the dynamics of a flexible tire. 105 /// - A MESH interface assumes communication is done at the tire mesh level. At a synchronization time, the terrain 106 /// node receives the tire mesh vertex states (positions and velocities) are must send forces acting on vertices of 107 /// the mesh, for each tire. This tire interface is typically used when flexible tires are simulated outside the 108 /// terrain node (either on the multibody node or else on separate tire nodes). 109 enum class InterfaceType { 110 BODY, ///< exchange state and force for a single body (wheel spindle) 111 MESH ///< exchange state and force for a mesh (flexible tire mesh) 112 }; 113 ~ChVehicleCosimBaseNode()114 virtual ~ChVehicleCosimBaseNode() {} 115 116 /// Return the node type. 117 virtual NodeType GetNodeType() const = 0; 118 119 /// Return the node type as a string. 120 std::string GetNodeTypeString() const; 121 122 /// Return true if this node is part of the co-simulation infrastructure. 123 bool IsCosimNode() const; 124 125 /// Set the integration step size (default: 1e-4). SetStepSize(double step)126 void SetStepSize(double step) { m_step_size = step; } 127 128 /// Get the integration step size. GetStepSize()129 double GetStepSize() const { return m_step_size; } 130 131 /// Set the name of the output directory and an identifying suffix. 132 /// Output files will be created in subdirectories named 133 /// dir_name/[NodeName]suffix/ 134 /// where [NodeName] is "MBS", "TIRE", or "TERRAIN". 135 void SetOutDir(const std::string& dir_name, const std::string& suffix); 136 137 /// Enable/disable verbose messages during simulation (default: true). SetVerbose(bool verbose)138 void SetVerbose(bool verbose) { m_verbose = verbose; } 139 140 /// Get the output directory name for this node. GetOutDirName()141 const std::string& GetOutDirName() const { return m_node_out_dir; } 142 143 /// Get the simulation execution time for the current step on this node. 144 /// This represents the time elapsed since the last synchronization point. GetStepExecutionTime()145 double GetStepExecutionTime() const { return m_timer.GetTimeSeconds(); } 146 147 /// Get the cumulative simulation execution time on this node. GetTotalExecutionTime()148 double GetTotalExecutionTime() const { return m_cum_sim_time; } 149 150 /// Initialize this node. 151 /// This function allows the node to initialize itself and, optionally, perform an initial data exchange with any 152 /// other node. A derived class implementation should first call this base class function. 153 virtual void Initialize(); 154 155 /// Synchronize this node. 156 /// This function is called at every co-simulation synchronization time to 157 /// allow the node to exchange information with any other node. 158 virtual void Synchronize(int step_number, double time) = 0; 159 160 /// Advance simulation. 161 /// This function is called after a synchronization to allow the node to advance 162 /// its state by the specified time step. A node is allowed to take as many internal 163 /// integration steps as required, but no inter-node communication should occur. 164 virtual void Advance(double step_size) = 0; 165 166 /// Output logging and debugging data. 167 virtual void OutputData(int frame) = 0; 168 169 /// Output post-processing visualization data. 170 /// If implemented, this function should write a file in the "visualization" subdirectory of m_node_out_dir. 171 virtual void OutputVisualizationData(int frame) = 0; 172 173 /// Write checkpoint to the specified file (which will be created in the output directory). WriteCheckpoint(const std::string & filename)174 virtual void WriteCheckpoint(const std::string& filename) const {} 175 176 /// Utility function for creating an output file name. 177 /// It generates and returns a string of the form "{dir}/{root}_{frame}.{ext}", where {frame} is printed using the 178 /// format "%0{frame_digits}d". 179 static std::string OutputFilename(const std::string& dir, 180 const std::string& root, 181 const std::string& ext, 182 int frame, 183 int frame_digits); 184 185 protected: 186 /// Mesh data 187 struct MeshData { 188 unsigned int nv; ///< number of vertices 189 unsigned int nn; ///< number of normals 190 unsigned int nt; ///< number of triangles 191 std::vector<ChVector<>> verts; ///< vertex positions (in local frame) 192 std::vector<ChVector<>> norms; ///< vertex normals (in local frame) 193 std::vector<ChVector<int>> idx_verts; ///< mesh vertex indices (connectivity) 194 std::vector<ChVector<int>> idx_norms; ///< mesh normal indices 195 }; 196 197 /// Mesh state 198 struct MeshState { 199 std::vector<ChVector<>> vpos; ///< vertex positions (in absolute frame) 200 std::vector<ChVector<>> vvel; ///< vertex velocities (in absolute frame) 201 }; 202 203 /// Mesh contact information 204 struct MeshContact { 205 int nv; ///< number of vertices in contact 206 std::vector<int> vidx; ///< indices of vertices experiencing contact forces 207 std::vector<ChVector<>> vforce; ///< contact forces on mesh vertices 208 }; 209 210 protected: 211 ChVehicleCosimBaseNode(const std::string& name); 212 213 int m_rank; ///< MPI rank of this node (in MPI_COMM_WORLD) 214 215 double m_step_size; ///< integration step size 216 217 std::string m_name; ///< name of the node 218 std::string m_out_dir; ///< top-level output directory 219 std::string m_node_out_dir; ///< node-specific output directory 220 std::ofstream m_outf; ///< output file stream 221 222 unsigned int m_num_mbs_nodes; 223 unsigned int m_num_terrain_nodes; 224 unsigned int m_num_tire_nodes; 225 226 ChTimer<double> m_timer; ///< timer for integration cost 227 double m_cum_sim_time; ///< cumulative integration cost 228 229 bool m_verbose; ///< verbose messages during simulation? 230 231 static const double m_gacc; 232 }; 233 234 /// @} vehicle_cosim 235 236 } // end namespace vehicle 237 } // end namespace chrono 238 239 #endif 240