LCOV - code coverage report
Current view: top level - src/microsim/cfmodels - MSCFModel_Rail.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.6 % 149 135
Test Date: 2026-06-15 15:46:12 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-2026 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      2617707 : MSCFModel_Rail::TrainParams::getResistance(double speed) const {
      38      2617707 :     if (resCoef_constant != INVALID_DOUBLE) {
      39        20704 :         return (resCoef_quadratic * speed * speed + resCoef_linear * speed + resCoef_constant); // kN
      40              :     } else {
      41      2597003 :         return LinearApproxHelpers::getInterpolatedValue(resistance, speed); // kN
      42              :     }
      43              : }
      44              : 
      45              : 
      46              : double
      47      1687048 : MSCFModel_Rail::TrainParams::getTraction(double speed) const {
      48      1687048 :     if (maxPower != INVALID_DOUBLE) {
      49         6596 :         return MIN2(maxPower / speed, maxTraction); // kN
      50              :     } else {
      51      1680452 :         return LinearApproxHelpers::getInterpolatedValue(traction, speed); // kN
      52              :     }
      53              : }
      54              : 
      55              : // ===========================================================================
      56              : // method definitions
      57              : // ===========================================================================
      58              : 
      59              : 
      60          390 : MSCFModel_Rail::MSCFModel_Rail(const MSVehicleType* vtype) :
      61          390 :     MSCFModel(vtype) {
      62          390 :     const std::string trainType = vtype->getParameter().getCFParamString(SUMO_ATTR_TRAIN_TYPE, "NGT400");
      63          390 :     if (trainType.compare("RB425") == 0) {
      64           58 :         myTrainParams = initRB425Params();
      65          332 :     } else if (trainType.compare("RB628") == 0) {
      66           19 :         myTrainParams = initRB628Params();
      67          313 :     } else if (trainType.compare("NGT400") == 0) {
      68          116 :         myTrainParams = initNGT400Params();
      69          197 :     } else if (trainType.compare("NGT400_16") == 0) {
      70            4 :         myTrainParams = initNGT400_16Params();
      71          193 :     } else if (trainType.compare("ICE1") == 0) {
      72            9 :         myTrainParams = initICE1Params();
      73          184 :     } else if (trainType.compare("REDosto7") == 0) {
      74           89 :         myTrainParams = initREDosto7Params();
      75           95 :     } else if (trainType.compare("Freight") == 0) {
      76           70 :         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          390 :     if (vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) {
      91           36 :         myTrainParams.vmax = vtype->getMaxSpeed();
      92              :     }
      93          390 :     if (vtype->wasSet(VTYPEPARS_LENGTH_SET)) {
      94           67 :         myTrainParams.length = vtype->getLength();
      95              :     }
      96          390 :     myTrainParams.mf = vtype->getParameter().getCFParam(SUMO_ATTR_MASSFACTOR, myTrainParams.mf);
      97          390 :     myTrainParams.decl = vtype->getParameter().getCFParam(SUMO_ATTR_DECEL, myTrainParams.decl);
      98              :     setMaxDecel(myTrainParams.decl);
      99          390 :     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          390 :     const_cast<MSVehicleType*>(vtype)->setMaxSpeed(myTrainParams.vmax);
     102          390 :     const_cast<MSVehicleType*>(vtype)->setLength(myTrainParams.length);
     103          390 :     if (!vtype->wasSet(VTYPEPARS_MASS_SET)) {
     104              :         // tons to kg
     105          359 :         const_cast<MSVehicleType*>(vtype)->setMass(myTrainParams.weight * 1000);
     106              :     }
     107              : 
     108              :     // init tabular curves
     109          390 :     myTrainParams.traction = vtype->getParameter().getCFProfile(SUMO_ATTR_TRACTION_TABLE, myTrainParams.traction);
     110          390 :     myTrainParams.resistance = vtype->getParameter().getCFProfile(SUMO_ATTR_RESISTANCE_TABLE, myTrainParams.resistance);
     111              : 
     112              :     // init parametric curves
     113          390 :     myTrainParams.maxPower = vtype->getParameter().getCFParam(SUMO_ATTR_MAXPOWER, INVALID_DOUBLE);
     114          390 :     myTrainParams.maxTraction = vtype->getParameter().getCFParam(SUMO_ATTR_MAXTRACTION, INVALID_DOUBLE);
     115          390 :     myTrainParams.resCoef_constant = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_CONSTANT, INVALID_DOUBLE);
     116          390 :     myTrainParams.resCoef_linear = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_LINEAR, INVALID_DOUBLE);
     117          390 :     myTrainParams.resCoef_quadratic = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_QUADRATIC, INVALID_DOUBLE);
     118              :     // curve resistance parameters
     119          390 :     myTrainParams.curveResistance = vtype->getParameter().getCFParam(SUMO_ATTR_CURVE_RESISTANCE, myTrainParams.curveResistance);
     120          390 :     myTrainParams.roeckl_sharp_radius = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_SHARP_RADIUS, myTrainParams.roeckl_sharp_radius);
     121          390 :     myTrainParams.roeckl_numerator = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_NUMERATOR, myTrainParams.roeckl_numerator);
     122          390 :     myTrainParams.roeckl_numerator_sharp = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_NUMERATOR_SHARP, myTrainParams.roeckl_numerator_sharp);
     123          390 :     myTrainParams.roeckl_offset = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_OFFSET, myTrainParams.roeckl_offset);
     124          390 :     myTrainParams.roeckl_offset_sharp = vtype->getParameter().getCFParam(SUMO_ATTR_ROECKL_OFFSET_SHARP, myTrainParams.roeckl_offset_sharp);
     125              : 
     126          390 :     if (myTrainParams.maxPower != INVALID_DOUBLE && myTrainParams.maxTraction == INVALID_DOUBLE) {
     127            0 :         throw ProcessError(TLF("Undefined maxPower for vType '%'.", vtype->getID()));
     128          390 :     } else if (myTrainParams.maxPower == INVALID_DOUBLE && myTrainParams.maxTraction != INVALID_DOUBLE) {
     129            0 :         throw ProcessError(TLF("Undefined maxTraction for vType '%'.", vtype->getID()));
     130              :     }
     131          402 :     if (myTrainParams.maxPower != INVALID_DOUBLE && vtype->getParameter().getCFParamString(SUMO_ATTR_TRACTION_TABLE, "") != "") {
     132            0 :         WRITE_WARNING(TLF("Ignoring tractionTable because maxPower and maxTraction are set for vType '%'.", vtype->getID()));
     133              :     }
     134          390 :     const bool hasSomeResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     135          386 :                                  || myTrainParams.resCoef_linear != INVALID_DOUBLE
     136          776 :                                  || myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     137              :     const bool hasAllResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     138            4 :                                 && myTrainParams.resCoef_linear != INVALID_DOUBLE
     139          394 :                                 && myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     140          390 :     if (hasSomeResCoef && !hasAllResCoef) {
     141            0 :         throw ProcessError(TLF("Some undefined resistance coefficients for vType '%' (requires resCoef_constant, resCoef_linear and resCoef_quadratic)", vtype->getID()));
     142              :     }
     143          402 :     if (myTrainParams.resCoef_constant != INVALID_DOUBLE && vtype->getParameter().getCFParamString(SUMO_ATTR_RESISTANCE_TABLE, "") != "") {
     144            4 :         WRITE_WARNING(TLF("Ignoring resistanceTable because resistance coefficients are set for vType '%'.", vtype->getID()));
     145              :     }
     146              : 
     147          390 :     if (myTrainParams.traction.empty() && myTrainParams.maxPower == INVALID_DOUBLE) {
     148           12 :         throw ProcessError(TLF("Either tractionTable or maxPower must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     149              :     }
     150          386 :     if (myTrainParams.resistance.empty() && myTrainParams.resCoef_constant == INVALID_DOUBLE) {
     151            0 :         throw ProcessError(TLF("Either resistanceTable or resCoef_constant must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     152              :     }
     153          394 : }
     154              : 
     155              : 
     156          770 : MSCFModel_Rail::~MSCFModel_Rail() { }
     157              : 
     158              : 
     159       108170 : double MSCFModel_Rail::followSpeed(const MSVehicle* const veh, double speed, double gap,
     160              :                                    double /* predSpeed */, double /* predMaxDecel*/, const MSVehicle* const /*pred*/, const CalcReason /*usage*/) const {
     161              : 
     162              :     // followSpeed module is used for the simulation of moving block operations. The safety gap is chosen similar to the existing german
     163              :     // system CIR-ELKE (based on LZB). Other implementations of moving block systems may differ, but for now no appropriate parameter
     164              :     // can be set (would be per lane, not per train) -> hard-coded
     165              : 
     166              :     // @note: default train minGap of 5 is already subtracted from gap
     167       108170 :     if (speed >= 30 / 3.6) {
     168              :         // safety distance for higher speeds (>= 30 km/h)
     169        71486 :         gap = MAX2(0.0, gap + veh->getVehicleType().getMinGap() - 50);
     170              :     }
     171              : 
     172       108170 :     const double vsafe = maximumSafeStopSpeed(gap, myDecel, speed, false, TS, false); // absolute breaking distance
     173       108170 :     const double vmin = minNextSpeed(speed, veh);
     174       108170 :     const double vmax = maxNextSpeed(speed, veh);
     175              : 
     176       108170 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     177              :         return MIN2(vsafe, vmax);
     178              :     } else {
     179              :         // ballistic
     180              :         // XXX: the euler variant can break as strong as it wishes immediately! The ballistic cannot, refs. #2575.
     181              :         return MAX2(MIN2(vsafe, vmax), vmin);
     182              :     }
     183              : }
     184              : 
     185              : 
     186              : int
     187            0 : MSCFModel_Rail::getModelID() const {
     188            0 :     return SUMO_TAG_CF_RAIL;
     189              : }
     190              : 
     191              : 
     192              : MSCFModel*
     193           11 : MSCFModel_Rail::duplicate(const MSVehicleType* vtype) const {
     194           11 :     return new MSCFModel_Rail(vtype);
     195              : }
     196              : 
     197              : double
     198      2617707 : MSCFModel_Rail::getRotWeight(const MSVehicle* const veh) const {
     199      2617707 :     return getWeight(veh) * myTrainParams.mf;
     200              : }
     201              : 
     202              : double
     203      7853121 : MSCFModel_Rail::getWeight(const MSVehicle* const veh) const {
     204              :     // kg to tons
     205      7853121 :     return veh->getVehicleType().getMass() / 1000;
     206              : }
     207              : 
     208              : double
     209      2617707 : MSCFModel_Rail::getCurveResistance(const MSVehicle* veh) const {
     210      2617707 :     if (myTrainParams.curveResistance > 0) {
     211        30280 :         const double r = veh->getCurveRadius();
     212        30280 :         if (r == std::numeric_limits<double>::max()) {
     213              :             return 0;
     214        23124 :         } else if (r >= myTrainParams.roeckl_sharp_radius) {
     215        10404 :             return 0.001 * myTrainParams.curveResistance * myTrainParams.roeckl_numerator / (r - myTrainParams.roeckl_offset);
     216        12720 :         } else if (r > myTrainParams.roeckl_offset_sharp) {
     217         5404 :             return 0.001 * myTrainParams.curveResistance * myTrainParams.roeckl_numerator_sharp / (r - myTrainParams.roeckl_offset_sharp);
     218              :         } else {
     219        21948 :             WRITE_WARNINGF("Cannot compute curve resistance for vehicle '%' with radius % at time %",
     220              :                     veh->getID(), r, time2string(SIMSTEP));
     221         7316 :             return 0;
     222              :         }
     223              :     }
     224              :     return 0;
     225              : }
     226              : 
     227              : 
     228      2055715 : double MSCFModel_Rail::maxNextSpeed(double speed, const MSVehicle* const veh) const {
     229              : 
     230      2055715 :     if (speed >= myTrainParams.vmax) {
     231              :         return myTrainParams.vmax;
     232              :     }
     233              : 
     234              :     double targetSpeed = myTrainParams.vmax;
     235              : 
     236      1687048 :     double res = myTrainParams.getResistance(speed); // kN
     237              : 
     238      1687048 :     double slope = veh->getSlope();
     239      1687048 :     double gr = getWeight(veh) * GRAVITY * sin(DEG2RAD(slope)); //kN
     240      1687048 :     double cr = getWeight(veh) * getCurveResistance(veh); //kN
     241              : 
     242      1687048 :     double totalRes = res + gr + cr; //kN
     243              : 
     244      1687048 :     double trac = myTrainParams.getTraction(speed); // kN
     245              :     double a;
     246      1687048 :     if (speed < targetSpeed) {
     247      1687048 :         a = (trac - totalRes) / getRotWeight(veh); //kN/t == N/kg
     248              :     } else {
     249              :         a = 0.;
     250            0 :         if (totalRes > trac) {
     251            0 :             a = (trac - totalRes) / getRotWeight(veh); //kN/t == N/kg
     252              :         }
     253              :     }
     254      1687048 :     double maxNextSpeed = speed + ACCEL2SPEED(a);
     255              : 
     256              : //    std::cout << veh->getID() << " speed: " << (speed*3.6) << std::endl;
     257              : 
     258      1687048 :     return MIN2(myTrainParams.vmax, maxNextSpeed);
     259              : }
     260              : 
     261              : 
     262       930659 : double MSCFModel_Rail::minNextSpeed(double speed, const MSVehicle* const veh) const {
     263              : 
     264       930659 :     const double slope = veh->getSlope();
     265       930659 :     const double gr = getWeight(veh) * GRAVITY * sin(DEG2RAD(slope)); //kN
     266       930659 :     const double cr = getWeight(veh) * getCurveResistance(veh);
     267       930659 :     const double res = myTrainParams.getResistance(speed); // kN
     268       930659 :     const double totalRes = res + gr + cr; //kN
     269       930659 :     const double a = myTrainParams.decl + totalRes / getRotWeight(veh);
     270       930659 :     const double vMin = speed - ACCEL2SPEED(a);
     271       930659 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     272              :         return MAX2(vMin, 0.);
     273              :     } else {
     274              :         // NOTE: ballistic update allows for negative speeds to indicate a stop within the next timestep
     275              :         return vMin;
     276              :     }
     277              : 
     278              : }
     279              : 
     280              : 
     281              : double
     282       272539 : MSCFModel_Rail::minNextSpeedEmergency(double speed, const MSVehicle* const veh) const {
     283       272539 :     return minNextSpeed(speed, veh);
     284              : }
     285              : 
     286              : 
     287              : //void
     288              : //MSCFModel_Rail::initVehicleVariables(const MSVehicle *const veh, MSCFModel_Rail::VehicleVariables *pVariables) const {
     289              : //
     290              : //    pVariables->setInitialized();
     291              : //
     292              : //}
     293              : 
     294              : 
     295            0 : double MSCFModel_Rail::getSpeedAfterMaxDecel(double /* speed */) const {
     296              : 
     297              : //    //TODO: slope not known here
     298              : //    double gr = 0; //trainParams.weight * GRAVITY * edge.grade
     299              : //
     300              : //    double a = 0;//trainParams.decl - gr/trainParams.rotWeight;
     301              : //
     302              : //    return speed + a * DELTA_T / 1000.;
     303            0 :     WRITE_ERROR("function call not allowed for rail model. Exiting!");
     304            0 :     throw ProcessError();
     305              : }
     306              : 
     307              : 
     308       272539 : double MSCFModel_Rail::finalizeSpeed(MSVehicle* const veh, double vPos) const {
     309       272539 :     return MSCFModel::finalizeSpeed(veh, vPos);
     310              : }
     311              : 
     312              : 
     313      1011862 : double MSCFModel_Rail::freeSpeed(const MSVehicle* const /* veh */, double /* speed */, double dist, double targetSpeed,
     314              :                                  const bool onInsertion, const CalcReason /*usage*/) const {
     315              : 
     316              : //    MSCFModel_Rail::VehicleVariables *vars = (MSCFModel_Rail::VehicleVariables *) veh->getCarFollowVariables();
     317              : //    if (vars->isNotYetInitialized()) {
     318              : //        initVehicleVariables(veh, vars);
     319              : //    }
     320              : 
     321              :     //TODO: signals, coasting, ...
     322              : 
     323      1011862 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     324              :         // adapt speed to succeeding lane, no reaction time is involved
     325              :         // when breaking for y steps the following distance g is covered
     326              :         // (drive with v in the final step)
     327              :         // g = (y^2 + y) * 0.5 * b + y * v
     328              :         // y = ((((sqrt((b + 2.0*v)*(b + 2.0*v) + 8.0*b*g)) - b)*0.5 - v)/b)
     329      1011861 :         const double v = SPEED2DIST(targetSpeed);
     330      1011861 :         if (dist < v) {
     331              :             return targetSpeed;
     332              :         }
     333       916074 :         const double b = ACCEL2DIST(myDecel);
     334       916074 :         const double y = MAX2(0.0, ((sqrt((b + 2.0 * v) * (b + 2.0 * v) + 8.0 * b * dist) - b) * 0.5 - v) / b);
     335       916074 :         const double yFull = floor(y);
     336       916074 :         const double exactGap = (yFull * yFull + yFull) * 0.5 * b + yFull * v + (y > yFull ? v : 0.0);
     337       916074 :         const double fullSpeedGain = (yFull + (onInsertion ? 1. : 0.)) * ACCEL2SPEED(myTrainParams.decl);
     338      1241006 :         return DIST2SPEED(MAX2(0.0, dist - exactGap) / (yFull + 1)) + fullSpeedGain + targetSpeed;
     339              :     } else {
     340            1 :         WRITE_ERROR(TL("Anything else than semi implicit euler update is not yet implemented. Exiting!"));
     341            1 :         throw ProcessError();
     342              :     }
     343              : }
     344              : 
     345              : 
     346      1075314 : double MSCFModel_Rail::stopSpeed(const MSVehicle* const veh, const double speed, double gap, double decel, const CalcReason /*usage*/) const {
     347      1075314 :     return MIN2(maximumSafeStopSpeed(gap, decel, speed, false, TS, false), maxNextSpeed(speed, veh));
     348              : }
        

Generated by: LCOV version 2.0-1