// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChDoubleWishboneReduced::LogConstraintViolations(VehicleSide side) {
    // Revolute joint
    {
        ChMatrix<>* C = m_revolute[side]->GetC();
        GetLog() << "Spindle revolute      ";
        GetLog() << "  " << C->GetElement(0, 0) << "  ";
        GetLog() << "  " << C->GetElement(1, 0) << "  ";
        GetLog() << "  " << C->GetElement(2, 0) << "  ";
        GetLog() << "  " << C->GetElement(3, 0) << "  ";
        GetLog() << "  " << C->GetElement(4, 0) << "\n";
    }

    // Distance constraints
    GetLog() << "UCA front distance    ";
    GetLog() << "  " << m_distUCA_F[side]->GetCurrentDistance() - m_distUCA_F[side]->GetImposedDistance() << "\n";

    GetLog() << "UCA back distance     ";
    GetLog() << "  " << m_distUCA_B[side]->GetCurrentDistance() - m_distUCA_B[side]->GetImposedDistance() << "\n";

    GetLog() << "LCA front distance    ";
    GetLog() << "  " << m_distLCA_F[side]->GetCurrentDistance() - m_distLCA_F[side]->GetImposedDistance() << "\n";

    GetLog() << "LCA back distance     ";
    GetLog() << "  " << m_distLCA_B[side]->GetCurrentDistance() - m_distLCA_B[side]->GetImposedDistance() << "\n";

    GetLog() << "Tierod distance       ";
    GetLog() << "  " << m_distTierod[side]->GetCurrentDistance() - m_distTierod[side]->GetImposedDistance() << "\n";
}
예제 #2
0
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChPitmanArm::LogConstraintViolations() {
    // Revolute joint
    ////{
    ////    ChMatrix<>* C = m_revolute->GetC();
    ////    GetLog() << "Revolute              ";
    ////    GetLog() << "  " << C->GetElement(0, 0) << "  ";
    ////    GetLog() << "  " << C->GetElement(1, 0) << "  ";
    ////    GetLog() << "  " << C->GetElement(2, 0) << "  ";
    ////    GetLog() << "  " << C->GetElement(3, 0) << "  ";
    ////    GetLog() << "  " << C->GetElement(4, 0) << "\n";
    ////}

    // Universal joint
    {
        ChMatrix<>* C = m_universal->GetC();
        GetLog() << "Universal             ";
        GetLog() << "  " << C->GetElement(0, 0) << "  ";
        GetLog() << "  " << C->GetElement(1, 0) << "  ";
        GetLog() << "  " << C->GetElement(2, 0) << "  ";
        GetLog() << "  " << C->GetElement(3, 0) << "\n";
    }

    // Revolute-spherical joint
    {
        ChMatrix<>* C = m_revsph->GetC();
        GetLog() << "Revolute-spherical    ";
        GetLog() << "  " << C->GetElement(0, 0) << "  ";
        GetLog() << "  " << C->GetElement(1, 0) << "\n";
    }
}
예제 #3
0
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChRoadWheel::LogConstraintViolations() {
    ChMatrix<>* C = m_revolute->GetC();
    GetLog() << "  Road-wheel revolute\n";
    GetLog() << "  " << C->GetElement(0, 0) << "  ";
    GetLog() << "  " << C->GetElement(1, 0) << "  ";
    GetLog() << "  " << C->GetElement(2, 0) << "  ";
    GetLog() << "  " << C->GetElement(3, 0) << "  ";
    GetLog() << "  " << C->GetElement(4, 0) << "\n";
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChLinearDamperRWAssembly::LogConstraintViolations() {
    ChMatrix<>* C = m_revolute->GetC();
    GetLog() << "  Arm-chassis revolute\n";
    GetLog() << "  " << C->GetElement(0, 0) << "  ";
    GetLog() << "  " << C->GetElement(1, 0) << "  ";
    GetLog() << "  " << C->GetElement(2, 0) << "  ";
    GetLog() << "  " << C->GetElement(3, 0) << "  ";
    GetLog() << "  " << C->GetElement(4, 0) << "\n";

    m_road_wheel->LogConstraintViolations();
}
예제 #5
0
ChMapMatrix::ChMapMatrix(const ChMatrix<>& mat) {
    m_num_rows = mat.GetRows();
    m_num_cols = mat.GetColumns();
    m_rows.resize(mat.GetRows());
    for (int ir = 0; ir < m_num_rows; ir++) {
        for (int ic = 0; ic < m_num_cols; ic++) {
            double val = mat.GetElement(ir, ic);
            if (val != 0) {
                ChMapMatrix::SetElement(ir, ic, val);
            }
        }
    }
    m_CSR_current = false;
}
// =============================================================================
//
// Worker function for performing the simulation with specified parameters.
//
bool TestLinActuator(const ChQuaternion<>& rot,              // translation along Z axis
                     double                desiredSpeed,     // imposed translation speed
                     double                simTimeStep,      // simulation time step
                     double                outTimeStep,      // output time step
                     const std::string&    testName,         // name of this test
                     bool                  animate)          // if true, animate with Irrlich
{
  std::cout << "TEST: " << testName << std::endl;

  // Unit vector along translation axis, expressed in global frame
  ChVector<> axis = rot.GetZaxis();

  // Settings
  //---------

  double mass = 1.0;              // mass of plate
  ChVector<> inertiaXX(1, 1, 1);  // mass moments of inertia of plate (centroidal frame)
  double g = 9.80665;

  double timeRecord = 5;          // Stop recording to the file after this much simulated time

  // Create the mechanical system
  // ----------------------------

  ChSystem my_system;
  my_system.Set_G_acc(ChVector<>(0.0, 0.0, -g));

  my_system.SetIntegrationType(ChSystem::INT_ANITESCU);
  my_system.SetIterLCPmaxItersSpeed(100);
  my_system.SetIterLCPmaxItersStab(100); //Tasora stepper uses this, Anitescu does not
  my_system.SetLcpSolverType(ChSystem::LCP_ITERATIVE_SOR);
  my_system.SetTol(1e-6);
  my_system.SetTolForce(1e-4);


  // Create the ground body.

  ChSharedBodyPtr  ground(new ChBody);
  my_system.AddBody(ground);
  ground->SetBodyFixed(true);

  // Add geometry to the ground body for visualizing the translational joint
  ChSharedPtr<ChBoxShape> box_g(new ChBoxShape);
  box_g->GetBoxGeometry().SetLengths(ChVector<>(0.1, 0.1, 5));
  box_g->GetBoxGeometry().Pos = 2.5 * axis;
  box_g->GetBoxGeometry().Rot = rot;
  ground->AddAsset(box_g);

  ChSharedPtr<ChColorAsset> col_g(new ChColorAsset);
  col_g->SetColor(ChColor(0.6f, 0.2f, 0.2f));
  ground->AddAsset(col_g);

  // Create the plate body.

  ChSharedBodyPtr  plate(new ChBody);
  my_system.AddBody(plate);
  plate->SetPos(ChVector<>(0, 0, 0));
  plate->SetRot(rot);
  plate->SetPos_dt(desiredSpeed * axis);
  plate->SetMass(mass);
  plate->SetInertiaXX(inertiaXX);

  // Add geometry to the plate for visualization
  ChSharedPtr<ChBoxShape> box_p(new ChBoxShape);
  box_p->GetBoxGeometry().SetLengths(ChVector<>(1, 1, 0.2));
  plate->AddAsset(box_p);

  ChSharedPtr<ChColorAsset> col_p(new ChColorAsset);
  col_p->SetColor(ChColor(0.2f, 0.2f, 0.6f));
  plate->AddAsset(col_p);

  // Create prismatic (translational) joint between plate and ground.
  // We set the ground as the "master" body (second one in the initialization
  // call) so that the link coordinate system is expressed in the ground frame.

  ChSharedPtr<ChLinkLockPrismatic> prismatic(new ChLinkLockPrismatic);
  prismatic->Initialize(plate, ground, ChCoordsys<>(ChVector<>(0, 0, 0), rot));
  my_system.AddLink(prismatic);

  // Create a ramp function to impose constant speed.  This function returns
  //   y(t) = 0 + t * desiredSpeed
  //   y'(t) = desiredSpeed

  ChSharedPtr<ChFunction_Ramp> actuator_fun(new ChFunction_Ramp(0.0, desiredSpeed));

  // Create the linear actuator, connecting the plate to the ground.
  // Here, we set the plate as the master body (second one in the initialization
  // call) so that the link coordinate system is expressed in the plate body
  // frame.

  ChSharedPtr<ChLinkLinActuator> actuator(new ChLinkLinActuator);
  ChVector<> pt1 = ChVector<>(0, 0, 0);
  ChVector<> pt2 = axis;
  actuator->Initialize(ground, plate, false, ChCoordsys<>(pt1, rot), ChCoordsys<>(pt2, rot));
  actuator->Set_lin_offset(1);
  actuator->Set_dist_funct(actuator_fun);
  my_system.AddLink(actuator);

  // Perform the simulation (animation with Irrlicht)
  // ------------------------------------------------

  if (animate)
  {
    // Create the Irrlicht application for visualization
    ChIrrApp application(&my_system, L"ChLinkRevolute demo", core::dimension2d<u32>(800, 600), false, true);
    application.AddTypicalLogo();
    application.AddTypicalSky();
    application.AddTypicalLights();
    application.AddTypicalCamera(core::vector3df(1, 2, -2), core::vector3df(0, 0, 2));

    application.AssetBindAll();
    application.AssetUpdateAll();

    application.SetStepManage(true);
    application.SetTimestep(simTimeStep);

    // Simulation loop
    while (application.GetDevice()->run())
    {
      application.BeginScene();
      application.DrawAll();

      // Draw an XZ grid at the global origin to add in visualization
      ChIrrTools::drawGrid(
        application.GetVideoDriver(), 1, 1, 20, 20,
        ChCoordsys<>(ChVector<>(0, 0, 0), Q_from_AngX(CH_C_PI_2)),
        video::SColor(255, 80, 100, 100), true);

      application.DoStep();  //Take one step in time
      application.EndScene();
    }

    return true;
  }

  // Perform the simulation (record results)
  // ------------------------------------------------

  // Create the CSV_Writer output objects (TAB delimited)
  utils::CSV_writer out_pos = OutStream();
  utils::CSV_writer out_vel = OutStream();
  utils::CSV_writer out_acc = OutStream();

  utils::CSV_writer out_quat = OutStream();
  utils::CSV_writer out_avel = OutStream();
  utils::CSV_writer out_aacc = OutStream();

  utils::CSV_writer out_rfrcP = OutStream();
  utils::CSV_writer out_rtrqP = OutStream();

  utils::CSV_writer out_rfrcA = OutStream();
  utils::CSV_writer out_rtrqA = OutStream();

  utils::CSV_writer out_cnstrP = OutStream();

  utils::CSV_writer out_cnstrA = OutStream();

  // Write headers
  out_pos << "Time" << "X_Pos" << "Y_Pos" << "Z_Pos" << std::endl;
  out_vel << "Time" << "X_Vel" << "Y_Vel" << "Z_Vel" << std::endl;
  out_acc << "Time" << "X_Acc" << "Y_Acc" << "Z_Acc" << std::endl;

  out_quat << "Time" << "e0" << "e1" << "e2" << "e3" << std::endl;
  out_avel << "Time" << "X_AngVel" << "Y_AngVel" << "Z_AngVel" << std::endl;
  out_aacc << "Time" << "X_AngAcc" << "Y_AngAcc" << "Z_AngAcc" << std::endl;

  out_rfrcP << "Time" << "X_Force" << "Y_Force" << "Z_Force" << std::endl;
  out_rtrqP << "Time" << "X_Torque" << "Y_Torque" << "Z_Torque" << std::endl;

  out_rfrcA << "Time" << "X_Force" << "Y_Force" << "Z_Force" << std::endl;
  out_rtrqA << "Time" << "X_Torque" << "Y_Torque" << "Z_Torque" << std::endl;

  out_cnstrP << "Time" << "Cnstr_1" << "Cnstr_2" << "Cnstr_3" << "Constraint_4" << "Cnstr_5" << std::endl;

  out_cnstrA << "Time" << "Cnstr_1" << std::endl;

  // Simulation loop
  double simTime = 0;
  double outTime = 0;

  while (simTime <= timeRecord + simTimeStep / 2)
  {
    // Ensure that the final data point is recorded.
    if (simTime >= outTime - simTimeStep / 2)
    {

      // CM position, velocity, and acceleration (expressed in global frame).
      const ChVector<>& position = plate->GetPos();
      const ChVector<>& velocity = plate->GetPos_dt();
      out_pos << simTime << position << std::endl;
      out_vel << simTime << velocity << std::endl;
      out_acc << simTime << plate->GetPos_dtdt() << std::endl;

      // Orientation, angular velocity, and angular acceleration (expressed in
      // global frame).
      out_quat << simTime << plate->GetRot() << std::endl;
      out_avel << simTime << plate->GetWvel_par() << std::endl;
      out_aacc << simTime << plate->GetWacc_par() << std::endl;

      // Reaction Force and Torque in prismatic joint.
      // These are expressed in the link coordinate system. We convert them to
      // the coordinate system of Body2 (in our case this is the ground).
      ChCoordsys<> linkCoordsysP = prismatic->GetLinkRelativeCoords();
      ChVector<> reactForceP = prismatic->Get_react_force();
      ChVector<> reactForceGlobalP = linkCoordsysP.TransformDirectionLocalToParent(reactForceP);
      out_rfrcP << simTime << reactForceGlobalP << std::endl;

      ChVector<> reactTorqueP = prismatic->Get_react_torque();
      ChVector<> reactTorqueGlobalP = linkCoordsysP.TransformDirectionLocalToParent(reactTorqueP);
      out_rtrqP << simTime << reactTorqueGlobalP << std::endl;


      // Reaction force and Torque in linear actuator.
      // These are expressed  in the link coordinate system. We convert them to
      // the coordinate system of Body2 (in our case this is the plate). As such,
      // the reaction force represents the force that needs to be applied to the
      // plate in order to maintain the prescribed constant velocity.  These are
      // then converted to the global frame for comparison to ADAMS
      ChCoordsys<> linkCoordsysA = actuator->GetLinkRelativeCoords();
      ChVector<> reactForceA = actuator->Get_react_force();
      reactForceA = linkCoordsysA.TransformDirectionLocalToParent(reactForceA);
      ChVector<> reactForceGlobalA = plate->TransformDirectionLocalToParent(reactForceA);
      out_rfrcA << simTime << reactForceGlobalA << std::endl;

      ChVector<> reactTorqueA = actuator->Get_react_torque();
      reactTorqueA = linkCoordsysA.TransformDirectionLocalToParent(reactTorqueA);
      ChVector<> reactTorqueGlobalA = plate->TransformDirectionLocalToParent(reactTorqueA);
      out_rtrqA << simTime << reactTorqueGlobalA << std::endl;


      // Constraint violations in prismatic joint
      ChMatrix<>* CP = prismatic->GetC();
      out_cnstrP << simTime
                 << CP->GetElement(0, 0)
                 << CP->GetElement(1, 0)
                 << CP->GetElement(2, 0)
                 << CP->GetElement(3, 0)
                 << CP->GetElement(4, 0) << std::endl;

      // Constraint violations in linear actuator
      ChMatrix<>* CA = actuator->GetC();
      out_cnstrA << simTime << CA->GetElement(0, 0) << std::endl;

      // Increment output time
      outTime += outTimeStep;
    }

    // Advance simulation by one step
    my_system.DoStepDynamics(simTimeStep);

    // Increment simulation time
    simTime += simTimeStep;
  }

  // Write output files
  out_pos.write_to_file(out_dir + testName + "_CHRONO_Pos.txt", testName + "\n\n");
  out_vel.write_to_file(out_dir + testName + "_CHRONO_Vel.txt", testName + "\n\n");
  out_acc.write_to_file(out_dir + testName + "_CHRONO_Acc.txt", testName + "\n\n");

  out_quat.write_to_file(out_dir + testName + "_CHRONO_Quat.txt", testName + "\n\n");
  out_avel.write_to_file(out_dir + testName + "_CHRONO_Avel.txt", testName + "\n\n");
  out_aacc.write_to_file(out_dir + testName + "_CHRONO_Aacc.txt", testName + "\n\n");

  out_rfrcP.write_to_file(out_dir + testName + "_CHRONO_RforceP.txt", testName + "\n\n");
  out_rtrqP.write_to_file(out_dir + testName + "_CHRONO_RtorqueP.txt", testName + "\n\n");

  out_rfrcA.write_to_file(out_dir + testName + "_CHRONO_RforceA.txt", testName + "\n\n");
  out_rtrqA.write_to_file(out_dir + testName + "_CHRONO_RtorqueA.txt", testName + "\n\n");

  out_cnstrP.write_to_file(out_dir + testName + "_CHRONO_ConstraintsP.txt", testName + "\n\n");

  out_cnstrA.write_to_file(out_dir + testName + "_CHRONO_ConstraintsA.txt", testName + "\n\n");

  return true;
}
예제 #7
0
int main(int argc, char* argv[]) {
  // Create output directories.
  if (ChFileutils::MakeDirectory(out_dir.c_str()) < 0) {
    cout << "Error creating directory " << out_dir << endl;
    return 1;
  }
  if (ChFileutils::MakeDirectory(pov_dir.c_str()) < 0) {
    cout << "Error creating directory " << pov_dir << endl;
    return 1;
  }

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

#ifdef USE_DEM
  cout << "Create DEM system" << endl;
  ChSystemParallelDEM* msystem = new ChSystemParallelDEM();
#else
  cout << "Create DVI system" << endl;
  ChSystemParallelDVI* msystem = new ChSystemParallelDVI();
#endif

  msystem->Set_G_acc(ChVector<>(0, 0, -gravity));

  // Set number of threads.
  int max_threads = omp_get_num_procs();
  if (threads > max_threads)
    threads = max_threads;
  msystem->SetParallelThreadNumber(threads);
  omp_set_num_threads(threads);
  cout << "Using " << threads << " threads" << endl;

  msystem->GetSettings()->max_threads = threads;
  msystem->GetSettings()->perform_thread_tuning = thread_tuning;

  // Edit system settings
  msystem->GetSettings()->solver.use_full_inertia_tensor = false;
  msystem->GetSettings()->solver.tolerance = tolerance;
  msystem->GetSettings()->solver.max_iteration_bilateral = max_iteration_bilateral;
  msystem->GetSettings()->solver.clamp_bilaterals = clamp_bilaterals;
  msystem->GetSettings()->solver.bilateral_clamp_speed = bilateral_clamp_speed;

#ifdef USE_DEM
  msystem->GetSettings()->collision.narrowphase_algorithm = NARROWPHASE_R;
  msystem->GetSettings()->solver.contact_force_model = contact_force_model;
  msystem->GetSettings()->solver.tangential_displ_mode = tangential_displ_mode;
#else
  msystem->GetSettings()->solver.solver_mode = SLIDING;
  msystem->GetSettings()->solver.max_iteration_normal = max_iteration_normal;
  msystem->GetSettings()->solver.max_iteration_sliding = max_iteration_sliding;
  msystem->GetSettings()->solver.max_iteration_spinning = max_iteration_spinning;
  msystem->GetSettings()->solver.alpha = 0;
  msystem->GetSettings()->solver.contact_recovery_speed = contact_recovery_speed;
  msystem->SetMaxPenetrationRecoverySpeed(contact_recovery_speed);
  msystem->ChangeSolverType(APGDREF);

  msystem->GetSettings()->collision.collision_envelope = 0.05 * r_g;
#endif

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

  // --------------
  // Problem set up
  // --------------

  // Depending on problem type:
  // - Select end simulation time
  // - Select output FPS
  // - Create / load objects

  double time_min = 0;
  double time_end;
  int out_fps;
  ChSharedPtr<ChBody> ground;
  ChSharedPtr<ChBody> loadPlate;
  ChSharedPtr<ChLinkLockPrismatic> prismatic;
  ChSharedPtr<ChLinkLinActuator> actuator;

  switch (problem) {
    case SETTLING: {
      time_min = time_settling_min;
      time_end = time_settling_max;
      out_fps = out_fps_settling;

      // Create the mechanism bodies (all fixed).
      CreateMechanismBodies(msystem);

      // Grab handles to mechanism bodies (must increase ref counts)
      ground = ChSharedPtr<ChBody>(msystem->Get_bodylist()->at(0));
      loadPlate = ChSharedPtr<ChBody>(msystem->Get_bodylist()->at(1));
      msystem->Get_bodylist()->at(0)->AddRef();
      msystem->Get_bodylist()->at(1)->AddRef();

      // Create granular material.
      int num_particles = CreateGranularMaterial(msystem);
      cout << "Granular material:  " << num_particles << " particles" << endl;

      break;
    }

    case PRESSING: {
      time_min = time_pressing_min;
      time_end = time_pressing_max;
      out_fps = out_fps_pressing;

      // Create bodies from checkpoint file.
      cout << "Read checkpoint data from " << settled_ckpnt_file;
      utils::ReadCheckpoint(msystem, settled_ckpnt_file);
      cout << "  done.  Read " << msystem->Get_bodylist()->size() << " bodies." << endl;

      // Grab handles to mechanism bodies (must increase ref counts)
      ground = ChSharedPtr<ChBody>(msystem->Get_bodylist()->at(0));
      loadPlate = ChSharedPtr<ChBody>(msystem->Get_bodylist()->at(1));
      msystem->Get_bodylist()->at(0)->AddRef();
      msystem->Get_bodylist()->at(1)->AddRef();

      // Move the load plate just above the granular material.
      double highest, lowest;
      FindHeightRange(msystem, lowest, highest);
      ChVector<> pos = loadPlate->GetPos();
      double z_new = highest + 1.01 * r_g;
      loadPlate->SetPos(ChVector<>(pos.x, pos.y, z_new));

      // Add collision geometry to plate
      loadPlate->GetCollisionModel()->ClearModel();
      utils::AddBoxGeometry(loadPlate.get_ptr(), ChVector<>(hdimX_p, hdimY_p, hdimZ_p), ChVector<>(0, 0, hdimZ_p));
      loadPlate->GetCollisionModel()->BuildModel();

      // If using an actuator, connect the load plate and get a handle to the actuator.
      if (use_actuator) {
        ConnectLoadPlate(msystem, ground, loadPlate);
        prismatic = msystem->SearchLink("prismatic").StaticCastTo<ChLinkLockPrismatic>();
        actuator = msystem->SearchLink("actuator").StaticCastTo<ChLinkLinActuator>();
      }

      // Release the load plate.
      loadPlate->SetBodyFixed(!use_actuator);

      break;
    }

    case TESTING: {
      time_end = time_testing;
      out_fps = out_fps_testing;

      // For TESTING only, increse shearing velocity.
      desiredVelocity = 0.5;

      // Create the mechanism bodies (all fixed).
      CreateMechanismBodies(msystem);

      // Create the test ball.
      CreateBall(msystem);

      // Grab handles to mechanism bodies (must increase ref counts)
      ground = ChSharedPtr<ChBody>(msystem->Get_bodylist()->at(0));
      loadPlate = ChSharedPtr<ChBody>(msystem->Get_bodylist()->at(1));
      msystem->Get_bodylist()->at(0)->AddRef();
      msystem->Get_bodylist()->at(1)->AddRef();

      // Move the load plate just above the test ball.
      ChVector<> pos = loadPlate->GetPos();
      double z_new = 2.1 * radius_ball;
      loadPlate->SetPos(ChVector<>(pos.x, pos.y, z_new));

      // Add collision geometry to plate
      loadPlate->GetCollisionModel()->ClearModel();
      utils::AddBoxGeometry(loadPlate.get_ptr(), ChVector<>(hdimX_p, hdimY_p, hdimZ_p), ChVector<>(0, 0, hdimZ_p));
      loadPlate->GetCollisionModel()->BuildModel();

      // If using an actuator, connect the shear box and get a handle to the actuator.
      if (use_actuator) {
        ConnectLoadPlate(msystem, ground, loadPlate);
        actuator = msystem->SearchLink("actuator").StaticCastTo<ChLinkLinActuator>();
      }

      // Release the shear box when using an actuator.
      loadPlate->SetBodyFixed(!use_actuator);

      break;
    }
  }

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

  // Set number of simulation steps and steps between successive output
  int num_steps = (int)std::ceil(time_end / time_step);
  int out_steps = (int)std::ceil((1.0 / time_step) / out_fps);
  int write_steps = (int)std::ceil((1.0 / time_step) / write_fps);

  // Initialize counters
  double time = 0;
  int sim_frame = 0;
  int out_frame = 0;
  int next_out_frame = 0;
  double exec_time = 0;
  int num_contacts = 0;
  double max_cnstr_viol[2] = {0, 0};

  // Circular buffer with highest particle location
  // (only used for SETTLING or PRESSING)
  int buffer_size = std::ceil(time_min / time_step);
  std::valarray<double> hdata(0.0, buffer_size);

  // Create output files
  ChStreamOutAsciiFile statsStream(stats_file.c_str());
  ChStreamOutAsciiFile sinkageStream(sinkage_file.c_str());
  sinkageStream.SetNumFormat("%16.4e");

#ifdef CHRONO_PARALLEL_HAS_OPENGL
  opengl::ChOpenGLWindow& gl_window = opengl::ChOpenGLWindow::getInstance();
  gl_window.Initialize(1280, 720, "Pressure Sinkage Test", msystem);
  gl_window.SetCamera(ChVector<>(0, -10 * hdimY, hdimZ), ChVector<>(0, 0, hdimZ), ChVector<>(0, 0, 1));
  gl_window.SetRenderMode(opengl::WIREFRAME);
#endif

  // Loop until reaching the end time...
  while (time < time_end) {
    // Current position and velocity of the shear box
    ChVector<> pos_old = loadPlate->GetPos();
    ChVector<> vel_old = loadPlate->GetPos_dt();

    // Calculate minimum and maximum particle heights
    double highest, lowest;
    FindHeightRange(msystem, lowest, highest);

    // If at an output frame, write PovRay file and print info
    if (sim_frame == next_out_frame) {
      cout << "------------ Output frame:   " << out_frame + 1 << endl;
      cout << "             Sim frame:      " << sim_frame << endl;
      cout << "             Time:           " << time << endl;
      cout << "             Load plate pos: " << pos_old.z << endl;
      cout << "             Lowest point:   " << lowest << endl;
      cout << "             Highest point:  " << highest << endl;
      cout << "             Execution time: " << exec_time << endl;

      // Save PovRay post-processing data.
      if (write_povray_data) {
        char filename[100];
        sprintf(filename, "%s/data_%03d.dat", pov_dir.c_str(), out_frame + 1);
        utils::WriteShapesPovray(msystem, filename, false);
      }

      // Create a checkpoint from the current state.
      if (problem == SETTLING || problem == PRESSING) {
        cout << "             Write checkpoint data " << flush;
        if (problem == SETTLING)
          utils::WriteCheckpoint(msystem, settled_ckpnt_file);
        else
          utils::WriteCheckpoint(msystem, pressed_ckpnt_file);
        cout << msystem->Get_bodylist()->size() << " bodies" << endl;
      }

      // Increment counters
      out_frame++;
      next_out_frame += out_steps;
    }

    // Check for early termination of a settling phase.
    if (problem == SETTLING) {
      // Store maximum particle height in circular buffer
      hdata[sim_frame % buffer_size] = highest;

      // Check variance of data in circular buffer
      if (time > time_min) {
        double mean_height = hdata.sum() / buffer_size;
        std::valarray<double> x = hdata - mean_height;
        double var = std::sqrt((x * x).sum() / buffer_size);

        // Consider the material settled when the variance is below the
        // specified fraction of a particle radius
        if (var < settling_tol * r_g) {
          cout << "Granular material settled...  time = " << time << endl;
          break;
        }
      }
    }

// Advance simulation by one step
#ifdef CHRONO_PARALLEL_HAS_OPENGL
    if (gl_window.Active()) {
      gl_window.DoStepDynamics(time_step);
      gl_window.Render();
    } else
      break;
#else
    msystem->DoStepDynamics(time_step);
#endif

    TimingOutput(msystem);

    // Record stats about the simulation
    if (sim_frame % write_steps == 0) {
      // write stat info
      int numIters = msystem->data_manager->measures.solver.maxd_hist.size();
      double residual = 0;
      if (numIters)
        residual = msystem->data_manager->measures.solver.residual;
      statsStream << time << ", " << exec_time << ", " << num_contacts / write_steps << ", " << numIters << ", "
                  << residual << ", " << max_cnstr_viol[0] << ", " << max_cnstr_viol[1] << ", \n";
      statsStream.GetFstream().flush();

      num_contacts = 0;
      max_cnstr_viol[0] = 0;
      max_cnstr_viol[1] = 0;
    }

    if (problem == PRESSING || problem == TESTING) {
      // Get the current reaction force or impose load plate position
      double cnstr_force = 0;
      if (use_actuator) {
        cnstr_force = actuator->Get_react_force().x;
      } else {
        double zpos_new = pos_old.z + desiredVelocity * time_step;
        loadPlate->SetPos(ChVector<>(pos_old.x, pos_old.y, zpos_new));
        loadPlate->SetPos_dt(ChVector<>(0, 0, desiredVelocity));
      }

      if (sim_frame % write_steps == 0) {
        // std::cout << time << ", " << loadPlate->GetPos().z << ", " << cnstr_force << ", \n";
        sinkageStream << time << ", " << loadPlate->GetPos().z << ", " << cnstr_force << ", \n";
        sinkageStream.GetFstream().flush();
      }
    }

    // Find maximum constraint violation
    if (!prismatic.IsNull()) {
      ChMatrix<>* C = prismatic->GetC();
      for (int i = 0; i < 5; i++)
        max_cnstr_viol[0] = std::max(max_cnstr_viol[0], std::abs(C->GetElement(i, 0)));
    }
    if (!actuator.IsNull()) {
      ChMatrix<>* C = actuator->GetC();
      max_cnstr_viol[1] = std::max(max_cnstr_viol[2], std::abs(C->GetElement(0, 0)));
    }

    // Increment counters
    time += time_step;
    sim_frame++;
    exec_time += msystem->GetTimerStep();
    num_contacts += msystem->GetNcontacts();

    // If requested, output detailed timing information for this step
    if (sim_frame == timing_frame)
      msystem->PrintStepStats();
  }

  // ----------------
  // Final processing
  // ----------------

  // Create a checkpoint from the last state
  if (problem == SETTLING || problem == PRESSING) {
    cout << "             Write checkpoint data " << flush;
    if (problem == SETTLING)
      utils::WriteCheckpoint(msystem, settled_ckpnt_file);
    else
      utils::WriteCheckpoint(msystem, pressed_ckpnt_file);
    cout << msystem->Get_bodylist()->size() << " bodies" << endl;
  }

  // Final stats
  cout << "==================================" << endl;
  cout << "Number of bodies:  " << msystem->Get_bodylist()->size() << endl;
  cout << "Simulation time:   " << exec_time << endl;
  cout << "Number of threads: " << threads << endl;

  return 0;
}