int main(
      int argc,
      char* argv[]) {
   double time_step = .00001;
   ChVector<> gravity = ChVector<>(0, -9.80665, 0);
   ChSystemParallelDVI msystem;
   msystem.Set_G_acc(gravity);
   msystem.SetStep(time_step);

   ChSharedBodyPtr ball(new ChBody(new ChCollisionModelParallel));
   double mass = 1;
   ChVector<> pos = ChVector<>(0, 0, 0);
   ChVector<> vel = ChVector<>(2, 2, 0);
   ball->SetMass(mass);
   ball->SetPos(pos);
   ball->SetPos_dt(vel);
   msystem.AddBody(ball);

   real t = .01;
   real3 a = ToReal3(gravity);
   real3 p_final = ToReal3(vel) * t + .5 * a * t * t;

   double time = 0;
   for (int i = 0; i < 1000; i++) {
      msystem.DoStepDynamics(time_step);
      time = time + time_step;
   }
   WeakEqual(ToReal3(ball->GetPos()), p_final, 1e-6);
   return 0;
}
// -----------------------------------------------------------------------------
// Create the system, specify simulation parameters, and run simulation loop.
// -----------------------------------------------------------------------------
int main(int argc,
         char* argv[]) {
   int threads = 8;

   // Simulation parameters
   // ---------------------

   double gravity = 9.81;
   double time_step = 1e-3;
   double time_end = 100;

   double out_fps = 50;

   uint max_iteration = 30;
   real tolerance = 1e-3;

   // Create system
   // -------------

   ChSystemParallelDVI msystem;

   // Set number of threads.
   int max_threads = msystem.GetParallelThreadNumber();
   if (threads > max_threads)
      threads = max_threads;
   msystem.SetParallelThreadNumber(threads);
   omp_set_num_threads(threads);

   // Set gravitational acceleration
   msystem.Set_G_acc(ChVector<>(0, 0, -gravity));

   // Set solver parameters
   msystem.GetSettings()->solver.solver_mode = SLIDING;
   msystem.GetSettings()->solver.max_iteration_normal = max_iteration / 3;
   msystem.GetSettings()->solver.max_iteration_sliding = max_iteration / 3;
   msystem.GetSettings()->solver.max_iteration_spinning = 0;
   msystem.GetSettings()->solver.max_iteration_bilateral = max_iteration / 3;
   msystem.GetSettings()->solver.tolerance = tolerance;
   msystem.GetSettings()->solver.alpha = 0;
   msystem.GetSettings()->solver.contact_recovery_speed = 10000;
   msystem.ChangeSolverType(APGDREF);
   msystem.GetSettings()->collision.narrowphase_algorithm = NARROWPHASE_HYBRID_MPR;

   msystem.GetSettings()->collision.collision_envelope = 0.01;
   msystem.GetSettings()->collision.bins_per_axis = I3(10, 10, 10);


   // Create the fixed and moving bodies
   // ----------------------------------

   AddContainer(&msystem);
   AddFallingBalls(&msystem);

   // Perform the simulation
   // ----------------------

#ifdef CHRONO_PARALLEL_HAS_OPENGL
   opengl::ChOpenGLWindow &gl_window = opengl::ChOpenGLWindow::getInstance();
   gl_window.Initialize(1280, 720, "mixerDVI", &msystem);
   gl_window.SetCamera(ChVector<>(0, -10, 0), ChVector<>(0, 0, 0), ChVector<>(0, 0, 1));

   // Uncomment the following two lines for the OpenGL manager to automatically
   // run the simulation in an infinite loop.
   //gl_window.StartDrawLoop(time_step);
   //return 0;
#endif

   // Run simulation for specified time
   int num_steps = std::ceil(time_end / time_step);
   int out_steps = std::ceil((1 / time_step) / out_fps);
   double time = 0;
   int out_frame = 0;

   for (int i = 0; i < num_steps; i++) {
      if (i % out_steps == 0) {
         OutputData(&msystem, out_frame, time);
         out_frame++;
      }

#ifdef CHRONO_PARALLEL_HAS_OPENGL
      if (gl_window.Active()) {
         gl_window.DoStepDynamics(time_step);
         gl_window.Render();
      }
#else
      msystem.DoStepDynamics(time_step);
#endif

      time += time_step;
   }

   return 0;
}