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, Jay Taves
13 // =============================================================================
14 //
15 // Deformable terrain based on SCM (Soil Contact Model) from DLR
16 // (Krenn & Hirzinger)
17 //
18 // =============================================================================
19 
20 #ifndef SCM_DEFORMABLE_TERRAIN_H
21 #define SCM_DEFORMABLE_TERRAIN_H
22 
23 #include <string>
24 #include <ostream>
25 #include <unordered_map>
26 
27 #include "chrono/assets/ChColorAsset.h"
28 #include "chrono/assets/ChTriangleMeshShape.h"
29 #include "chrono/physics/ChBody.h"
30 #include "chrono/physics/ChLoadContainer.h"
31 #include "chrono/physics/ChLoadsBody.h"
32 #include "chrono/physics/ChSystem.h"
33 #include "chrono/core/ChTimer.h"
34 
35 #include "chrono_vehicle/ChApiVehicle.h"
36 #include "chrono_vehicle/ChSubsysDefs.h"
37 #include "chrono_vehicle/ChTerrain.h"
38 #include "chrono_vehicle/ChWorldFrame.h"
39 
40 namespace chrono {
41 namespace vehicle {
42 
43 class SCMDeformableSoil;
44 
45 /// @addtogroup vehicle_terrain
46 /// @{
47 
48 /// Deformable terrain model.
49 /// This class implements a deformable terrain based on the Soil Contact Model.
50 /// Unlike RigidTerrain, the vertical coordinates of this terrain mesh can be deformed
51 /// due to interaction with ground vehicles or other collision shapes.
52 class CH_VEHICLE_API SCMDeformableTerrain : public ChTerrain {
53   public:
54     enum DataPlotType {
55         PLOT_NONE,
56         PLOT_LEVEL,
57         PLOT_LEVEL_INITIAL,
58         PLOT_SINKAGE,
59         PLOT_SINKAGE_ELASTIC,
60         PLOT_SINKAGE_PLASTIC,
61         PLOT_STEP_PLASTIC_FLOW,
62         PLOT_PRESSURE,
63         PLOT_PRESSURE_YELD,
64         PLOT_SHEAR,
65         PLOT_K_JANOSI,
66         PLOT_IS_TOUCHED,
67         PLOT_ISLAND_ID,
68         PLOT_MASSREMAINDER
69     };
70 
71     /// Construct a default SCM deformable terrain.
72     /// The user is responsible for calling various Set methods before Initialize.
73     SCMDeformableTerrain(ChSystem* system,               ///< [in] pointer to the containing multibody system
74                          bool visualization_mesh = true  ///< [in] enable/disable visualization asset
75     );
76 
~SCMDeformableTerrain()77     ~SCMDeformableTerrain() {}
78 
79     /// Set the plane reference.
80     /// By default, the reference plane is horizontal with Z up (ISO vehicle reference frame).
81     /// To set as Y up, call SetPlane(ChCoordys(VNULL, Q_from_AngX(-CH_C_PI_2)));
82     void SetPlane(const ChCoordsys<>& plane);
83 
84     /// Get the current reference plane.
85     /// The SCM terrain patch is in the (x,y) plane with normal along the Z axis.
86     const ChCoordsys<>& GetPlane() const;
87 
88     /// Set the properties of the SCM soil model.
89     /// These parameters are described in: "Parameter Identification of a Planetary Rover Wheel-Soil Contact Model via a
90     /// Bayesian Approach", A.Gallina, R. Krenn et al. Note that the original SCM model does not include the K and R
91     /// coefficients. A very large value of K and R=0 reproduce the original SCM.
92     void SetSoilParameters(
93         double Bekker_Kphi,    ///< Kphi, frictional modulus in Bekker model
94         double Bekker_Kc,      ///< Kc, cohesive modulus in Bekker model
95         double Bekker_n,       ///< n, exponent of sinkage in Bekker model (usually 0.6...1.8)
96         double Mohr_cohesion,  ///< Cohesion for shear failure [Pa]
97         double Mohr_friction,  ///< Friction angle for shear failure [degree]
98         double Janosi_shear,   ///< Shear parameter in Janosi-Hanamoto formula [m]
99         double elastic_K,      ///< elastic stiffness K per unit area, [Pa/m] (must be larger than Kphi)
100         double damping_R       ///< vertical damping R per unit area [Pa.s/m] (proportional to vertical speed)
101     );
102 
103     /// Enable/disable the creation of soil inflation at the side of the ruts (bulldozing effects).
104     void EnableBulldozing(bool mb);
105 
106     /// Set parameters controlling the creation of side ruts (bulldozing effects).
107     void SetBulldozingParameters(
108         double erosion_angle,          ///< angle of erosion of the displaced material [degrees]
109         double flow_factor = 1.0,      ///< growth of lateral volume relative to pressed volume
110         int erosion_iterations = 3,    ///< number of erosion refinements per timestep
111         int erosion_propagations = 10  ///< number of concentric vertex selections subject to erosion
112     );
113 
114     /// Set the vertical level up to which collision is tested (relative to the reference level at the sample point).
115     /// Since the contact is unilateral, this could be zero. However, when computing bulldozing flow, one might also
116     /// need to know if in the surrounding there is some potential future contact: so it might be better to use a
117     /// positive value (but not higher than the max. expected height of the bulldozed rubble, to avoid slowdown of
118     /// collision tests). Default: 0.1 m.
119     void SetTestHeight(double offset);
120 
121     ///  Return the current test height level.
122     double GetTestHeight() const;
123 
124     /// Set the color plot type for the soil mesh.
125     /// When a scalar plot is used, also define the range in the pseudo-color colormap.
126     void SetPlotType(DataPlotType plot_type, double min_val, double max_val);
127 
128     /// Set visualization color.
129     void SetColor(const ChColor& color);
130 
131     /// Set texture properties.
132     void SetTexture(const std::string tex_file,  ///< [in] texture filename
133                     float tex_scale_x = 1,       ///< [in] texture scale in X
134                     float tex_scale_y = 1        ///< [in] texture scale in Y
135     );
136 
137     /// Add a new moving patch.
138     /// Multiple calls to this function can be made, each of them adding a new active patch area.
139     /// If no patches are defined, ray-casting is performed for every single node of the underlying SCM grid.
140     /// If at least one patch is defined, ray-casting is performed only for mesh nodes within the AABB of the
141     /// body OOBB projection onto the SCM plane.
142     void AddMovingPatch(std::shared_ptr<ChBody> body,   ///< [in] monitored body
143                         const ChVector<>& OOBB_center,  ///< [in] OOBB center, relative to body
144                         const ChVector<>& OOBB_dims     ///< [in] OOBB dimensions
145     );
146 
147     /// Class to be used as a callback interface for location-dependent soil parameters.
148     /// A derived class must implement Set() and set *all* soil parameters (no defaults are provided).
149     class CH_VEHICLE_API SoilParametersCallback {
150       public:
~SoilParametersCallback()151         virtual ~SoilParametersCallback() {}
152 
153         /// Set the soil properties at a given (x,y) location (below the given point).
154         /// Attention: the location is assumed to be provided in the SCM reference frame!
155         virtual void Set(
156             const ChVector<>& loc,  ///< query location
157             double& Bekker_Kphi,    ///< frictional modulus in Bekker model
158             double& Bekker_Kc,      ///< cohesive modulus in Bekker model
159             double& Bekker_n,       ///< exponent of sinkage in Bekker model (usually 0.6...1.8)
160             double& Mohr_cohesion,  ///< cohesion for shear failure [Pa]
161             double& Mohr_friction,  ///< friction angle for shear failure [degree]
162             double& Janosi_shear,   ///< shear parameter in Janosi-Hanamoto formula [m]
163             double& elastic_K,      ///< elastic stiffness K per unit area, [Pa/m] (must be larger than Kphi)
164             double& damping_R       ///< vertical damping R per unit area [Pa.s/m] (proportional to vertical speed)
165             ) = 0;
166     };
167 
168     /// Specify the callback object to set the soil parameters at given (x,y) locations.
169     /// To use constant soil parameters throughout the entire patch, use SetSoilParameters.
170     void RegisterSoilParametersCallback(std::shared_ptr<SoilParametersCallback> cb);
171 
172     /// Get the initial (undeformed) terrain height below the specified location.
173     double GetInitHeight(const ChVector<>& loc) const;
174 
175     /// Get the initial (undeformed) terrain normal at the point below the specified location.
176     ChVector<> GetInitNormal(const ChVector<>& loc) const;
177 
178     /// Get the terrain height below the specified location.
179     virtual double GetHeight(const ChVector<>& loc) const override;
180 
181     /// Get the terrain normal at the point below the specified location.
182     virtual ChVector<> GetNormal(const ChVector<>& loc) const override;
183 
184     /// Get the terrain coefficient of friction at the point below the specified location.
185     /// This coefficient of friction value may be used by certain tire models to modify
186     /// the tire characteristics, but it will have no effect on the interaction of the terrain
187     /// with other objects (including tire models that do not explicitly use it).
188     /// For SCMDeformableTerrain, this function defers to the user-provided functor object
189     /// of type ChTerrain::FrictionFunctor, if one was specified.
190     /// Otherwise, it returns the constant value of 0.8.
191     virtual float GetCoefficientFriction(const ChVector<>& loc) const override;
192 
193     /// Get the visualization triangular mesh.
194     std::shared_ptr<ChTriangleMeshShape> GetMesh() const;
195 
196     /// Set the visualization mesh as wireframe or as solid (default: wireframe).
197     /// Note: in wireframe mode, normals for the visualization mesh are not calculated.
198     void SetMeshWireframe(bool val);
199 
200     /// Save the visualization mesh as a Wavefront OBJ file.
201     void WriteMesh(const std::string& filename) const;
202 
203     /// Initialize the terrain system (flat).
204     /// This version creates a flat array of points.
205     void Initialize(double sizeX,  ///< [in] terrain dimension in the X direction
206                     double sizeY,  ///< [in] terrain dimension in the Y direction
207                     double delta   ///< [in] grid spacing (may be slightly decreased)
208     );
209 
210     /// Initialize the terrain system (height map).
211     /// The initial undeformed mesh is provided via the specified image file as a height map.
212     /// By default, a mesh vertex is created for each pixel in the image. If divX and divY are non-zero,
213     /// the image will be sampled at the specified resolution with linear interpolation as needed.
214     void Initialize(const std::string& heightmap_file,  ///< [in] filename for the height map (image file)
215                     double sizeX,                       ///< [in] terrain dimension in the X direction
216                     double sizeY,                       ///< [in] terrain dimension in the Y direction
217                     double hMin,                        ///< [in] minimum height (black level)
218                     double hMax,                        ///< [in] maximum height (white level)
219                     double delta                        ///< [in] grid spacing (may be slightly decreased)
220     );
221 
222     /// Node height level at a given grid location.
223     typedef std::pair<ChVector2<int>, double> NodeLevel;
224 
225     /// Get the heights of all modified grid nodes.
226     /// If 'all_nodes = true', return modified nodes from the start of simulation.  Otherwise, return only the nodes
227     /// modified over the last step.
228     std::vector<NodeLevel> GetModifiedNodes(bool all_nodes = false) const;
229 
230     /// Modify the level of grid nodes from the given list.
231     void SetModifiedNodes(const std::vector<NodeLevel>& nodes);
232 
233     /// Return the current cumulative contact force on the specified body (due to interaction with the SCM terrain).
234     TerrainForce GetContactForce(std::shared_ptr<ChBody> body) const;
235 
236     /// Return the number of rays cast at last step.
237     int GetNumRayCasts() const;
238     /// Return the number of ray hits at last step.
239     int GetNumRayHits() const;
240     /// Return the number of contact patches at last step.
241     int GetNumContactPatches() const;
242     /// Return the number of nodes in the erosion domain at last step (bulldosing effects).
243     int GetNumErosionNodes() const;
244 
245     /// Return time for updating moving patches at last step (ms).
246     double GetTimerMovingPatches() const;
247     /// Return time for geometric ray intersection tests at last step (ms).
248     double GetTimerRayTesting() const;
249     /// Return time for ray casting at last step (ms). Includes time for ray intersectin testing.
250     double GetTimerRayCasting() const;
251     /// Return time for computing contact patches at last step (ms).
252     double GetTimerContactPatches() const;
253     /// Return time for computing contact forces at last step (ms).
254     double GetTimerContactForces() const;
255     /// Return time for computing bulldozing effects at last step (ms).
256     double GetTimerBulldozing() const;
257     /// Return time for visualization assets update at last step (ms).
258     double GetTimerVisUpdate() const;
259 
260     /// Print timing and counter information for last step.
261     void PrintStepStatistics(std::ostream& os) const;
262 
263   private:
264     std::shared_ptr<SCMDeformableSoil> m_ground;  ///< underlying load container for contact force generation
265 };
266 
267 /// Parameters for soil-contactable interaction.
268 class CH_VEHICLE_API SCMContactableData {
269   public:
270     SCMContactableData(double area_ratio,     ///< area fraction with overriden parameters (in [0,1])
271                        double Mohr_cohesion,  ///< cohesion for shear failure [Pa]
272                        double Mohr_friction,  ///< friction angle for shear failure [degree]
273                        double Janosi_shear    ///< shear parameter in Janosi-Hanamoto formula [m]
274     );
275 
276   private:
277     double area_ratio;     ///< fraction of contactable surface where soil-soil parameters are overriden
278     double Mohr_cohesion;  ///< cohesion for shear failure [Pa]
279     double Mohr_mu;        ///< coefficient of friction for shear failure [degree]
280     double Janosi_shear;   ///< shear parameter in Janosi-Hanamoto formula [m]
281 
282     friend class SCMDeformableSoil;
283 };
284 
285 /// Underlying implementation of the Soil Contact Model.
286 class CH_VEHICLE_API SCMDeformableSoil : public ChLoadContainer {
287   public:
288     SCMDeformableSoil(ChSystem* system, bool visualization_mesh);
~SCMDeformableSoil()289     ~SCMDeformableSoil() {}
290 
291     /// Initialize the terrain system (flat).
292     /// This version creates a flat array of points.
293     void Initialize(double hsizeX,  ///< [in] terrain dimension in the X direction
294                     double hsizeY,  ///< [in] terrain dimension in the Y direction
295                     double delta    ///< [in] grid spacing (may be slightly decreased)
296     );
297 
298     /// Initialize the terrain system (height map).
299     /// The initial undeformed mesh is provided via the specified image file as a height map
300     /// By default, a mesh vertex is created for each pixel in the image. If divX and divY are non-zero,
301     /// the image will be sampled at the specified resolution with linear interpolation as needed.
302     void Initialize(const std::string& heightmap_file,  ///< [in] filename for the height map (image file)
303                     double sizeX,                       ///< [in] terrain dimension in the X direction
304                     double sizeY,                       ///< [in] terrain dimension in the Y direction
305                     double hMin,                        ///< [in] minimum height (black level)
306                     double hMax,                        ///< [in] maximum height (white level)
307                     double delta                        ///< [in] grid spacing (may be slightly decreased)
308     );
309 
310   private:
311     // SCM patch type
312     enum class PatchType {
313         FLAT,       // flat patch
314         HEIGHT_MAP  // triangular mesh (generated from a gray-scale image height-map)
315     };
316 
317     // Moving patch parameters
318     struct MovingPatchInfo {
319         std::shared_ptr<ChBody> m_body;       // tracked body
320         ChVector<> m_center;                  // OOBB center, relative to body
321         ChVector<> m_hdims;                   // OOBB half-dimensions
322         std::vector<ChVector2<int>> m_range;  // current grid nodes covered by the patch
323         ChVector<> m_ooN;                     // current inverse of SCM normal in body frame
324     };
325 
326     // Information at contacted node
327     struct NodeRecord {
328         double level_initial;      // initial node level (relative to SCM frame)
329         double level;              // current node level (relative to SCM frame)
330         double hit_level;          // ray hit level (relative to SCM frame)
331         ChVector<> normal;         // normal of undeformed terrain (in SCM frame)
332         double sinkage;            // along local normal direction
333         double sinkage_plastic;    // along local normal direction
334         double sinkage_elastic;    // along local normal direction
335         double sigma;              // along local normal direction
336         double sigma_yield;        // along local normal direction
337         double kshear;             // along local tangent direction
338         double tau;                // along local tangent direction
339         bool erosion;              // for bulldozing
340         double massremainder;      // for bulldozing
341         double step_plastic_flow;  // for bulldozing
342 
NodeRecordNodeRecord343         NodeRecord() : NodeRecord(0, 0, ChVector<>(0,0,1)) {}
~NodeRecordNodeRecord344         ~NodeRecord() {}
345 
NodeRecordNodeRecord346         NodeRecord(double init_level, double level, const ChVector<>& n)
347             : level_initial(init_level),
348               level(level),
349               hit_level(1e9),
350               normal(n),
351               sinkage(init_level - level),
352               sinkage_plastic(0),
353               sinkage_elastic(0),
354               sigma(0),
355               sigma_yield(0),
356               kshear(0),
357               tau(0),
358               erosion(false),
359               massremainder(0),
360               step_plastic_flow(0) {}
361     };
362 
363     // Hash function for a pair of integer grid coordinates
364     struct CoordHash {
365       public:
366         // 31 is just a decently-sized prime number to reduce bucket collisions
operatorCoordHash367         std::size_t operator()(const ChVector2<int>& p) const { return p.x() * 31 + p.y(); }
368     };
369 
370     // Get the initial undeformed terrain height (relative to the SCM plane) at the specified grid node.
371     double GetInitHeight(const ChVector2<int>& loc) const;
372 
373     // Get the initial undeformed terrain normal (relative to the SCM plane) at the specified grid node.
374     ChVector<> GetInitNormal(const ChVector2<int>& loc) const;
375 
376     // Get the terrain height (relative to the SCM plane) at the specified grid node.
377     double GetHeight(const ChVector2<int>& loc) const;
378 
379     // Get the terrain normal (relative to the SCM plane) at the specified grid vertex.
380     ChVector<> GetNormal(const ChVector2<>& loc) const;
381 
382     // Get the initial terrain height (expressed in World frame) below the specified location.
383     double GetInitHeight(const ChVector<>& loc) const;
384 
385     // Get the initial terrain normal (expressed in World frame) at the point below the specified location.
386     ChVector<> GetInitNormal(const ChVector<>& loc) const;
387 
388     // Get the terrain height (expressed in World frame) below the specified location.
389     double GetHeight(const ChVector<>& loc) const;
390 
391     // Get the terrain normal (expressed in World frame) at the point below the specified location.
392     ChVector<> GetNormal(const ChVector<>& loc) const;
393 
394     // Get index of trimesh vertex corresponding to the specified grid node.
395     int GetMeshVertexIndex(const ChVector2<int>& loc);
396 
397     // Get indices of trimesh faces incident to the specified grid vertex.
398     std::vector<int> GetMeshFaceIndices(const ChVector2<int>& loc);
399 
400     // Check if the provided grid location is within the visualization mesh bounds
401     bool CheckMeshBounds(const ChVector2<int>& loc) const;
402 
403     // Complete setup before first simulation step.
404     virtual void SetupInitial() override;
405 
406     // Update the forces and the geometry, at the beginning of each timestep.
Setup()407     virtual void Setup() override {
408         ComputeInternalForces();
409         ChLoadContainer::Update(ChTime, true);
410     }
411 
412     virtual void Update(double mytime, bool update_assets = true) override {
413         // Note!!! we cannot call ComputeInternalForces here, because Update() could
414         // be called multiple times per timestep and not necessarily in time-increasing order;
415         // this is a problem because in this force model the force is dissipative and keeps a 'history'.
416         // Instead, we invoke ComputeInternalForces only at the beginning of the timestep in Setup().
417 
418         ChTime = mytime;
419     }
420 
421     // Synchronize information for a moving patch
422     void UpdateMovingPatch(MovingPatchInfo& p, const ChVector<>& Z);
423 
424     // Synchronize information for fixed patch
425     void UpdateFixedPatch(MovingPatchInfo& p);
426 
427     // Ray-OBB intersection test
428     bool RayOBBtest(const MovingPatchInfo& p, const ChVector<>& from, const ChVector<>& Z);
429 
430     // Reset the list of forces and fill it with forces from the soil contact model.
431     // This is called automatically during timestepping (only at the beginning of each step).
432     void ComputeInternalForces();
433 
434     // Override the ChLoadContainer method for computing the generalized force F term:
IntLoadResidual_F(const unsigned int off,ChVectorDynamic<> & R,const double c)435     virtual void IntLoadResidual_F(const unsigned int off,  // offset in R residual
436                                    ChVectorDynamic<>& R,    // result: the R residual, R += c*F
437                                    const double c           // a scaling factor
438                                    ) override {
439         ChLoadContainer::IntLoadResidual_F(off, R, c);
440     }
441 
442     // Add specified amount of material (possibly clamped) to node.
443     void AddMaterialToNode(double amount, NodeRecord& nr);
444 
445     // Remove specified amount of material (possibly clamped) from node.
446     void RemoveMaterialFromNode(double amount, NodeRecord& nr);
447 
448     // Update vertex position and color in visualization mesh
449     void UpdateMeshVertexCoordinates(const ChVector2<int> ij, int iv, const NodeRecord& nr);
450 
451     // Update vertex normal in visualization mesh
452     void UpdateMeshVertexNormal(const ChVector2<int> ij, int iv);
453 
454     /// Get the heights of all modified grid nodes.
455     /// If 'all_nodes = true', return modified nodes from the start of simulation.  Otherwise, return only the nodes
456     /// modified over the last step.
457     std::vector<SCMDeformableTerrain::NodeLevel> GetModifiedNodes(bool all_nodes = false) const;
458 
459     // Modify the level of grid nodes from the given list.
460     void SetModifiedNodes(const std::vector<SCMDeformableTerrain::NodeLevel>& nodes);
461 
462     PatchType m_type;      // type of SCM patch
463     ChCoordsys<> m_plane;  // SCM frame (deformation occurs along the z axis of this frame)
464     ChVector<> m_Z;        // SCM plane vertical direction (in absolute frame)
465     double m_delta;        // grid spacing
466     double m_area;         // area of a grid cell
467     int m_nx;              // range for grid indices in X direction: [-m_nx, +m_nx]
468     int m_ny;              // range for grid indices in Y direction: [-m_ny, +m_ny]
469 
470     ChMatrixDynamic<> m_heights;  // (base) grid heights (when initializing from height-field map)
471 
472     std::unordered_map<ChVector2<int>, NodeRecord, CoordHash> m_grid_map;  // modified grid nodes (persistent)
473     std::vector<ChVector2<int>> m_modified_nodes;                          // modified grid nodes (current)
474 
475     std::vector<MovingPatchInfo> m_patches;  // set of active moving patches
476     bool m_moving_patch;                     // user-specified moving patches?
477 
478     double m_test_offset_down;  // offset for ray start
479     double m_test_offset_up;    // offset for ray end
480 
481     std::shared_ptr<ChTriangleMeshShape> m_trimesh_shape;  // mesh visualization asset
482     std::shared_ptr<ChColorAsset> m_color;                 // mesh edge default color
483 
484     // SCM parameters
485     double m_Bekker_Kphi;    ///< frictional modulus in Bekker model
486     double m_Bekker_Kc;      ///< cohesive modulus in Bekker model
487     double m_Bekker_n;       ///< exponent of sinkage in Bekker model (usually 0.6...1.8)
488     double m_Mohr_cohesion;  ///< cohesion for shear failure [Pa]
489     double m_Mohr_mu;        ///< coefficient of friction for shear failure [degree]
490     double m_Janosi_shear;   ///< shear parameter in Janosi-Hanamoto formula [m]
491     double m_elastic_K;      ///< elastic stiffness K per unit area, [Pa/m] (must be larger than Kphi)
492     double m_damping_R;      ///< vertical damping R per unit area [Pa.s/m] (proportional to vertical speed)
493 
494     // Callback object for position-dependent soil properties
495     std::shared_ptr<SCMDeformableTerrain::SoilParametersCallback> m_soil_fun;
496 
497     // Contact forces on contactable objects interacting with the SCM terrain
498     std::unordered_map<ChContactable*, TerrainForce> m_contact_forces;
499 
500     // Bulldozing effects
501     bool m_bulldozing;
502     double m_flow_factor;
503     double m_erosion_slope;
504     int m_erosion_iterations;
505     int m_erosion_propagations;
506 
507     // Mesh coloring mode
508     SCMDeformableTerrain::DataPlotType m_plot_type;
509     double m_plot_v_min;
510     double m_plot_v_max;
511 
512     // Indices of visualization mesh vertices modified externally
513     std::vector<int> m_external_modified_vertices;
514 
515     // Timers and counters
516     ChTimer<double> m_timer_moving_patches;
517     ChTimer<double> m_timer_ray_testing;
518     ChTimer<double> m_timer_ray_casting;
519     ChTimer<double> m_timer_contact_patches;
520     ChTimer<double> m_timer_contact_forces;
521     ChTimer<double> m_timer_bulldozing;
522     ChTimer<double> m_timer_bulldozing_boundary;
523     ChTimer<double> m_timer_bulldozing_domain;
524     ChTimer<double> m_timer_bulldozing_erosion;
525     ChTimer<double> m_timer_visualization;
526     int m_num_ray_casts;
527     int m_num_ray_hits;
528     int m_num_contact_patches;
529     int m_num_erosion_nodes;
530 
531     friend class SCMDeformableTerrain;
532 };
533 
534 /// @} vehicle_terrain
535 
536 }  // end namespace vehicle
537 }  // end namespace chrono
538 
539 #endif
540