private void run(
      final LinkDynamics linkDynamics,
      final TrafficDynamics trafficDynamics,
      final boolean isUsingFastCapacityUpdate) {

    MatsimRandom.reset();
    scenario = ScenarioUtils.loadScenario(ConfigUtils.createConfig());
    createNetwork();

    storeVehicleTypeInfo();

    scenario.getConfig().qsim().setMainModes(Arrays.asList(travelModes));
    scenario.getConfig().qsim().setEndTime(14 * 3600);
    scenario.getConfig().qsim().setLinkDynamics(linkDynamics.name());

    if (linkDynamics.equals(LinkDynamics.SeepageQ)) {
      scenario.getConfig().qsim().setSeepMode("bike");
      scenario.getConfig().qsim().setSeepModeStorageFree(false);
      scenario.getConfig().qsim().setRestrictingSeepage(true);
    }

    scenario
        .getConfig()
        .vspExperimental()
        .setVspDefaultsCheckingLevel(VspDefaultsCheckingLevel.abort);
    scenario.getConfig().qsim().setTrafficDynamics(trafficDynamics);

    scenario.getConfig().qsim().setUsingFastCapacityUpdate(isUsingFastCapacityUpdate);

    // equal modal split run
    Map<String, Integer> minSteps = new HashMap<String, Integer>();

    double pcu1 = modeVehicleTypes.get(travelModes[0]).getPcuEquivalents();
    double pcu2 = modeVehicleTypes.get(travelModes[1]).getPcuEquivalents();

    if (pcu1 == 1 && pcu2 == 0.25) { // car bike
      minSteps.put(travelModes[0], 1);
      minSteps.put(travelModes[1], 4);
    } else { // car truck
      minSteps.put(travelModes[0], 3);
      minSteps.put(travelModes[1], 1);
    }

    int reduceNoOfDataPointsInPlot = 4; // 1--> will generate all possible data points;

    double networkDensity = 3. * (1000. / 7.5);
    double sumOfPCUInEachStep =
        (modeVehicleTypes.get(travelModes[0]).getPcuEquivalents() * minSteps.get(travelModes[0]))
            + (modeVehicleTypes.get(travelModes[1]).getPcuEquivalents()
                * minSteps.get(travelModes[1]));
    int numberOfPoints =
        (int) Math.ceil(networkDensity / (reduceNoOfDataPointsInPlot * sumOfPCUInEachStep)) + 5;

    List<Map<String, Integer>> points2Run = new ArrayList<Map<String, Integer>>();

    for (int m = 1; m < numberOfPoints; m++) {
      Map<String, Integer> pointToRun = new HashMap<>();
      for (String mode : travelModes) {
        pointToRun.put(mode, minSteps.get(mode) * m * reduceNoOfDataPointsInPlot);
      }

      double density = 0;
      for (String mode : pointToRun.keySet()) {
        double pcu = this.modeVehicleTypes.get(mode).getPcuEquivalents();
        density += pcu * pointToRun.get(mode);
      }

      if (density <= networkDensity + 10) {
        System.out.println("Number of Agents - \t" + pointToRun.toString());
        points2Run.add(pointToRun);
      }
    }

    Map<Double, Map<String, Tuple<Double, Double>>> outData =
        new HashMap<Double, Map<String, Tuple<Double, Double>>>();

    for (Map<String, Integer> point2run : points2Run) {

      System.out.println("\n \n \t \t Running points " + point2run.toString() + "\n \n");

      int count = 0;
      person2Mode.clear();

      for (String mode : point2run.keySet()) {
        for (int ii = 0; ii < point2run.get(mode); ii++) {
          person2Mode.put(Id.createPersonId(count++), mode);
        }
        this.mode2FlowData
            .get(modeVehicleTypes.get(mode).getId())
            .setnumberOfAgents(point2run.get(mode));
      }

      EventsManager events = EventsUtils.createEventsManager();
      globalFlowDynamicsUpdator = new GlobalFlowDynamicsUpdator(mode2FlowData);
      events.addHandler(globalFlowDynamicsUpdator);

      final QSim qSim = new QSim(scenario, events);
      ActivityEngine activityEngine = new ActivityEngine(events, qSim.getAgentCounter());
      qSim.addMobsimEngine(activityEngine);
      qSim.addActivityHandler(activityEngine);
      QNetsimEngine netsimEngine = new QNetsimEngine(qSim);
      qSim.addMobsimEngine(netsimEngine);
      qSim.addDepartureHandler(netsimEngine.getDepartureHandler());

      final Map<String, VehicleType> travelModesTypes = new HashMap<String, VehicleType>();

      for (String mode : travelModes) {
        travelModesTypes.put(mode, modeVehicleTypes.get(mode));
      }

      AgentSource agentSource =
          new AgentSource() {
            @Override
            public void insertAgentsIntoMobsim() {
              for (Id<Person> personId : person2Mode.keySet()) {
                String travelMode = person2Mode.get(personId);
                double actEndTime = (MatsimRandom.getRandom().nextDouble()) * 900;

                MobsimAgent agent =
                    new MySimplifiedRoundAndRoundAgent(personId, actEndTime, travelMode);
                qSim.insertAgentIntoMobsim(agent);

                final Vehicle vehicle =
                    VehicleUtils.getFactory()
                        .createVehicle(
                            Id.create(agent.getId(), Vehicle.class),
                            travelModesTypes.get(travelMode));
                final Id<Link> linkId4VehicleInsertion = Id.createLinkId("home");
                qSim.createAndParkVehicleOnLink(vehicle, linkId4VehicleInsertion);
              }
            }
          };

      qSim.addAgentSource(agentSource);

      qSim.run();

      Map<String, Tuple<Double, Double>> mode2FlowSpeed =
          new HashMap<String, Tuple<Double, Double>>();
      for (int i = 0; i < travelModes.length; i++) {

        Tuple<Double, Double> flowSpeed =
            new Tuple<Double, Double>(
                this.mode2FlowData
                    .get(Id.create(travelModes[i], VehicleType.class))
                    .getPermanentFlow(),
                this.mode2FlowData
                    .get(Id.create(travelModes[i], VehicleType.class))
                    .getPermanentAverageVelocity());
        mode2FlowSpeed.put(travelModes[i], flowSpeed);
        outData.put(
            globalFlowDynamicsUpdator.getGlobalData().getPermanentDensity(), mode2FlowSpeed);
      }
    }

    /*
     *	Basically overriding the helper.getOutputDirectory() method, such that,
     *	if file directory does not exists or same file already exists, remove and re-creates the whole dir hierarchy so that
     *	all existing files are re-written
     *	else, just keep adding files in the directory.
     *	This is necessary in order to allow writing different tests results from JUnit parameterization.
     */

    String outDir =
        "test/output/"
            + CreateAutomatedFDTest.class.getCanonicalName().replace('.', '/')
            + "/"
            + helper.getMethodName()
            + "/";
    String fileName = linkDynamics + "_" + trafficDynamics + ".png";
    String outFile;
    // ZZ_TODO : what is there exists some different directory => changing method name will keep
    // collecting the old data.
    if (!new File(outDir).exists() || new File(outDir + fileName).exists()) {
      outFile = helper.getOutputDirectory() + fileName;
    } else {
      outFile = outDir + fileName;
    }

    // plotting data
    scatterPlot(outData, outFile);
  }
  @Override
  public Mobsim get() {
    QSimConfigGroup configGroup = scenario.getConfig().qsim();
    if (configGroup == null) {
      throw new NullPointerException(
          "There is no configuration set for the QSim. Please add the module 'qsim' to your config file.");
    }

    /* Set up the QSim. */
    QSim qSim = new QSim(scenario, eventsManager);
    /* Add the activity engine. */
    ActivityEngine activityEngine = new ActivityEngine(eventsManager, qSim.getAgentCounter());
    qSim.addMobsimEngine(activityEngine);
    qSim.addActivityHandler(activityEngine);

    /* This is the crucial part for changing the queue type. */
    NetsimNetworkFactory netsimNetworkFactory =
        new NetsimNetworkFactory() {
          @Override
          public QLinkImpl createNetsimLink(
              final Link link, final QNetwork network, final QNode toQueueNode) {
            VehicleQ<QVehicle> vehicleQ = null;
            switch (queueType) {
              case FIFO:
              case GFIP_FIFO:
                vehicleQ = new FIFOVehicleQ();
                break;
              case BASIC_PASSING:
              case GFIP_PASSING:
                vehicleQ = new PassingVehicleQ();
                break;
              default:
                throw new RuntimeException(
                    "Do not know what VehicleQ to use with queue type " + queueType.toString());
            }
            return new QLinkImpl(link, network, toQueueNode, vehicleQ);
          }

          @Override
          public QNode createNetsimNode(final Node node, QNetwork network) {
            return new QNode(node, network);
          }
        };
    QNetsimEngine netsimEngine = new QNetsimEngine(qSim, netsimNetworkFactory);

    /* Add the custom GFIP link speed calculator, but only when required. */
    if (queueType == QueueType.FIFO) {
      log.info("------------------------------------------------------------------------------");
      log.info("  Using basic FIFO link speed calculator. ");
      log.info("------------------------------------------------------------------------------");
      netsimEngine.setLinkSpeedCalculator(new DefaultLinkSpeedCalculator());
    } else if (queueType == QueueType.BASIC_PASSING) {
      log.info("------------------------------------------------------------------------------");
      log.info("  Using basic passing link speed calculator. ");
      log.info("------------------------------------------------------------------------------");
      netsimEngine.setLinkSpeedCalculator(
          new GfipLinkSpeedCalculator(scenario.getVehicles(), qSim, queueType));
    } else if (queueType == QueueType.GFIP_PASSING) {
      log.info("------------------------------------------------------------------------------");
      log.info("  Using custom GFIP-link-density-based link speed calculator with passing. ");
      log.info("------------------------------------------------------------------------------");
      netsimEngine.setLinkSpeedCalculator(
          new GfipLinkSpeedCalculator(scenario.getVehicles(), qSim, queueType));
    } else if (queueType == QueueType.GFIP_FIFO) {
      log.info("------------------------------------------------------------------------------");
      log.info("  Using custom GFIP-link-density-based link speed calculator without passing.");
      log.info("------------------------------------------------------------------------------");
      netsimEngine.setLinkSpeedCalculator(
          new GfipLinkSpeedCalculator(scenario.getVehicles(), qSim, queueType));
    }

    qSim.addMobsimEngine(netsimEngine);
    qSim.addDepartureHandler(netsimEngine.getDepartureHandler());
    TeleportationEngine teleportationEngine = new TeleportationEngine(scenario, eventsManager);
    qSim.addMobsimEngine(teleportationEngine);
    AgentFactory agentFactory = new DefaultAgentFactory(qSim);

    /* ..Update the PopulationAgentSource to ensure the correct vehicle is
     * passed to the mobsim, not some default-per-mode vehicle. */
    PopulationAgentSource agentSource =
        new PopulationAgentSource(scenario.getPopulation(), agentFactory, qSim);
    Map<String, VehicleType> modeVehicleTypes = new HashMap<String, VehicleType>();
    for (Id<VehicleType> id : scenario.getVehicles().getVehicleTypes().keySet()) {
      modeVehicleTypes.put(id.toString(), scenario.getVehicles().getVehicleTypes().get(id));
    }
    agentSource.setModeVehicleTypes(modeVehicleTypes);
    qSim.addAgentSource(agentSource);

    return qSim;
  }