Package org.movsim.simulator

Source Code of org.movsim.simulator.Simulator

/*
* Copyright (C) 2010, 2011, 2012 by Arne Kesting, Martin Treiber, Ralph Germ, Martin Budden
* <movsim.org@gmail.com>
* -----------------------------------------------------------------------------------------
*
* This file is part of
*
* MovSim - the multi-model open-source vehicular-traffic simulator.
*
* MovSim is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MovSim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MovSim. If not, see <http://www.gnu.org/licenses/>
* or <http://www.movsim.org>.
*
* -----------------------------------------------------------------------------------------
*/
package org.movsim.simulator;

import java.util.List;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.joda.time.format.DateTimeFormat;
import org.movsim.autogen.InitialConditions;
import org.movsim.autogen.MacroIC;
import org.movsim.autogen.MicroIC;
import org.movsim.autogen.Movsim;
import org.movsim.autogen.Parking;
import org.movsim.autogen.Road;
import org.movsim.autogen.Simulation;
import org.movsim.autogen.TrafficSink;
import org.movsim.input.ProjectMetaData;
import org.movsim.input.network.OpenDriveReader;
import org.movsim.output.SimulationOutput;
import org.movsim.output.detector.LoopDetectors;
import org.movsim.output.fileoutput.FileTrafficSourceData;
import org.movsim.roadmappings.RoadMapping;
import org.movsim.roadmappings.RoadMappingPolyS;
import org.movsim.simulator.roadnetwork.AbstractTrafficSource;
import org.movsim.simulator.roadnetwork.FlowConservingBottlenecks;
import org.movsim.simulator.roadnetwork.InflowTimeSeries;
import org.movsim.simulator.roadnetwork.InitialConditionsMacro;
import org.movsim.simulator.roadnetwork.LaneSegment;
import org.movsim.simulator.roadnetwork.Lanes;
import org.movsim.simulator.roadnetwork.MicroInflowFileReader;
import org.movsim.simulator.roadnetwork.RoadNetwork;
import org.movsim.simulator.roadnetwork.RoadSegment;
import org.movsim.simulator.roadnetwork.SimpleRamp;
import org.movsim.simulator.roadnetwork.TrafficSourceMacro;
import org.movsim.simulator.roadnetwork.TrafficSourceMicro;
import org.movsim.simulator.roadnetwork.routing.Routing;
import org.movsim.simulator.trafficlights.TrafficLights;
import org.movsim.simulator.vehicles.TestVehicle;
import org.movsim.simulator.vehicles.TrafficCompositionGenerator;
import org.movsim.simulator.vehicles.Vehicle;
import org.movsim.simulator.vehicles.VehicleFactory;
import org.movsim.utilities.MyRandom;
import org.movsim.utilities.Units;
import org.movsim.xml.MovsimInputLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import com.google.common.base.Preconditions;

public class Simulator implements SimulationTimeStep, SimulationRun.CompletionCallback {

    /** The Constant LOG. */
    private static final Logger LOG = LoggerFactory.getLogger(Simulator.class);

    private long startTimeMillis;

    private final ProjectMetaData projectMetaData;
    private String projectName;
    private Movsim inputData;

    private VehicleFactory vehicleFactory;
    private TrafficCompositionGenerator defaultTrafficComposition;
    private TrafficLights trafficLights;
    private SimulationOutput simOutput;
    private final RoadNetwork roadNetwork;
    private Routing routing;
    private final SimulationRunnable simulationRunnable;
    private int obstacleCount;
    private long timeOffsetMillis;

    /**
     * Constructor.
     *
     * @throws SAXException
     * @throws JAXBException
     */
    public Simulator() {
        this.projectMetaData = ProjectMetaData.getInstance();
        roadNetwork = new RoadNetwork();
        simulationRunnable = new SimulationRunnable(this);
        simulationRunnable.setCompletionCallback(this);
    }

    public void initialize() throws JAXBException, SAXException {
        LOG.info("Copyright '\u00A9' by Arne Kesting, Martin Treiber, Ralph Germ and Martin Budden (2011-2013)");

        projectName = projectMetaData.getProjectName();
        // TODO temporary handling of Variable Message Sign until added to XML
        roadNetwork.setHasVariableMessageSign(projectName.startsWith("routing"));

        inputData = MovsimInputLoader.getInputData(projectMetaData.getInputFile());

        timeOffsetMillis = 0;
        if (inputData.getScenario().getSimulation().isSetTimeOffset()) {
            DateTime dateTime = LocalDateTime.parse(inputData.getScenario().getSimulation().getTimeOffset(),
                    DateTimeFormat.forPattern("YYYY-MM-dd'T'HH:mm:ssZ")).toDateTime(DateTimeZone.UTC);
            timeOffsetMillis = dateTime.getMillis();
            LOG.info("global time offset set={} --> {} milliseconds.", dateTime, timeOffsetMillis);
            ProjectMetaData.getInstance().setTimeOffsetMillis(timeOffsetMillis);
        }
        projectMetaData.setXodrNetworkFilename(inputData.getScenario().getNetworkFilename()); // TODO

        Simulation simulationInput = inputData.getScenario().getSimulation();

        final boolean loadedRoadNetwork = parseOpenDriveXml(roadNetwork, projectMetaData);
        routing = new Routing(inputData.getScenario().getRoutes(), roadNetwork);
       
        vehicleFactory = new VehicleFactory(simulationInput.getTimestep(), inputData.getVehiclePrototypes(),
                inputData.getConsumption(), routing);

        roadNetwork.setWithCrashExit(simulationInput.isCrashExit());

        simulationRunnable.setTimeStep(simulationInput.getTimestep());

        // TODO better handling of case "duration = INFINITY"
        double duration = simulationInput.isSetDuration() ? simulationInput.getDuration() : -1;

        simulationRunnable.setDuration(duration < 0 ? Double.MAX_VALUE : duration);

        if (simulationInput.isWithSeed()) {
            MyRandom.initializeWithSeed(simulationInput.getSeed());
        }

        defaultTrafficComposition = new TrafficCompositionGenerator(simulationInput.getTrafficComposition(),
                vehicleFactory);

        // For each road in the MovSim XML input data, find the corresponding roadSegment and
        // set its input data accordingly
        // final Map<String, RoadInput> roadInputMap = simulationInput.get.getRoadInput();
        if (loadedRoadNetwork == false && simulationInput.getRoad().size() == 1) {
            defaultTestingRoadMapping(simulationInput.getRoad().get(0));
        } else {
            matchRoadSegmentsAndRoadInput(simulationInput.getRoad());
        }

        trafficLights = new TrafficLights(inputData.getScenario().getTrafficLights(), roadNetwork);

        reset();
    }


    public Iterable<String> getVehiclePrototypeLabels() {
        return vehicleFactory.getLabels();
    }

    public TrafficCompositionGenerator getVehicleGenerator() {
        return defaultTrafficComposition;
    }

    public ProjectMetaData getProjectMetaData() {
        return projectMetaData;
    }

    public RoadNetwork getRoadNetwork() {
        return roadNetwork;
    }

    public SimulationRunnable getSimulationRunnable() {
        return simulationRunnable;
    }

    /**
     * Load scenario from xml.
     *
     * @param scenario
     * @param path
     * @throws JAXBException
     * @throws SAXException
     * @throws ParserConfigurationException
     */
    public void loadScenarioFromXml(String scenario, String path) throws JAXBException, SAXException
             {
        roadNetwork.clear();
        projectMetaData.setProjectName(scenario);
        projectMetaData.setPathToProjectXmlFile(path);
        initialize();
    }

    private void matchRoadSegmentsAndRoadInput(List<Road> roads) {
        for (final Road roadInput : roads) {
            RoadSegment roadSegment = Preconditions.checkNotNull(roadNetwork.findByUserId(roadInput.getId()),
                    "cannot find roadId=" + roadInput.getId()
                            + " from input in constructed roadNetwork. IGNORE DATA!!!");
            addInputToRoadSegment(roadSegment, roadInput);
        }
    }

    /**
     * There was no xodr file and there is only one road segment in the MovSimXML file so set up a default s-shaped road
     * mapping.
     *
     * @param road
     */
    private void defaultTestingRoadMapping(Road roadInput) {
        LOG.warn("Simulation with test network");
        final int laneCount = 1;
        final double roadLength = 1500;
        final RoadMapping roadMapping = new RoadMappingPolyS(laneCount, 10, 50, 50, 100.0 / Math.PI, roadLength);
        final RoadSegment roadSegment = new RoadSegment(roadMapping);
        addInputToRoadSegment(roadSegment, roadInput);
        roadSegment.setUserId("1");
        roadSegment.addDefaultSink();
        roadNetwork.add(roadSegment);
    }

    /**
     * Parse the OpenDrive (.xodr) file to load the network topology and road layout.
     *
     * @param projectMetaData
     * @return
     * @throws SAXException
     * @throws JAXBException
     * @throws ParserConfigurationException
     */
    private static boolean parseOpenDriveXml(RoadNetwork roadNetwork, ProjectMetaData projectMetaData)
            throws JAXBException, SAXException {
        final String xodrFileName = projectMetaData.getXodrNetworkFilename();
        final String xodrPath = projectMetaData.getPathToProjectFile();
        final String fullXodrFileName = xodrPath + xodrFileName;
        LOG.info("try to load {}", fullXodrFileName);
        final boolean loaded = OpenDriveReader.loadRoadNetwork(roadNetwork, fullXodrFileName);
        LOG.info("done with parsing road network {}. Success: {}", fullXodrFileName, loaded);
        return loaded;
    }

    /**
     * Add input data to road segment.
     *
     * Note by rules of encapsulation this function is NOT a member of RoadSegment, since RoadSegment should not be
     * aware of form of XML file or RoadInput data structure.
     *
     * @param roadSegment
     * @param roadInput
     */
    private void addInputToRoadSegment(RoadSegment roadSegment, Road roadInput) {
        // setup own vehicle generator for roadSegment: needed for trafficSource and initial conditions
        TrafficCompositionGenerator composition = roadInput.isSetTrafficComposition() ? new TrafficCompositionGenerator(
                roadInput.getTrafficComposition(), vehicleFactory) : defaultTrafficComposition;
        if (roadInput.isSetTrafficComposition()) {
            LOG.info("road with id={} has its own vehicle composition generator.", roadSegment.id());
        }

        // set up the traffic source
        if (roadInput.isSetTrafficSource()) {
            final org.movsim.autogen.TrafficSource trafficSourceData = roadInput.getTrafficSource();
            AbstractTrafficSource trafficSource = null;
            if (trafficSourceData.isSetInflow()) {
                InflowTimeSeries inflowTimeSeries = new InflowTimeSeries(trafficSourceData.getInflow());
                trafficSource = new TrafficSourceMacro(composition, roadSegment, inflowTimeSeries);
            } else if (trafficSourceData.isSetInflowFromFile()) {
                trafficSource = new TrafficSourceMicro(composition, roadSegment);
                MicroInflowFileReader reader = new MicroInflowFileReader(trafficSourceData.getInflowFromFile(),
                        roadSegment.laneCount(), timeOffsetMillis, routing, (TrafficSourceMicro) trafficSource);
                reader.readData();
            }
            if (trafficSource != null) {
                if (trafficSourceData.isLogging()) {
                    trafficSource.setRecorder(new FileTrafficSourceData(roadSegment.userId()));
                }
                roadSegment.setTrafficSource(trafficSource);
            }
        }

        // set up the traffic sink
        if (roadInput.isSetTrafficSink()) {
            createParkingSink(roadInput.getTrafficSink(), roadSegment);
        }

        // set up simple ramp with dropping mechanism
        if (roadInput.isSetSimpleRamp()) {
            org.movsim.autogen.SimpleRamp simpleRampData = roadInput.getSimpleRamp();
            InflowTimeSeries inflowTimeSeries = new InflowTimeSeries(simpleRampData.getInflow());
            SimpleRamp simpleRamp = new SimpleRamp(composition, roadSegment, simpleRampData, inflowTimeSeries);
            if (simpleRampData.isLogging()) {
                simpleRamp.setRecorder(new FileTrafficSourceData(roadSegment.userId()));
            }
            roadSegment.setSimpleRamp(simpleRamp);
        }

        // set up the detectors
        if (roadInput.isSetDetectors()) {
            roadSegment.setLoopDetectors(new LoopDetectors(roadSegment, roadInput.getDetectors()));
        }
        // set up the flow conserving bottlenecks
        if (roadInput.isSetFlowConservingInhomogeneities()) {
            roadSegment.setFlowConservingBottlenecks(new FlowConservingBottlenecks(roadInput
                    .getFlowConservingInhomogeneities()));
        }

        if (roadInput.isSetInitialConditions()) {
            initialConditions(roadSegment, roadInput.getInitialConditions(), composition);
        }

    }

    private void createParkingSink(TrafficSink trafficSink, RoadSegment roadSegment) {
        // setup source which models the re-entrance of vehicles leaving the parking lot
        if (!trafficSink.isSetParking()) {
            return;
        }
        Parking parking = trafficSink.getParking();
        RoadSegment sourceRoadSegment = Preconditions.checkNotNull(roadNetwork.findByUserId(parking.getSourceRoadId()),
                "cannot find roadSegment=" + parking.getSourceRoadId() + " specified as re-entrance from the road="
                        + roadSegment.id());
        TrafficSourceMicro trafficSource = new TrafficSourceMicro(defaultTrafficComposition, sourceRoadSegment);
        if (trafficSink.isLogging()) {
            trafficSource.setRecorder(new FileTrafficSourceData(sourceRoadSegment.userId()));
        }
        sourceRoadSegment.setTrafficSource(trafficSource);
        roadSegment.sink().setupParkingLot(parking, timeOffsetMillis, trafficSource);
    }

    private static void initialConditions(RoadSegment roadSegment, InitialConditions initialConditions,
            TrafficCompositionGenerator vehGenerator) {
        Preconditions.checkNotNull(initialConditions);
        if (initialConditions.isSetMacroIC()) {
            setMacroInitialConditions(roadSegment, initialConditions.getMacroIC(), vehGenerator);
        } else if (initialConditions.isSetMicroIC()) {
            setMicroInitialConditions(roadSegment, initialConditions.getMicroIC(), vehGenerator);
        } else {
            LOG.info("no initial conditions defined");
        }
    }

    /**
     * Determine vehicle positions on all relevant lanes while considering minimum gaps to avoid accidents. Gaps are
     * left at the beginning and the end of the road segment on purpose. However, the consistency check is not complete
     * and other segments are not considered.
     *
     * @param roadSegment
     * @param macroInitialConditions
     * @param vehGenerator
     */
    private static void setMacroInitialConditions(RoadSegment roadSegment, List<MacroIC> macroInitialConditions,
            TrafficCompositionGenerator vehGenerator) {

        LOG.info("choose macro initial conditions: generate vehicles from macro-localDensity ");
        final InitialConditionsMacro icMacro = new InitialConditionsMacro(macroInitialConditions);

        for (LaneSegment laneSegment : roadSegment.laneSegments()) {
            if (laneSegment.type() != Lanes.Type.TRAFFIC) {
                LOG.debug("no macroscopic initial conditions for non-traffic lanes (slip roads etc).");
                continue;
            }

            double position = roadSegment.roadLength(); // start at end of segment
            while (position > 0) {
                final TestVehicle testVehicle = vehGenerator.getTestVehicle();

                final double rhoLocal = icMacro.rho(position);
                double speedInit = icMacro.hasUserDefinedSpeeds() ? icMacro.vInit(position) : testVehicle
                        .getEquilibriumSpeed(rhoLocal);
                if (LOG.isDebugEnabled() && !icMacro.hasUserDefinedSpeeds()) {
                    LOG.debug("use equilibrium speed={} in macroscopic initial conditions.", speedInit);
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug(String
                            .format("macroscopic init conditions from input: roadId=%s, x=%.3f, rho(x)=%.3f/km, speed=%.2fkm/h",
                                    roadSegment.id(), position, Units.INVM_TO_INVKM * rhoLocal, Units.MS_TO_KMH
                                            * speedInit));
                }

                if (rhoLocal <= 0) {
                    LOG.debug("no vehicle added at x={} for vanishing initial localDensity={}.", position, rhoLocal);
                    position -= 50; // move on in upstream direction
                    continue;
                }

                final Vehicle veh = vehGenerator.createVehicle(testVehicle);
                final double meanDistanceInLane = 1. / (rhoLocal + MovsimConstants.SMALL_VALUE);
                // TODO icMacro for ca
                // final double minimumGap = veh.getLongitudinalModel().isCA() ? veh.getLength() : veh.getLength() +
                // veh.getLongitudinalModel().getS0();
                final double minimumGap = veh.getLength() + veh.getLongitudinalModel().getMinimumGap();
                final double posDecrement = Math.max(meanDistanceInLane, minimumGap);
                position -= posDecrement;

                if (position <= posDecrement) {
                    LOG.debug("leave minimum gap at origin of road segment and start with next lane, pos={}", position);
                    break;
                }
                final Vehicle leader = laneSegment.rearVehicle();
                final double gapToLeader = (leader == null) ? MovsimConstants.GAP_INFINITY : leader.getRearPosition()
                        - position;

                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format(
                            "meanDistance=%.3f, minimumGap=%.2f, posDecrement=%.3f, gapToLeader=%.3f\n",
                            meanDistanceInLane, minimumGap, posDecrement, gapToLeader));
                }

                if (gapToLeader > 0) {
                    veh.setFrontPosition(position);
                    veh.setSpeed(speedInit);
                    veh.setLane(laneSegment.lane());
                    LOG.debug("add vehicle from macroscopic initial conditions at pos={} with speed={}.", position,
                            speedInit);
                    roadSegment.addVehicle(veh);
                } else {
                    LOG.debug("cannot add vehicle due to gap constraints at pos={} with speed={}.", position, speedInit);
                }

            }
        }
    }

    private static void setMicroInitialConditions(RoadSegment roadSegment, List<MicroIC> initialMicroConditions,
            TrafficCompositionGenerator vehGenerator) {
        LOG.debug(("choose micro initial conditions"));
        int vehicleNumber = 1;
        for (final MicroIC ic : initialMicroConditions) {
            // TODO counter
            final String vehTypeFromFile = ic.getLabel();
            final Vehicle veh = (vehTypeFromFile.length() == 0) ? vehGenerator.createVehicle() : vehGenerator
                    .createVehicle(vehTypeFromFile);
            veh.setVehNumber(vehicleNumber);
            vehicleNumber++;
            // testwise:
            veh.setFrontPosition(Math.round(ic.getPosition() / veh.physicalQuantities().getxScale()));
            veh.setSpeed(Math.round(ic.getSpeed() / veh.physicalQuantities().getvScale()));
            final int lane = ic.getLane();
            if (lane < Lanes.MOST_INNER_LANE || lane > roadSegment.laneCount()) {
                throw new IllegalArgumentException("lane=" + lane
                        + " given in initial condition does not exist for road=" + roadSegment.id()
                        + " which has a laneCount of " + roadSegment.laneCount());
            }
            veh.setLane(lane);
            roadSegment.addVehicle(veh);
            LOG.info(String.format("set vehicle with label = %s on lane=%d with front at x=%.2f, speed=%.2f",
                    veh.getLabel(), veh.lane(), veh.getFrontPosition(), veh.getSpeed()));
            if (veh.getLongitudinalModel().isCA()) {
                LOG.info(String.format("and for the CA in physical quantities: front position at x=%.2f, speed=%.2f",
                        veh.physicalQuantities().getFrontPosition(), veh.physicalQuantities().getSpeed()));
            }
        }
    }

    public void reset() {
        simulationRunnable.reset();
        if (inputData.getScenario().isSetOutputConfiguration()) {
            simOutput = new SimulationOutput(simulationRunnable.timeStep(),
                    projectMetaData.isInstantaneousFileOutput(), inputData.getScenario().getOutputConfiguration(),
                    roadNetwork, routing, vehicleFactory);
        }
        obstacleCount = roadNetwork.obstacleCount();
    }

    public void runToCompletion() {
        LOG.info("Simulator.run: start simulation at {} seconds of simulation project={}",
                simulationRunnable.simulationTime(), projectName);

        startTimeMillis = System.currentTimeMillis();
        // TODO check if first output update has to be called in update for external call!!
        // TODO FloatingCars do not need this call. First output line for t=0 is written twice to file
        // simOutput.timeStep(simulationRunnable.timeStep(), simulationRunnable.simulationTime(),
        // simulationRunnable.iterationCount());
        simulationRunnable.runToCompletion();
    }

    /**
     * Returns true if the simulation has finished.
     */
    public boolean isFinished() {
        if (simulationRunnable.simulationTime() > 60.0 && roadNetwork.vehicleCount() == obstacleCount) {
            return true;
        }
        return false;
    }

    @Override
    public void simulationComplete(double simulationTime) {
        LOG.info(String.format("Simulator.run: stop after time = %.2fs = %.2fh of simulation project=%s",
                simulationTime, simulationTime / 3600, projectName));
        final double elapsedTime = 0.001 * (System.currentTimeMillis() - startTimeMillis);
        LOG.info(String.format(
                "time elapsed = %.3fs --> simulation time warp = %.2f, time per 1000 update steps=%.3fs", elapsedTime,
                simulationTime / elapsedTime, 1000 * elapsedTime / simulationRunnable.iterationCount()));
    }

    @Override
    public void timeStep(double dt, double simulationTime, long iterationCount) {
        if (iterationCount % 200 == 0) {
            if (LOG.isInfoEnabled()) {
                LOG.info(String.format("Simulator.update :time = %.2fs = %.2fh, dt = %.2fs, projectName=%s",
                        simulationTime, simulationTime / 3600, dt, projectName));
            }
        }

        trafficLights.timeStep(dt, simulationTime, iterationCount);
        roadNetwork.timeStep(dt, simulationTime, iterationCount);
        if (simOutput != null) {
            simOutput.timeStep(dt, simulationTime, iterationCount);
        }
    }
}
TOP

Related Classes of org.movsim.simulator.Simulator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.