public static void main(final String[] args) {
    OutputDirectoryLogging.catchLogEntries();
    final String configFile = args[0];

    // read the config with our special parameters
    // Note that you need scoring parameters converted
    // from the KTI config by something like
    // playground.thibautd.scripts.KtiToSimiliKtiConfig
    final Config config = ConfigUtils.createConfig();
    config.addModule(new KtiPtConfigGroup());
    config.addModule(new KtiLikeScoringConfigGroup());
    ConfigUtils.loadConfig(config, configFile);

    // just make sure the scenario is loaded
    // Controler accepts a config, but if the Scenario is not
    // fully loaded when creating the routing module, we may get into
    // troubles later...
    final Scenario scenario = ScenarioUtils.loadScenario(config);
    final Controler controler = new Controler(scenario);
    controler.setTripRouterFactory(new KtiTripRouterFactory(scenario));
    controler.setScoringFunctionFactory(
        new KtiLikeActivitiesScoringFunctionFactory(
            new StageActivityTypesImpl(PtConstants.TRANSIT_ACTIVITY_TYPE),
            (KtiLikeScoringConfigGroup) config.getModule(KtiLikeScoringConfigGroup.GROUP_NAME),
            config.planCalcScore(),
            scenario));

    // we're done!
    controler.run();
  }
  public static void main(final String[] args) {
    final String configFile = args[0];

    // This allows to get a log file containing the log messages happening
    // before controler init.
    OutputDirectoryLogging.catchLogEntries();

    // This is the location choice MultiNodeDijkstra.
    // Suppress all log messages of level below error --- to avoid spaming the config
    // file with zillions of "not route found" messages.
    Logger.getLogger(org.matsim.core.router.MultiNodeDijkstra.class)
        .setLevel(Level.ERROR); // this is location choice
    Logger.getLogger(org.matsim.pt.router.MultiNodeDijkstra.class).setLevel(Level.ERROR);

    final Config config =
        ConfigUtils.loadConfig(
            configFile,
            // this adds a new config group, used by the specific scoring function
            // we use

            new KtiLikeScoringConfigGroup());

    // This is currently needed for location choice: initializing
    // the location choice writes K-values files to the output directory, which:
    // - fails if the directory does not exist
    // - makes the controler crash latter if the unsafe setOverwriteFiles( true )
    // is not called.
    // This ensures that we get safety with location choice working as expected,
    // before we sort this out and definitely kick out setOverwriteFiles.
    createEmptyDirectoryOrFailIfExists(config.controler().getOutputDirectory());

    OneWayCarsharingRDConfigGroup configGroup = new OneWayCarsharingRDConfigGroup();
    config.addModule(configGroup);

    FreeFloatingConfigGroup configGroupff = new FreeFloatingConfigGroup();
    config.addModule(configGroupff);

    TwoWayCSConfigGroup configGrouptw = new TwoWayCSConfigGroup();
    config.addModule(configGrouptw);

    AllCSModesConfigGroup configGroupAll = new AllCSModesConfigGroup();
    config.addModule(configGroupAll);

    final Scenario scenario = ScenarioUtils.loadScenario(config);

    final Controler controler = new Controler(scenario);
    controler
        .getConfig()
        .controler()
        .setOverwriteFileSetting(
            true
                ? OutputDirectoryHierarchy.OverwriteFileSetting.overwriteExistingFiles
                : OutputDirectoryHierarchy.OverwriteFileSetting.failIfDirectoryExists);

    Set<String> modes = new TreeSet<String>();
    modes.add("freefloating");
    modes.add("twowaycarsharing");
    modes.add("onewaycarsharing");
    modes.add("car");
    modes.add("walk");
    modes.add("pt");
    modes.add("bike");
    TripsAnalyzer tripsAnalyzer =
        new TripsAnalyzer(
            controler.getConfig().getParam("controler", "outputDirectory") + "/tripsFile",
            controler.getConfig().getParam("controler", "outputDirectory") + "/durationsFile",
            controler.getConfig().getParam("controler", "outputDirectory") + "/distancesFile",
            modes,
            true,
            controler.getScenario().getNetwork());

    controler.addControlerListener(tripsAnalyzer);

    controler.addControlerListener(
        new AllCSModesTestListener(
            controler,
            Integer.parseInt(
                controler.getConfig().getModule("AllCSModes").getValue("statsWriterFrequency"))));

    controler.addOverridingModule(
        new AbstractModule() {
          @Override
          public void install() {
            bindMobsim().toProvider(AllCSModesQsimFactory.class);
          }
        });

    controler.setTripRouterFactory(
        new TripRouterFactory() {
          @Override
          public TripRouter instantiateAndConfigureTripRouter(RoutingContext routingContext) {
            // this factory initializes a TripRouter with default modules,
            // taking into account what is asked for in the config

            // This allows us to just add our module and go.
            final TripRouterFactory delegate =
                DefaultTripRouterFactoryImpl.createRichTripRouterFactoryImpl(
                    controler.getScenario());

            final TripRouter router = delegate.instantiateAndConfigureTripRouter(routingContext);

            // add our module to the instance
            router.setRoutingModule("twowaycarsharing", new TwoWayCSRoutingModule());

            router.setRoutingModule("freefloating", new FreeFloatingRoutingModule());

            router.setRoutingModule("onewaycarsharing", new OneWayCarsharingRDRoutingModule());

            // we still need to provide a way to identify our trips
            // as being twowaycarsharing trips.
            // This is for instance used at re-routing.
            final MainModeIdentifier defaultModeIdentifier = router.getMainModeIdentifier();
            router.setMainModeIdentifier(
                new MainModeIdentifier() {
                  @Override
                  public String identifyMainMode(final List<? extends PlanElement> tripElements) {
                    for (PlanElement pe : tripElements) {
                      if (pe instanceof Leg && ((Leg) pe).getMode().equals("twowaycarsharing")) {
                        return "twowaycarsharing";
                      } else if (pe instanceof Leg
                          && ((Leg) pe).getMode().equals("onewaycarsharing")) {
                        return "onewaycarsharing";
                      } else if (pe instanceof Leg && ((Leg) pe).getMode().equals("freefloating")) {
                        return "freefloating";
                      }
                    }
                    // if the trip doesn't contain a carsharing leg,
                    // fall back to the default identification method.
                    return defaultModeIdentifier.identifyMainMode(tripElements);
                  }
                });

            return router;
          }
        });

    connectFacilitiesWithNetwork(controler);

    initializeLocationChoice(controler);

    // We use a specific scoring function, that uses individual preferences
    // for activity durations.
    controler.setScoringFunctionFactory(
        new CarsharingMATSimLectureScoringFunctionFactory(
            controler.getScenario(),
            new StageActivityTypesImpl(PtConstants.TRANSIT_ACTIVITY_TYPE)));

    controler.run();
  }