LCOV - code coverage report
Current view: top level - src/microsim/cfmodels - MSCFModel_Rail.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 89.6 % 134 120
Test Date: 2025-12-06 15:35:27 Functions: 88.9 % 18 16

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2012-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSCFModel_Rail.cpp
      15              : /// @author  Gregor Laemmel
      16              : /// @author  Leander Flamm
      17              : /// @date    Tue, 08 Feb 2017
      18              : ///
      19              : // <description missing>
      20              : /****************************************************************************/
      21              : #include <config.h>
      22              : 
      23              : #include <iostream>
      24              : #include <utils/common/MsgHandler.h>
      25              : #include <utils/common/StringUtils.h>
      26              : #include <utils/common/StringTokenizer.h>
      27              : #include <utils/geom/GeomHelper.h>
      28              : #include <microsim/MSVehicle.h>
      29              : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
      30              : #include "MSCFModel_Rail.h"
      31              : 
      32              : // ===========================================================================
      33              : // trainParams method definitions
      34              : // ===========================================================================
      35              : 
      36              : double
      37      2365163 : MSCFModel_Rail::TrainParams::getResistance(double speed) const {
      38      2365163 :     if (resCoef_constant != INVALID_DOUBLE) {
      39        20704 :         return (resCoef_quadratic * speed * speed + resCoef_linear * speed + resCoef_constant); // kN
      40              :     } else {
      41      2344459 :         return LinearApproxHelpers::getInterpolatedValue(resistance, speed); // kN
      42              :     }
      43              : }
      44              : 
      45              : 
      46              : double
      47      1518850 : MSCFModel_Rail::TrainParams::getTraction(double speed) const {
      48      1518850 :     if (maxPower != INVALID_DOUBLE) {
      49         6596 :         return MIN2(maxPower / speed, maxTraction); // kN
      50              :     } else {
      51      1512254 :         return LinearApproxHelpers::getInterpolatedValue(traction, speed); // kN
      52              :     }
      53              : }
      54              : 
      55              : // ===========================================================================
      56              : // method definitions
      57              : // ===========================================================================
      58              : 
      59              : 
      60          358 : MSCFModel_Rail::MSCFModel_Rail(const MSVehicleType* vtype) :
      61          358 :     MSCFModel(vtype) {
      62          358 :     const std::string trainType = vtype->getParameter().getCFParamString(SUMO_ATTR_TRAIN_TYPE, "NGT400");
      63          358 :     if (trainType.compare("RB425") == 0) {
      64           38 :         myTrainParams = initRB425Params();
      65          320 :     } else if (trainType.compare("RB628") == 0) {
      66           19 :         myTrainParams = initRB628Params();
      67          301 :     } else if (trainType.compare("NGT400") == 0) {
      68          116 :         myTrainParams = initNGT400Params();
      69          185 :     } else if (trainType.compare("NGT400_16") == 0) {
      70            4 :         myTrainParams = initNGT400_16Params();
      71          181 :     } else if (trainType.compare("ICE1") == 0) {
      72            9 :         myTrainParams = initICE1Params();
      73          172 :     } else if (trainType.compare("REDosto7") == 0) {
      74           89 :         myTrainParams = initREDosto7Params();
      75           83 :     } else if (trainType.compare("Freight") == 0) {
      76           58 :         myTrainParams = initFreightParams();
      77           25 :     } else if (trainType.compare("ICE3") == 0) {
      78            5 :         myTrainParams = initICE3Params();
      79           20 :     } else if (trainType.compare("MireoPlusB") == 0) {
      80            4 :         myTrainParams = initMireoPlusB2TParams();
      81           16 :     } else if (trainType.compare("MireoPlusH") == 0) {
      82            4 :         myTrainParams = initMireoPlusH2TParams();
      83           12 :     } else if (trainType.compare("custom") == 0) {
      84           12 :         myTrainParams = initCustomParams();
      85              :     } else {
      86            0 :         WRITE_ERRORF(TL("Unknown train type: %. Exiting!"), trainType);
      87            0 :         throw ProcessError();
      88              :     }
      89              :     // override with user values
      90          358 :     if (vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) {
      91           36 :         myTrainParams.vmax = vtype->getMaxSpeed();
      92              :     }
      93          358 :     if (vtype->wasSet(VTYPEPARS_LENGTH_SET)) {
      94           67 :         myTrainParams.length = vtype->getLength();
      95              :     }
      96          358 :     myTrainParams.mf = vtype->getParameter().getCFParam(SUMO_ATTR_MASSFACTOR, myTrainParams.mf);
      97          358 :     myTrainParams.decl = vtype->getParameter().getCFParam(SUMO_ATTR_DECEL, myTrainParams.decl);
      98              :     setMaxDecel(myTrainParams.decl);
      99          358 :     setEmergencyDecel(vtype->getParameter().getCFParam(SUMO_ATTR_EMERGENCYDECEL, myTrainParams.decl + 0.3));
     100              :     // update type parameters so they are shown correctly in the gui (if defaults from trainType are used)
     101          358 :     const_cast<MSVehicleType*>(vtype)->setMaxSpeed(myTrainParams.vmax);
     102          358 :     const_cast<MSVehicleType*>(vtype)->setLength(myTrainParams.length);
     103          358 :     if (!vtype->wasSet(VTYPEPARS_MASS_SET)) {
     104              :         // tons to kg
     105          331 :         const_cast<MSVehicleType*>(vtype)->setMass(myTrainParams.weight * 1000);
     106              :     }
     107              : 
     108              :     // init tabular curves
     109          358 :     myTrainParams.traction = vtype->getParameter().getCFProfile(SUMO_ATTR_TRACTION_TABLE, myTrainParams.traction);
     110          358 :     myTrainParams.resistance = vtype->getParameter().getCFProfile(SUMO_ATTR_RESISTANCE_TABLE, myTrainParams.resistance);
     111              : 
     112              :     // init parametric curves
     113          358 :     myTrainParams.maxPower = vtype->getParameter().getCFParam(SUMO_ATTR_MAXPOWER, INVALID_DOUBLE);
     114          358 :     myTrainParams.maxTraction = vtype->getParameter().getCFParam(SUMO_ATTR_MAXTRACTION, INVALID_DOUBLE);
     115          358 :     myTrainParams.resCoef_constant = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_CONSTANT, INVALID_DOUBLE);
     116          358 :     myTrainParams.resCoef_linear = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_LINEAR, INVALID_DOUBLE);
     117          358 :     myTrainParams.resCoef_quadratic = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_QUADRATIC, INVALID_DOUBLE);
     118              : 
     119          358 :     if (myTrainParams.maxPower != INVALID_DOUBLE && myTrainParams.maxTraction == INVALID_DOUBLE) {
     120            0 :         throw ProcessError(TLF("Undefined maxPower for vType '%'.", vtype->getID()));
     121          358 :     } else if (myTrainParams.maxPower == INVALID_DOUBLE && myTrainParams.maxTraction != INVALID_DOUBLE) {
     122            0 :         throw ProcessError(TLF("Undefined maxTraction for vType '%'.", vtype->getID()));
     123              :     }
     124          370 :     if (myTrainParams.maxPower != INVALID_DOUBLE && vtype->getParameter().getCFParamString(SUMO_ATTR_TRACTION_TABLE, "") != "") {
     125            0 :         WRITE_WARNING(TLF("Ignoring tractionTable because maxPower and maxTraction are set for vType '%'.", vtype->getID()));
     126              :     }
     127          358 :     const bool hasSomeResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     128          354 :                                  || myTrainParams.resCoef_linear != INVALID_DOUBLE
     129          712 :                                  || myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     130              :     const bool hasAllResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     131            4 :                                 && myTrainParams.resCoef_linear != INVALID_DOUBLE
     132          362 :                                 && myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     133          358 :     if (hasSomeResCoef && !hasAllResCoef) {
     134            0 :         throw ProcessError(TLF("Some undefined resistance coefficients for vType '%' (requires resCoef_constant, resCoef_linear and resCoef_quadratic)", vtype->getID()));
     135              :     }
     136          370 :     if (myTrainParams.resCoef_constant != INVALID_DOUBLE && vtype->getParameter().getCFParamString(SUMO_ATTR_RESISTANCE_TABLE, "") != "") {
     137            4 :         WRITE_WARNING(TLF("Ignoring resistanceTable because resistance coefficients are set for vType '%'.", vtype->getID()));
     138              :     }
     139              : 
     140          358 :     if (myTrainParams.traction.empty() && myTrainParams.maxPower == INVALID_DOUBLE) {
     141           12 :         throw ProcessError(TLF("Either tractionTable or maxPower must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     142              :     }
     143          354 :     if (myTrainParams.resistance.empty() && myTrainParams.resCoef_constant == INVALID_DOUBLE) {
     144            0 :         throw ProcessError(TLF("Either resistanceTable or resCoef_constant must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     145              :     }
     146          362 : }
     147              : 
     148              : 
     149          706 : MSCFModel_Rail::~MSCFModel_Rail() { }
     150              : 
     151              : 
     152        88786 : double MSCFModel_Rail::followSpeed(const MSVehicle* const veh, double speed, double gap,
     153              :                                    double /* predSpeed */, double /* predMaxDecel*/, const MSVehicle* const /*pred*/, const CalcReason /*usage*/) const {
     154              : 
     155              :     // followSpeed module is used for the simulation of moving block operations. The safety gap is chosen similar to the existing german
     156              :     // system CIR-ELKE (based on LZB). Other implementations of moving block systems may differ, but for now no appropriate parameter
     157              :     // can be set (would be per lane, not per train) -> hard-coded
     158              : 
     159              :     // @note: default train minGap of 5 is already subtracted from gap
     160        88786 :     if (speed >= 30 / 3.6) {
     161              :         // safety distance for higher speeds (>= 30 km/h)
     162        57520 :         gap = MAX2(0.0, gap + veh->getVehicleType().getMinGap() - 50);
     163              :     }
     164              : 
     165        88786 :     const double vsafe = maximumSafeStopSpeed(gap, myDecel, speed, false, TS, false); // absolute breaking distance
     166        88786 :     const double vmin = minNextSpeed(speed, veh);
     167        88786 :     const double vmax = maxNextSpeed(speed, veh);
     168              : 
     169        88786 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     170              :         return MIN2(vsafe, vmax);
     171              :     } else {
     172              :         // ballistic
     173              :         // XXX: the euler variant can break as strong as it wishes immediately! The ballistic cannot, refs. #2575.
     174              :         return MAX2(MIN2(vsafe, vmax), vmin);
     175              :     }
     176              : }
     177              : 
     178              : 
     179              : int
     180            0 : MSCFModel_Rail::getModelID() const {
     181            0 :     return SUMO_TAG_CF_RAIL;
     182              : }
     183              : 
     184              : 
     185              : MSCFModel*
     186           11 : MSCFModel_Rail::duplicate(const MSVehicleType* vtype) const {
     187           11 :     return new MSCFModel_Rail(vtype);
     188              : }
     189              : 
     190              : double
     191      2365163 : MSCFModel_Rail::getRotWeight(const MSVehicle* const veh) const {
     192      2365163 :     return getWeight(veh) * myTrainParams.mf;
     193              : }
     194              : 
     195              : double
     196      4730326 : MSCFModel_Rail::getWeight(const MSVehicle* const veh) const {
     197              :     // kg to tons
     198      4730326 :     return veh->getVehicleType().getMass() / 1000;
     199              : }
     200              : 
     201      1653125 : double MSCFModel_Rail::maxNextSpeed(double speed, const MSVehicle* const veh) const {
     202              : 
     203      1653125 :     if (speed >= myTrainParams.vmax) {
     204              :         return myTrainParams.vmax;
     205              :     }
     206              : 
     207              :     double targetSpeed = myTrainParams.vmax;
     208              : 
     209      1518850 :     double res = myTrainParams.getResistance(speed); // kN
     210              : 
     211      1518850 :     double slope = veh->getSlope();
     212      1518850 :     double gr = getWeight(veh) * GRAVITY * sin(DEG2RAD(slope)); //kN
     213              : 
     214      1518850 :     double totalRes = res + gr; //kN
     215              : 
     216      1518850 :     double trac = myTrainParams.getTraction(speed); // kN
     217              :     double a;
     218      1518850 :     if (speed < targetSpeed) {
     219      1518850 :         a = (trac - totalRes) / getRotWeight(veh); //kN/t == N/kg
     220              :     } else {
     221              :         a = 0.;
     222            0 :         if (totalRes > trac) {
     223            0 :             a = (trac - totalRes) / getRotWeight(veh); //kN/t == N/kg
     224              :         }
     225              :     }
     226      1518850 :     double maxNextSpeed = speed + ACCEL2SPEED(a);
     227              : 
     228              : //    std::cout << veh->getID() << " speed: " << (speed*3.6) << std::endl;
     229              : 
     230      1518850 :     return MIN2(myTrainParams.vmax, maxNextSpeed);
     231              : }
     232              : 
     233              : 
     234       846313 : double MSCFModel_Rail::minNextSpeed(double speed, const MSVehicle* const veh) const {
     235              : 
     236       846313 :     const double slope = veh->getSlope();
     237       846313 :     const double gr = getWeight(veh) * GRAVITY * sin(DEG2RAD(slope)); //kN
     238       846313 :     const double res = myTrainParams.getResistance(speed); // kN
     239       846313 :     const double totalRes = res + gr; //kN
     240       846313 :     const double a = myTrainParams.decl + totalRes / getRotWeight(veh);
     241       846313 :     const double vMin = speed - ACCEL2SPEED(a);
     242       846313 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     243              :         return MAX2(vMin, 0.);
     244              :     } else {
     245              :         // NOTE: ballistic update allows for negative speeds to indicate a stop within the next timestep
     246              :         return vMin;
     247              :     }
     248              : 
     249              : }
     250              : 
     251              : 
     252              : double
     253       250885 : MSCFModel_Rail::minNextSpeedEmergency(double speed, const MSVehicle* const veh) const {
     254       250885 :     return minNextSpeed(speed, veh);
     255              : }
     256              : 
     257              : 
     258              : //void
     259              : //MSCFModel_Rail::initVehicleVariables(const MSVehicle *const veh, MSCFModel_Rail::VehicleVariables *pVariables) const {
     260              : //
     261              : //    pVariables->setInitialized();
     262              : //
     263              : //}
     264              : 
     265              : 
     266            0 : double MSCFModel_Rail::getSpeedAfterMaxDecel(double /* speed */) const {
     267              : 
     268              : //    //TODO: slope not known here
     269              : //    double gr = 0; //trainParams.weight * GRAVITY * edge.grade
     270              : //
     271              : //    double a = 0;//trainParams.decl - gr/trainParams.rotWeight;
     272              : //
     273              : //    return speed + a * DELTA_T / 1000.;
     274            0 :     WRITE_ERROR("function call not allowed for rail model. Exiting!");
     275            0 :     throw ProcessError();
     276              : }
     277              : 
     278              : 
     279          438 : MSCFModel::VehicleVariables* MSCFModel_Rail::createVehicleVariables() const {
     280          438 :     VehicleVariables* ret = new VehicleVariables();
     281          438 :     return ret;
     282              : }
     283              : 
     284              : 
     285       250885 : double MSCFModel_Rail::finalizeSpeed(MSVehicle* const veh, double vPos) const {
     286       250885 :     return MSCFModel::finalizeSpeed(veh, vPos);
     287              : }
     288              : 
     289              : 
     290       936196 : double MSCFModel_Rail::freeSpeed(const MSVehicle* const /* veh */, double /* speed */, double dist, double targetSpeed,
     291              :                                  const bool onInsertion, const CalcReason /*usage*/) const {
     292              : 
     293              : //    MSCFModel_Rail::VehicleVariables *vars = (MSCFModel_Rail::VehicleVariables *) veh->getCarFollowVariables();
     294              : //    if (vars->isNotYetInitialized()) {
     295              : //        initVehicleVariables(veh, vars);
     296              : //    }
     297              : 
     298              :     //TODO: signals, coasting, ...
     299              : 
     300       936196 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     301              :         // adapt speed to succeeding lane, no reaction time is involved
     302              :         // when breaking for y steps the following distance g is covered
     303              :         // (drive with v in the final step)
     304              :         // g = (y^2 + y) * 0.5 * b + y * v
     305              :         // y = ((((sqrt((b + 2.0*v)*(b + 2.0*v) + 8.0*b*g)) - b)*0.5 - v)/b)
     306       936195 :         const double v = SPEED2DIST(targetSpeed);
     307       936195 :         if (dist < v) {
     308              :             return targetSpeed;
     309              :         }
     310       842744 :         const double b = ACCEL2DIST(myDecel);
     311       842744 :         const double y = MAX2(0.0, ((sqrt((b + 2.0 * v) * (b + 2.0 * v) + 8.0 * b * dist) - b) * 0.5 - v) / b);
     312       842744 :         const double yFull = floor(y);
     313       842744 :         const double exactGap = (yFull * yFull + yFull) * 0.5 * b + yFull * v + (y > yFull ? v : 0.0);
     314       842744 :         const double fullSpeedGain = (yFull + (onInsertion ? 1. : 0.)) * ACCEL2SPEED(myTrainParams.decl);
     315      1123861 :         return DIST2SPEED(MAX2(0.0, dist - exactGap) / (yFull + 1)) + fullSpeedGain + targetSpeed;
     316              :     } else {
     317            1 :         WRITE_ERROR(TL("Anything else than semi implicit euler update is not yet implemented. Exiting!"));
     318            1 :         throw ProcessError();
     319              :     }
     320              : }
     321              : 
     322              : 
     323      1008407 : double MSCFModel_Rail::stopSpeed(const MSVehicle* const veh, const double speed, double gap, double decel, const CalcReason /*usage*/) const {
     324      1008407 :     return MIN2(maximumSafeStopSpeed(gap, decel, speed, false, TS, false), maxNextSpeed(speed, veh));
     325              : }
        

Generated by: LCOV version 2.0-1