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}