001/* *********************************************************************** * 002 * project: org.matsim.* 003 * *********************************************************************** * 004 * * 005 * copyright : (C) 2007, 2008 by the members listed in the COPYING, * 006 * LICENSE and WARRANTY file. * 007 * email : info at matsim dot org * 008 * * 009 * *********************************************************************** * 010 * * 011 * This program is free software; you can redistribute it and/or modify * 012 * it under the terms of the GNU General Public License as published by * 013 * the Free Software Foundation; either version 2 of the License, or * 014 * (at your option) any later version. * 015 * See also COPYING, LICENSE and WARRANTY file * 016 * * 017 * *********************************************************************** */ 018 019package org.matsim.core.controler; 020 021import com.google.inject.Key; 022import com.google.inject.Provider; 023import com.google.inject.TypeLiteral; 024import com.google.inject.name.Names; 025import org.apache.log4j.Logger; 026import org.apache.logging.log4j.core.layout.PatternLayout; 027import org.matsim.analysis.CalcLinkStats; 028import org.matsim.analysis.IterationStopWatch; 029import org.matsim.analysis.ScoreStats; 030import org.matsim.analysis.VolumesAnalyzer; 031import org.matsim.api.core.v01.Scenario; 032import org.matsim.api.core.v01.TransportMode; 033import org.matsim.api.core.v01.events.Event; 034import org.matsim.core.api.experimental.events.EventsManager; 035import org.matsim.core.config.Config; 036import org.matsim.core.config.ConfigUtils; 037import org.matsim.core.config.consistency.ConfigConsistencyCheckerImpl; 038import org.matsim.core.config.consistency.UnmaterializedConfigGroupChecker; 039import org.matsim.core.controler.corelisteners.ControlerDefaultCoreListenersModule; 040import org.matsim.core.controler.listener.ControlerListener; 041import org.matsim.core.events.handler.EventHandler; 042import org.matsim.core.gbl.Gbl; 043import org.matsim.core.mobsim.qsim.AbstractQSimModule; 044import org.matsim.core.mobsim.qsim.components.QSimComponentsConfig; 045import org.matsim.core.mobsim.qsim.components.QSimComponentsConfigurator; 046import org.matsim.core.mobsim.qsim.components.StandardQSimComponentConfigurator; 047import org.matsim.core.replanning.ReplanningContext; 048import org.matsim.core.replanning.StrategyManager; 049import org.matsim.core.router.TripRouter; 050import org.matsim.core.router.costcalculators.TravelDisutilityFactory; 051import org.matsim.core.router.util.LeastCostPathCalculatorFactory; 052import org.matsim.core.router.util.TravelDisutility; 053import org.matsim.core.router.util.TravelTime; 054import org.matsim.core.scenario.ScenarioByConfigModule; 055import org.matsim.core.scenario.ScenarioByInstanceModule; 056import org.matsim.core.scoring.ScoringFunctionFactory; 057 058import java.util.*; 059 060/** 061 * The Controler is responsible for complete simulation runs, including the 062 * initialization of all required data, running the iterations and the 063 * replanning, analyses, etc. 064 * 065 * @author mrieser 066 */ 067public final class Controler implements ControlerI, MatsimServices, AllowsConfiguration{ 068 // yyyy Design thoughts: 069 // * Seems to me that we should try to get everything here final. Flexibility is provided by the ability to set or add factories. If this is 070 // not sufficient, people should use AbstractController. kai, jan'13 071 072 public static final String DIRECTORY_ITERS = "ITERS"; 073 074 public enum DefaultFiles { 075 config("config.xml"), 076 configReduced("config_reduced.xml"), 077 network("network.xml"), 078 lanes("lanes.xml"), 079 changeEvents("change_events.xml"), 080 counts("counts.xml"), 081 population("plans.xml"), 082 experiencedPlans("experienced_plans.xml"), 083 households("households.xml"), 084 facilities("facilities.xml"), 085 events("events.xml"), 086 eventsPb("events.pb"), 087 eventsJson("events.ndjson"), 088 transitSchedule("transitSchedule.xml"), 089 transitVehicles("transitVehicles.xml"), 090 vehicles("vehicles.xml"), 091 allVehicles("allVehicles.xml"), 092 linkstats("linkstats.txt"), 093 tripscsv("trips.csv"), 094 personscsv("persons.csv"), 095 legscsv("legs.csv"), 096 ; 097 098 final String filename; 099 100 DefaultFiles(String filename) { 101 this.filename = filename; 102 } 103 } 104 105 static final String OUTPUT_PREFIX = "output_"; 106 107 public static final String DIVIDER = "###################################################"; 108 109 private static final Logger log = Logger.getLogger(Controler.class); 110 111 public static final PatternLayout DEFAULTLOG4JLAYOUT = PatternLayout.newBuilder().withPattern("%d{ISO8601} %5p %C{1}:%L %m%n").build(); 112 113 private final Config config; 114 private Scenario scenario; 115 116 private com.google.inject.Injector injector; 117 private boolean injectorCreated = false; 118 119 @Override 120 public IterationStopWatch getStopwatch() { 121 return injector.getInstance(IterationStopWatch.class); 122 } 123 124 // DefaultControlerModule includes submodules. If you want less than what the Controler does 125 // by default, you can leave ControlerDefaultsModule out, look at what it does, 126 // and only include what you want. 127 private List<AbstractModule> modules = Collections.singletonList(new ControlerDefaultsModule()); 128 129 // The module which is currently defined by the sum of the setXX methods called on this Controler. 130 private AbstractModule overrides = AbstractModule.emptyModule(); 131 132 private List<AbstractQSimModule> overridingQSimModules = new LinkedList<>(); 133 134 public static void main(final String[] args) { 135 if ((args == null) || (args.length == 0)) { 136 System.out.println("No argument given!"); 137 System.out.println("Usage: Controler config-file [dtd-file]"); 138 System.out.println(); 139 } else { 140 final Controler controler = new Controler(args); 141 controler.run(); 142 } 143 System.exit(0); 144 } 145 146 /** 147 * Initializes a new instance of Controler with the given arguments. 148 * 149 * @param args 150 * The arguments to initialize the services with. 151 * <code>args[0]</code> is expected to contain the path to a 152 * configuration file, <code>args[1]</code>, if set, is expected 153 * to contain the path to a local copy of the DTD file used in 154 * the configuration file. 155 */ 156 public Controler(final String[] args) { 157 this(args.length > 0 ? args[0] : null, null, null); 158 } 159 160 public Controler(final String configFileName) { 161 this(configFileName, null, null); 162 } 163 164 public Controler(final Config config) { 165 this(null, config, null); 166 } 167 168 public Controler(final Scenario scenario) { 169 this(null, null, scenario); 170 } 171 172 private Controler(final String configFileName, final Config config, Scenario scenario) { 173 if (scenario != null) { 174 // scenario already loaded (recommended): 175 this.config = scenario.getConfig(); 176 this.config.addConfigConsistencyChecker(new ConfigConsistencyCheckerImpl()); 177 } else { 178 if (configFileName == null) { 179 // config should already be loaded: 180 if (config == null) { 181 throw new IllegalArgumentException("Either the config or the filename of a configfile must be set to initialize the Controler."); 182 } 183 this.config = config; 184 } else { 185 // else load config: 186 this.config = ConfigUtils.loadConfig(configFileName); 187 } 188 this.config.addConfigConsistencyChecker(new ConfigConsistencyCheckerImpl()); 189 190 // load scenario: 191 //scenario = ScenarioUtils.createScenario(this.config); 192 //ScenarioUtils.loadScenario(scenario) ; 193 } 194 this.config.parallelEventHandling().makeLocked(); 195 this.scenario = scenario; 196 this.overrides = scenario == null ? 197 new ScenarioByConfigModule() : 198 new ScenarioByInstanceModule(this.scenario); 199 200 this.config.qsim().setLocked(); 201 // yy this is awfully ad-hoc. kai, jul'18 202 // yy should probably come even earlier, before the scenario is generated. kai, jul'18 203 } 204 205 /** 206 * Starts the iterations. 207 */ 208 @Override 209 public final void run() { 210 // It is better to keep this line before actually creating the injector, because: 211 // - it actually means "fail if adding new Guice modules" 212 // - adding Guice modules to the Controler from other Guice modules is too late. 213 // This might sound silly, but might, in some cases, happen, through code that 214 // - transformed a StartupListener to a Guice module 215 // - that called methods such as setScoringFunctionFactory(), that redirects to addOverridingModule() 216 // And this happens silently, leading to lots of time and hair lost. 217 // td, nov 16 218 this.injectorCreated = true; 219 220 this.overrides = AbstractModule.override(Collections.singletonList(this.overrides), new AbstractModule() { 221 @Override 222 public void install() { 223 bind(Key.get(new TypeLiteral<List<AbstractQSimModule>>() { 224 }, Names.named("overrides"))).toInstance(overridingQSimModules); 225 } 226 }); 227 228 // check config consistency just before creating injector; sometimes, we can provide better error messages there: 229 config.removeConfigConsistencyChecker( UnmaterializedConfigGroupChecker.class ); 230 config.checkConsistency(); 231 config.addConfigConsistencyChecker( new UnmaterializedConfigGroupChecker() ); 232 233 final Set<AbstractModule> standardModules = Collections.singleton( 234 new AbstractModule(){ 235 @Override 236 public void install(){ 237 install( new NewControlerModule() ); 238 install( new ControlerDefaultCoreListenersModule() ); 239 for( AbstractModule module : modules ){ 240 install( module ); 241 } 242 // should not be necessary: created in the controler 243 //install(new ScenarioByInstanceModule(scenario)); 244 } 245 } 246 ); 247 this.injector = Injector.createInjector( config, AbstractModule.override( standardModules, overrides ) ); 248 ControlerI controler = injector.getInstance(ControlerI.class); 249 controler.run(); 250 } 251 252 253 // ******** --------- ******* 254 // The following is the internal interface of the Controler, which 255 // is meant to be called while the Controler is running (not before).. 256 // ******** --------- ******* 257 258 @Override 259 public final TravelTime getLinkTravelTimes() { 260 return this.injector.getInstance(com.google.inject.Injector.class).getInstance(Key.get(new TypeLiteral<Map<String, TravelTime>>() {})) 261 .get(TransportMode.car); 262 } 263 264 /** 265 * Gives access to a {@link org.matsim.core.router.TripRouter} instance. 266 * This is a routing service which you can use 267 * to calculate routes, e.g. from your own replanning code or your own within-day replanning 268 * agent code. 269 * You get a Provider (and not an instance directly) because your code may want to later 270 * create more than one instance. A TripRouter is not guaranteed to be thread-safe, so 271 * you must get() an instance for each thread if you plan to write multi-threaded code. 272 * 273 * See {@link org.matsim.core.router.TripRouter} for more information and pointers to examples. 274 */ 275 @Override 276 public final Provider<TripRouter> getTripRouterProvider() { 277 return this.injector.getProvider(TripRouter.class); 278 } 279 280 @Override 281 public final TravelDisutility createTravelDisutilityCalculator() { 282 return getTravelDisutilityFactory().createTravelDisutility(this.injector.getInstance(TravelTime.class)); 283 } 284 285 @Override 286 public final LeastCostPathCalculatorFactory getLeastCostPathCalculatorFactory() { 287 return this.injector.getInstance(LeastCostPathCalculatorFactory.class); 288 } 289 290 @Override 291 public final ScoringFunctionFactory getScoringFunctionFactory() { 292 return this.injector.getInstance(ScoringFunctionFactory.class); 293 } 294 295 @Override 296 public final Config getConfig() { 297 return config; 298 } 299 300 @Override 301 public final Scenario getScenario() { 302 if (this.injectorCreated) { 303 Gbl.assertNotNull(this.injector); 304 return this.injector.getInstance(Scenario.class); 305 } else { 306 if ( scenario == null ) { 307 log.error( "Trying to get Scenario before it was instanciated."); 308 log.error( "When passing a config file or a config file path to the Controler constructor," ); 309 log.error( "Scenario will be loaded first when the run() method is invoked." ); 310 throw new IllegalStateException( "Trying to get Scenario before is was instanciated." ); 311 } 312 return this.scenario; 313 } 314 } 315 316 317 @Override 318 public final EventsManager getEvents() { 319 if (this.injector != null) { 320 return this.injector.getInstance(EventsManager.class); 321 } else { 322 return new EventsManager() { 323 @Override 324 public void processEvent(Event event) { 325 Controler.this.injector.getInstance(EventsManager.class).processEvent(event); 326 } 327 328 @Override 329 public void addHandler(final EventHandler handler) { 330 addOverridingModule(new AbstractModule() { 331 @Override 332 public void install() { 333 addEventHandlerBinding().toInstance(handler); 334 } 335 }); 336 } 337 338 @Override 339 public void removeHandler(EventHandler handler) { 340 throw new UnsupportedOperationException(); 341 } 342 343 @Override 344 public void resetHandlers(int iteration) { 345 throw new UnsupportedOperationException(); 346 } 347 348 @Override 349 public void initProcessing() { 350 throw new UnsupportedOperationException(); 351 } 352 353 @Override 354 public void afterSimStep(double time) { 355 throw new UnsupportedOperationException(); 356 } 357 358 @Override 359 public void finishProcessing() { 360 throw new UnsupportedOperationException(); 361 } 362 }; 363 } 364 } 365 366 @Override 367 public final com.google.inject.Injector getInjector() { 368 return this.injector; 369 } 370 371 /** 372 * @deprecated Do not use this, as it may not contain values in every 373 * iteration 374 */ 375 @Override 376 @Deprecated 377 public final CalcLinkStats getLinkStats() { 378 return this.injector.getInstance(CalcLinkStats.class); 379 } 380 381 @Override 382 public final VolumesAnalyzer getVolumes() { 383 return this.injector.getInstance(VolumesAnalyzer.class); 384 } 385 386 @Override 387 public final ScoreStats getScoreStats() { 388 return this.injector.getInstance(ScoreStats.class); 389 } 390 391 @Override 392 public final TravelDisutilityFactory getTravelDisutilityFactory() { 393 return this.injector.getInstance(com.google.inject.Injector.class).getInstance(Key.get(new TypeLiteral<Map<String, TravelDisutilityFactory>>(){})) 394 .get(TransportMode.car); 395 } 396 397 /** 398 * @return Returns the {@link org.matsim.core.replanning.StrategyManager} 399 * used for the replanning of plans. 400 * @deprecated -- try to use services.addPlanStrategyFactory or services.addPlanSelectoryFactory. 401 * There are cases when this does not work, which is in particular necessary if you need to re-configure the StrategyManager 402 * during the iterations, <i>and</i> you cannot do this before the iterations start. In such cases, using this 403 * method may be ok. kai/mzilske, aug'14 404 */ 405 @Override 406 @Deprecated // see javadoc above 407 public final StrategyManager getStrategyManager() { 408 return this.injector.getInstance(StrategyManager.class); 409 } 410 411 @Override 412 public OutputDirectoryHierarchy getControlerIO() { 413 return injector.getInstance(OutputDirectoryHierarchy.class); 414 } 415 416 @Override 417 public Integer getIterationNumber() { 418 return injector.getInstance(ReplanningContext.class).getIteration(); 419 } 420 // ******** --------- ******* 421 // The following methods are the outer interface of the Controler. They are used 422 // to set up infrastructure from the outside, before calling run(). 423 // ******** --------- ******* 424 425 @Override 426 public void addControlerListener(final ControlerListener controlerListener) { 427 addOverridingModule(new AbstractModule() { 428 @Override 429 public void install() { 430 addControlerListenerBinding().toInstance(controlerListener); 431 } 432 }); 433 } 434 435 public final void setScoringFunctionFactory( 436 final ScoringFunctionFactory scoringFunctionFactory) { 437 this.addOverridingModule(new AbstractModule() { 438 @Override 439 public void install() { 440 this.bindScoringFunctionFactory().toInstance(scoringFunctionFactory); 441 } 442 }); 443 } 444 445 public final void setTerminationCriterion(final TerminationCriterion terminationCriterion) { 446 this.addOverridingModule(new AbstractModule() { 447 @Override 448 public void install() { 449 bind(TerminationCriterion.class).toInstance(terminationCriterion); 450 } 451 }); 452 } 453 454 @Override 455 public final Controler addOverridingModule( AbstractModule abstractModule ) { 456 if (this.injectorCreated) { 457 throw new RuntimeException("Too late for configuring the Controler. This can only be done before calling run."); 458 } 459 this.overrides = AbstractModule.override(Collections.singletonList(this.overrides), abstractModule); 460 return this ; 461 } 462 463 public final void setModules(AbstractModule... modules) { 464 if (this.injectorCreated) { 465 throw new RuntimeException("Too late for configuring the Controler. This can only be done before calling run."); 466 } 467 this.modules = Arrays.asList(modules); 468 } 469 470 @Override 471 public final Controler addOverridingQSimModule( AbstractQSimModule qsimModule ) { 472 if (this.injectorCreated) { 473 throw new RuntimeException("Too late for configuring the Controler. This can only be done before calling run."); 474 } 475 overridingQSimModules.add(qsimModule); 476 return this ; 477 } 478 @Override 479 public final Controler addQSimModule(AbstractQSimModule qsimModule) { 480 this.addOverridingModule(new AbstractModule() { 481 @Override 482 public void install() { 483 installQSimModule(qsimModule); 484 } 485 }); 486 return this ; 487 } 488 489 /** 490 * Only use if you know what you are doing, for experts only. 491 */ 492 @Override 493 public final Controler configureQSimComponents(QSimComponentsConfigurator configurator) { 494 this.addOverridingModule(new AbstractModule() { 495 @Override 496 public void install() { 497 QSimComponentsConfig components = new QSimComponentsConfig(); 498 new StandardQSimComponentConfigurator(config).configure(components); 499 configurator.configure(components); 500 bind(QSimComponentsConfig.class).toInstance(components); 501 } 502 }); 503 return this ; 504 } 505}