LCOV - code coverage report
Current view: top level - src/microsim/cfmodels - MSCFModel_Rail.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 149 165 90.3 %
Date: 2024-05-05 15:31:14 Functions: 17 19 89.5 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2012-2024 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 L\"ammel
      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     2406959 : MSCFModel_Rail::TrainParams::getResistance(double speed) const {
      38     2406959 :     if (resCoef_constant != INVALID_DOUBLE) {
      39       20704 :         return (resCoef_quadratic * speed * speed + resCoef_linear * speed + resCoef_constant); // kN
      40             :     } else {
      41     2386255 :         return getInterpolatedValueFromLookUpMap(speed, &resistance); // kN
      42             :     }
      43             : }
      44             : 
      45             : 
      46             : double
      47     1527965 : MSCFModel_Rail::TrainParams::getTraction(double speed) const {
      48     1527965 :     if (maxPower != INVALID_DOUBLE) {
      49        6596 :         return MIN2(maxPower / speed, maxTraction); // kN
      50             :     } else {
      51     1521369 :         return getInterpolatedValueFromLookUpMap(speed, &traction); // kN
      52             :     }
      53             : }
      54             : 
      55             : // ===========================================================================
      56             : // method definitions
      57             : // ===========================================================================
      58             : 
      59             : 
      60         319 : MSCFModel_Rail::MSCFModel_Rail(const MSVehicleType* vtype) :
      61         319 :     MSCFModel(vtype) {
      62         319 :     const std::string trainType = vtype->getParameter().getCFParamString(SUMO_ATTR_TRAIN_TYPE, "NGT400");
      63         319 :     if (trainType.compare("RB425") == 0) {
      64          38 :         myTrainParams = initRB425Params();
      65         281 :     } else if (trainType.compare("RB628") == 0) {
      66          19 :         myTrainParams = initRB628Params();
      67         262 :     } else if (trainType.compare("NGT400") == 0) {
      68         116 :         myTrainParams = initNGT400Params();
      69         146 :     } else if (trainType.compare("NGT400_16") == 0) {
      70           4 :         myTrainParams = initNGT400_16Params();
      71         142 :     } else if (trainType.compare("ICE1") == 0) {
      72           9 :         myTrainParams = initICE1Params();
      73         133 :     } else if (trainType.compare("REDosto7") == 0) {
      74          72 :         myTrainParams = initREDosto7Params();
      75          61 :     } else if (trainType.compare("Freight") == 0) {
      76          36 :         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           4 :         WRITE_ERRORF(TL("Unknown train type: %. Exiting!"), trainType);
      87           0 :         throw ProcessError();
      88             :     }
      89             :     // override with user values
      90         319 :     if (vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) {
      91          25 :         myTrainParams.vmax = vtype->getMaxSpeed();
      92             :     }
      93         319 :     if (vtype->wasSet(VTYPEPARS_LENGTH_SET)) {
      94          38 :         myTrainParams.length = vtype->getLength();
      95             :     }
      96         319 :     if (vtype->wasSet(VTYPEPARS_MASS_SET)) {
      97             :         // kg to tons
      98           4 :         myTrainParams.weight = vtype->getMass() / 1000;
      99             :     }
     100         319 :     myTrainParams.mf = vtype->getParameter().getCFParam(SUMO_ATTR_MASSFACTOR, myTrainParams.mf);
     101         319 :     myTrainParams.decl = vtype->getParameter().getCFParam(SUMO_ATTR_DECEL, myTrainParams.decl);
     102             :     setMaxDecel(myTrainParams.decl);
     103         319 :     setEmergencyDecel(vtype->getParameter().getCFParam(SUMO_ATTR_EMERGENCYDECEL, myTrainParams.decl + 0.3));
     104             :     // update type parameters so they are shown correctly in the gui (if defaults from trainType are used)
     105         319 :     const_cast<MSVehicleType*>(vtype)->setMaxSpeed(myTrainParams.vmax);
     106         319 :     const_cast<MSVehicleType*>(vtype)->setLength(myTrainParams.length);
     107             : 
     108             :     // init tabular curves
     109         319 :     std::vector<double> speedTable = getValueTable(vtype, SUMO_ATTR_SPEED_TABLE);
     110         319 :     std::vector<double> tractionTable = getValueTable(vtype, SUMO_ATTR_TRACTION_TABLE);
     111         319 :     std::vector<double> resistanceTable = getValueTable(vtype, SUMO_ATTR_RESISTANCE_TABLE);
     112         319 :     if (speedTable.size() > 0 || tractionTable.size() > 0 || resistanceTable.size() > 0) {
     113           8 :         if (speedTable.size() == 1) {
     114           0 :             throw ProcessError(TLF("Invalid size of speedTable for vType '%' (at least 2 values are required).", vtype->getID()));
     115           8 :         } else if (speedTable.size() != tractionTable.size()) {
     116           0 :             throw ProcessError(TLF("Mismatching size of speedTable and tractionTable for vType '%'.", vtype->getID()));
     117           8 :         } else if (speedTable.size() != resistanceTable.size()) {
     118           0 :             throw ProcessError(TLF("Mismatching size of speedTable and resistanceTable for vType '%'.", vtype->getID()));
     119             :         }
     120           8 :         myTrainParams.traction.clear();
     121           8 :         myTrainParams.resistance.clear();
     122         112 :         for (int i = 0; i < (int)speedTable.size(); i++) {
     123         104 :             myTrainParams.traction[speedTable[i]] = tractionTable[i];
     124         104 :             myTrainParams.resistance[speedTable[i]] = resistanceTable[i];
     125             :         }
     126             :     }
     127             : 
     128             :     // init parametric curves
     129         319 :     myTrainParams.maxPower = vtype->getParameter().getCFParam(SUMO_ATTR_MAXPOWER, INVALID_DOUBLE);
     130         319 :     myTrainParams.maxTraction = vtype->getParameter().getCFParam(SUMO_ATTR_MAXTRACTION, INVALID_DOUBLE);
     131         319 :     myTrainParams.resCoef_constant = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_CONSTANT, INVALID_DOUBLE);
     132         319 :     myTrainParams.resCoef_linear = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_LINEAR, INVALID_DOUBLE);
     133         319 :     myTrainParams.resCoef_quadratic = vtype->getParameter().getCFParam(SUMO_ATTR_RESISTANCE_COEFFICIENT_QUADRATIC, INVALID_DOUBLE);
     134             : 
     135         319 :     if (myTrainParams.maxPower != INVALID_DOUBLE && myTrainParams.maxTraction == INVALID_DOUBLE) {
     136           0 :         throw ProcessError(TLF("Undefined maxPower for vType '%'.", vtype->getID()));
     137         319 :     } else if (myTrainParams.maxPower == INVALID_DOUBLE && myTrainParams.maxTraction != INVALID_DOUBLE) {
     138           0 :         throw ProcessError(TLF("Undefined maxTraction for vType '%'.", vtype->getID()));
     139             :     }
     140         319 :     if (myTrainParams.maxPower != INVALID_DOUBLE && tractionTable.size() > 0) {
     141           0 :         WRITE_WARNING(TLF("Ignoring tractionTable because maxPower and maxTraction are set for vType '%'.", vtype->getID()));
     142             :     }
     143         319 :     const bool hasSomeResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     144         315 :             || myTrainParams.resCoef_linear != INVALID_DOUBLE
     145         634 :             || myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     146             :     const bool hasAllResCoef = (myTrainParams.resCoef_constant != INVALID_DOUBLE
     147           4 :             && myTrainParams.resCoef_linear != INVALID_DOUBLE
     148         323 :             && myTrainParams.resCoef_quadratic != INVALID_DOUBLE);
     149         319 :     if (hasSomeResCoef && !hasAllResCoef) {
     150           0 :         throw ProcessError(TLF("Some undefined resistance coefficients for vType '%' (requires resCoef_constant, resCoef_linear and resCoef_quadratic)", vtype->getID()));
     151             :     }
     152         319 :     if (myTrainParams.resCoef_constant != INVALID_DOUBLE && resistanceTable.size() > 0) {
     153           4 :         WRITE_WARNING(TLF("Ignoring resistanceTable because resistance coefficents are set for vType '%'.", vtype->getID()));
     154             :     }
     155             : 
     156         319 :     if (myTrainParams.traction.empty() && myTrainParams.maxPower == INVALID_DOUBLE) {
     157          16 :         throw ProcessError(TLF("Either tractionTable or maxPower must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     158             :     }
     159         315 :     if (myTrainParams.resistance.empty() && myTrainParams.resCoef_constant == INVALID_DOUBLE) {
     160           0 :         throw ProcessError(TLF("Either resistanceTable or resCoef_constant must be defined for vType '%' with Rail model type '%'.", vtype->getID(), trainType));
     161             :     }
     162         323 : }
     163             : 
     164             : 
     165         628 : MSCFModel_Rail::~MSCFModel_Rail() { }
     166             : 
     167             : 
     168             : std::vector<double>
     169         957 : MSCFModel_Rail::getValueTable(const MSVehicleType* vtype, SumoXMLAttr attr) {
     170             :     std::vector<double> result;
     171        1914 :     const std::string values = vtype->getParameter().getCFParamString(attr, "");
     172         957 :     if (!values.empty()) {
     173         360 :         for (std::string value : StringTokenizer(values).getVector()) {
     174         312 :             result.push_back(StringUtils::toDouble(value));
     175          24 :         }
     176             :     }
     177         957 :     return result;
     178             : }
     179             : 
     180             : 
     181      143097 : double MSCFModel_Rail::followSpeed(const MSVehicle* const veh, double speed, double gap,
     182             :                                    double /* predSpeed */, double /* predMaxDecel*/, const MSVehicle* const /*pred*/, const CalcReason /*usage*/) const {
     183             : 
     184             :     // followSpeed module is used for the simulation of moving block operations. The safety gap is chosen similar to the existing german
     185             :     // system CIR-ELKE (based on LZB). Other implementations of moving block systems may differ, but for now no appropriate parameter
     186             :     // can be set (would be per lane, not per train) -> hard-coded
     187             : 
     188             :     // @note: default train minGap of 5 is already subtracted from gap
     189      143097 :     if (speed >= 30 / 3.6) {
     190             :         // safety distance for higher speeds (>= 30 km/h)
     191       96479 :         gap = MAX2(0.0, gap + veh->getVehicleType().getMinGap() - 50);
     192             :     }
     193             : 
     194      143097 :     const double vsafe = maximumSafeStopSpeed(gap, myDecel, speed, false, TS, false); // absolute breaking distance
     195      143097 :     const double vmin = minNextSpeed(speed, veh);
     196      143097 :     const double vmax = maxNextSpeed(speed, veh);
     197             : 
     198      143097 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     199             :         return MIN2(vsafe, vmax);
     200             :     } else {
     201             :         // ballistic
     202             :         // XXX: the euler variant can break as strong as it wishes immediately! The ballistic cannot, refs. #2575.
     203             :         return MAX2(MIN2(vsafe, vmax), vmin);
     204             :     }
     205             : }
     206             : 
     207             : int
     208           0 : MSCFModel_Rail::getModelID() const {
     209           0 :     return SUMO_TAG_CF_RAIL;
     210             : }
     211             : 
     212             : MSCFModel*
     213           3 : MSCFModel_Rail::duplicate(const MSVehicleType* vtype) const {
     214           3 :     return new MSCFModel_Rail(vtype);
     215             : }
     216             : 
     217     1656554 : double MSCFModel_Rail::maxNextSpeed(double speed, const MSVehicle* const veh) const {
     218             : 
     219     1656554 :     if (speed >= myTrainParams.vmax) {
     220             :         return myTrainParams.vmax;
     221             :     }
     222             : 
     223             :     double targetSpeed = myTrainParams.vmax;
     224             : 
     225     1527965 :     double res = myTrainParams.getResistance(speed); // kN
     226             : 
     227     1527965 :     double slope = veh->getSlope();
     228     1527965 :     double gr = myTrainParams.weight * GRAVITY * sin(DEG2RAD(slope)); //kN
     229             : 
     230     1527965 :     double totalRes = res + gr; //kN
     231             : 
     232     1527965 :     double trac = myTrainParams.getTraction(speed); // kN
     233             : 
     234             :     double a;
     235     1527965 :     if (speed < targetSpeed) {
     236     1527965 :         a = (trac - totalRes) / myTrainParams.getRotWeight(); //kN/t == N/kg
     237             :     } else {
     238             :         a = 0.;
     239           0 :         if (totalRes > trac) {
     240           0 :             a = (trac - totalRes) / myTrainParams.getRotWeight(); //kN/t == N/kg
     241             :         }
     242             :     }
     243     1527965 :     double maxNextSpeed = speed + ACCEL2SPEED(a);
     244             : 
     245             : //    std::cout << veh->getID() << " speed: " << (speed*3.6) << std::endl;
     246             : 
     247     1527965 :     return maxNextSpeed;
     248             : }
     249             : 
     250      878994 : double MSCFModel_Rail::minNextSpeed(double speed, const MSVehicle* const veh) const {
     251             : 
     252      878994 :     const double slope = veh->getSlope();
     253      878994 :     const double gr = myTrainParams.weight * GRAVITY * sin(DEG2RAD(slope)); //kN
     254      878994 :     const double res = myTrainParams.getResistance(speed); // kN
     255      878994 :     const double totalRes = res + gr; //kN
     256      878994 :     const double a = myTrainParams.decl + totalRes / myTrainParams.getRotWeight();
     257      878994 :     const double vMin = speed - ACCEL2SPEED(a);
     258      878994 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     259             :         return MAX2(vMin, 0.);
     260             :     } else {
     261             :         // NOTE: ballistic update allows for negative speeds to indicate a stop within the next timestep
     262             :         return vMin;
     263             :     }
     264             : 
     265             : }
     266             : 
     267             : 
     268             : double
     269      243675 : MSCFModel_Rail::minNextSpeedEmergency(double speed, const MSVehicle* const veh) const {
     270      243675 :     return minNextSpeed(speed, veh);
     271             : }
     272             : 
     273             : 
     274     3907624 : double MSCFModel_Rail::getInterpolatedValueFromLookUpMap(double speed, const LookUpMap* lookUpMap) {
     275             :     std::map<double, double>::const_iterator low, prev;
     276             :     low = lookUpMap->lower_bound(speed);
     277             : 
     278     3907624 :     if (low == lookUpMap->end()) { //speed > max speed
     279        9160 :         return (lookUpMap->rbegin())->second;
     280             :     }
     281             : 
     282     3898464 :     if (low == lookUpMap->begin()) {
     283      602267 :         return low->second;
     284             :     }
     285             : 
     286             :     prev = low;
     287             :     --prev;
     288             : 
     289     3296197 :     double range = low->first - prev->first;
     290     3296197 :     double dist = speed - prev->first;
     291             :     assert(range > 0);
     292             :     assert(dist > 0);
     293             : 
     294     3296197 :     double weight = dist / range;
     295             : 
     296     3296197 :     double res = (1 - weight) * prev->second + weight * low->second;
     297             : 
     298     3296197 :     return res;
     299             : 
     300             : }
     301             : 
     302             : 
     303             : 
     304             : //void
     305             : //MSCFModel_Rail::initVehicleVariables(const MSVehicle *const veh, MSCFModel_Rail::VehicleVariables *pVariables) const {
     306             : //
     307             : //    pVariables->setInitialized();
     308             : //
     309             : //}
     310             : 
     311           0 : double MSCFModel_Rail::getSpeedAfterMaxDecel(double /* speed */) const {
     312             : 
     313             : //    //TODO: slope not known here
     314             : //    double gr = 0; //trainParams.weight * GRAVITY * edge.grade
     315             : //
     316             : //    double a = 0;//trainParams.decl - gr/trainParams.rotWeight;
     317             : //
     318             : //    return speed + a * DELTA_T / 1000.;
     319           0 :     WRITE_ERROR("function call not allowed for rail model. Exiting!");
     320           0 :     throw ProcessError();
     321             : }
     322             : 
     323         354 : MSCFModel::VehicleVariables* MSCFModel_Rail::createVehicleVariables() const {
     324         354 :     VehicleVariables* ret = new VehicleVariables();
     325         354 :     return ret;
     326             : }
     327             : 
     328             : 
     329      243675 : double MSCFModel_Rail::finalizeSpeed(MSVehicle* const veh, double vPos) const {
     330      243675 :     return MSCFModel::finalizeSpeed(veh, vPos);
     331             : }
     332             : 
     333      914082 : double MSCFModel_Rail::freeSpeed(const MSVehicle* const /* veh */, double /* speed */, double dist, double targetSpeed,
     334             :                                  const bool onInsertion, const CalcReason /*usage*/) const {
     335             : 
     336             : //    MSCFModel_Rail::VehicleVariables *vars = (MSCFModel_Rail::VehicleVariables *) veh->getCarFollowVariables();
     337             : //    if (vars->isNotYetInitialized()) {
     338             : //        initVehicleVariables(veh, vars);
     339             : //    }
     340             : 
     341             :     //TODO: signals, coasting, ...
     342             : 
     343      914082 :     if (MSGlobals::gSemiImplicitEulerUpdate) {
     344             :         // adapt speed to succeeding lane, no reaction time is involved
     345             :         // when breaking for y steps the following distance g is covered
     346             :         // (drive with v in the final step)
     347             :         // g = (y^2 + y) * 0.5 * b + y * v
     348             :         // y = ((((sqrt((b + 2.0*v)*(b + 2.0*v) + 8.0*b*g)) - b)*0.5 - v)/b)
     349      914081 :         const double v = SPEED2DIST(targetSpeed);
     350      914081 :         if (dist < v) {
     351             :             return targetSpeed;
     352             :         }
     353      820839 :         const double b = ACCEL2DIST(myDecel);
     354      820839 :         const double y = MAX2(0.0, ((sqrt((b + 2.0 * v) * (b + 2.0 * v) + 8.0 * b * dist) - b) * 0.5 - v) / b);
     355      820839 :         const double yFull = floor(y);
     356      820839 :         const double exactGap = (yFull * yFull + yFull) * 0.5 * b + yFull * v + (y > yFull ? v : 0.0);
     357      820839 :         const double fullSpeedGain = (yFull + (onInsertion ? 1. : 0.)) * ACCEL2SPEED(myTrainParams.decl);
     358     1093593 :         return DIST2SPEED(MAX2(0.0, dist - exactGap) / (yFull + 1)) + fullSpeedGain + targetSpeed;
     359             :     } else {
     360           1 :         WRITE_ERROR(TL("Anything else than semi implicit euler update is not yet implemented. Exiting!"));
     361           1 :         throw ProcessError();
     362             :     }
     363             : }
     364             : 
     365      972176 : double MSCFModel_Rail::stopSpeed(const MSVehicle* const veh, const double speed, double gap, double decel, const CalcReason /*usage*/) const {
     366      972176 :     return MIN2(maximumSafeStopSpeed(gap, decel, speed, false, TS, false), maxNextSpeed(speed, veh));
     367             : }
     368             : 
     369             : 
     370             : void
     371         614 : MSCFModel_Rail::convertMap(LookUpMap& map, double keyFactor, double valueFactor) {
     372             :     LookUpMap map2;
     373       18586 :     for (auto item : map) {
     374       17972 :         map2[item.first * keyFactor] = item.second * valueFactor;
     375             :     }
     376             :     map.clear();
     377         614 :     map.insert(map2.begin(), map2.end());
     378         614 : }

Generated by: LCOV version 1.14