Line data Source code
1 : /****************************************************************************/ 2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo 3 : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others. 4 : // activitygen module 5 : // Copyright 2010 TUM (Technische Universitaet Muenchen, http://www.tum.de/) 6 : // This program and the accompanying materials are made available under the 7 : // terms of the Eclipse Public License 2.0 which is available at 8 : // https://www.eclipse.org/legal/epl-2.0/ 9 : // This Source Code may also be made available under the following Secondary 10 : // Licenses when the conditions for such availability set forth in the Eclipse 11 : // Public License 2.0 are satisfied: GNU General Public License, version 2 12 : // or later which is available at 13 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html 14 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later 15 : /****************************************************************************/ 16 : /// @file AGWorkAndSchool.cpp 17 : /// @author Piotr Woznica 18 : /// @author Daniel Krajzewicz 19 : /// @author Michael Behrisch 20 : /// @author Walter Bamberger 21 : /// @date July 2010 22 : /// 23 : // Generates trips to work and to school 24 : /****************************************************************************/ 25 : #include <config.h> 26 : 27 : #include <list> 28 : #include <utils/common/SUMOVehicleClass.h> 29 : #include <activitygen/city/AGCar.h> 30 : #include <activitygen/city/AGChild.h> 31 : #include <activitygen/city/AGHousehold.h> 32 : #include <activitygen/city/AGStreet.h> 33 : #include <activitygen/city/AGWorkPosition.h> 34 : #include "AGWorkAndSchool.h" 35 : 36 : 37 : // =========================================================================== 38 : // method definitions 39 : // =========================================================================== 40 : bool 41 1506 : AGWorkAndSchool::generateTrips() { 42 : //buildDestinations(); 43 : // generation of the waiting list for the accompaniment 44 1506 : buildChildrenAccompaniment(); 45 : 46 1506 : buildWorkDestinations(); 47 : 48 1506 : if (myHousehold->getCarNbr() < (int)personsDrivingCars.size()) { 49 : return false; //to rebuild the household 50 : } 51 1506 : if (childrenNeedingCarAccompaniment.size() != 0 && myHousehold->getCarNbr() == 0) { 52 : return false; //to rebuild the household 53 : } 54 1506 : if (adultNeedingCarAccompaniment.size() != 0 && myHousehold->getCarNbr() == 0) { 55 : return false; 56 : } 57 : 58 1506 : carAllocation(); 59 : 60 1506 : if (personsDrivingCars.empty() && notNeedingDrivers.empty()) { 61 969 : genDone = true; 62 969 : return true; // no trip to generate 63 : } 64 : 65 537 : if (! carsToTrips()) { 66 : return false; 67 : } 68 : 69 537 : genDone = true; 70 537 : return true; 71 : } 72 : 73 : void 74 1506 : AGWorkAndSchool::buildChildrenAccompaniment() { 75 : std::list<AGChild>::const_iterator itC; 76 2076 : for (itC = myHousehold->getChildren().begin(); itC != myHousehold->getChildren().end(); ++itC) { 77 570 : if (itC->haveASchool()) { 78 570 : if (this->availableTranspMeans(myHousehold->getPosition(), itC->getSchoolLocation()) == 0) { 79 : //in this case the school is far from home and bus stations too 80 74 : this->childrenNeedingCarAccompaniment.push_back(*itC); 81 : } 82 : } 83 : } 84 1506 : } 85 : 86 : void 87 1506 : AGWorkAndSchool::buildWorkDestinations() { 88 : std::list<AGAdult>::const_iterator itA; 89 3942 : for (itA = myHousehold->getAdults().begin(); itA != myHousehold->getAdults().end(); ++itA) { 90 2436 : if (itA->isWorking()) { 91 1622 : if (this->possibleTranspMean(itA->getWorkPosition().getPosition()) % 2 == 0) { 92 : //not too close, to not being able to go by foot 93 1325 : if (this->possibleTranspMean(itA->getWorkPosition().getPosition()) > 4) { 94 : //too far from home ==> Car or Bus AND Car and bus are possible 95 736 : workingPeoplePossCar.push_back(*itA); 96 589 : } else if (this->possibleTranspMean(itA->getWorkPosition().getPosition()) == 4) { 97 : //only the car is possible (and there is one (use of possibleTranspMean)) 98 279 : if (myHousehold->getCarNbr() > (int)personsDrivingCars.size()) { 99 245 : personsDrivingCars.push_back(*itA); 100 : } else { 101 34 : adultNeedingCarAccompaniment.push_back(*itA); 102 : } 103 : } 104 : } 105 : } 106 : } 107 : 108 : // sometimes, people still have choice: when vehicles are available and their car take a bus. 109 : std::list<AGAdult>::iterator it; 110 2242 : for (it = workingPeoplePossCar.begin(); it != workingPeoplePossCar.end(); ++it) { 111 736 : if (possibleTranspMean(it->getWorkPosition().getPosition()) == 6 && myHousehold->getCarNbr() > (int)personsDrivingCars.size()) { 112 : //car or bus (always because of workDestinations' construction) AND at least one car not used 113 634 : if (myHousehold->getAdults().front().decide(this->carPreference)) { 114 332 : personsDrivingCars.push_back(*it); 115 : } 116 : } 117 : } 118 1506 : } 119 : 120 : void 121 1506 : AGWorkAndSchool::carAllocation() { 122 : // only two adults are possibles: no car, 1 car, 2 cars and more 123 : // the only choice case: 1 car / 2 adults needing this car (otherwise no choice problems) 124 1506 : if (! personsDrivingCars.empty() && ! adultNeedingCarAccompaniment.empty()) { 125 : //in that case there is only one element in each list and only one car. 126 34 : if (adultNeedingCarAccompaniment.front().getWorkPosition().getOpening() >= personsDrivingCars.front().getWorkPosition().getOpening()) { 127 : //we will invert the driver and the accompanied 128 25 : personsDrivingCars.push_back(adultNeedingCarAccompaniment.front()); 129 25 : adultNeedingCarAccompaniment.pop_front(); 130 : adultNeedingCarAccompaniment.push_back(personsDrivingCars.front()); 131 : personsDrivingCars.pop_front(); 132 : } 133 : } 134 1506 : if (personsDrivingCars.empty() && ! childrenNeedingCarAccompaniment.empty()) { 135 : //at least one adult exists because no household contains less than one adult 136 16 : if ((int)workingPeoplePossCar.size() != myHousehold->getAdultNbr()) { //personsDrivingCars.size() + adultNeedingCarAccompaniment.size() is equal to 0 137 : std::list<AGAdult>::const_iterator itUA; 138 18 : for (itUA = myHousehold->getAdults().begin(); itUA != myHousehold->getAdults().end(); ++itUA) { 139 18 : if (! itUA->isWorking()) { 140 16 : notNeedingDrivers.push_back(*itUA); 141 : break; 142 : } 143 : } 144 : } else { 145 0 : personsDrivingCars.push_back(workingPeoplePossCar.front()); 146 0 : workingPeoplePossCar.pop_front(); 147 : } 148 : } 149 1506 : } 150 : 151 : bool 152 537 : AGWorkAndSchool::carsToTrips() { 153 : // check if the starting edge allows cars 154 537 : if (!myHousehold->getPosition().getStreet().allows(SVC_PASSENGER)) { 155 : return false; 156 : } 157 : std::list<AGAdult>::const_iterator itDriA; 158 537 : std::list<AGCar>::const_iterator itCar = myHousehold->getCars().begin(); 159 1114 : for (itDriA = personsDrivingCars.begin(); itDriA != personsDrivingCars.end(); ++itDriA) { 160 : //check if the number of cars is lower than the number of drivers 161 577 : if (itCar == myHousehold->getCars().end()) { 162 0 : return false; 163 : } 164 : // check if the destination edge allows cars 165 577 : if (!itDriA->getWorkPosition().getPosition().getStreet().allows(SVC_PASSENGER)) { 166 : return false; 167 : } 168 1154 : AGTrip trip(myHousehold->getPosition(), itDriA->getWorkPosition().getPosition(), *itCar, depHour(myHousehold->getPosition(), itDriA->getWorkPosition().getPosition(), itDriA->getWorkPosition().getOpening())); 169 : ++itCar; 170 577 : tempTrip.push_back(trip); 171 577 : } 172 : 173 : std::list<AGAdult>::iterator itAccA; 174 571 : for (itAccA = adultNeedingCarAccompaniment.begin(); itAccA != adultNeedingCarAccompaniment.end(); ++itAccA) { 175 34 : AGTrip trip(myHousehold->getPosition(), itAccA->getWorkPosition().getPosition(), depHour(myHousehold->getPosition(), itAccA->getWorkPosition().getPosition(), itAccA->getWorkPosition().getOpening())); 176 34 : tempAccTrip.push_back(trip); 177 34 : } 178 : 179 : std::list<AGChild>::iterator itAccC; 180 611 : for (itAccC = childrenNeedingCarAccompaniment.begin(); itAccC != childrenNeedingCarAccompaniment.end(); ++itAccC) { 181 74 : AGTrip trip(myHousehold->getPosition(), itAccC->getSchoolLocation(), depHour(myHousehold->getPosition(), itAccC->getSchoolLocation(), itAccC->getSchoolOpening())); 182 74 : tempAccTrip.push_back(trip); 183 74 : } 184 : 185 537 : checkAndBuildTripConsistancy(); 186 537 : if (isThereUnusedCar() && ! checkDriversScheduleMatching()) { 187 0 : makePossibleDriversDrive(); 188 : } 189 : 190 537 : generateListTrips(); 191 : return true; 192 : } 193 : 194 : bool 195 537 : AGWorkAndSchool::isThereUnusedCar() { 196 537 : return (myHousehold->getCarNbr() > static_cast<int>(notNeedingDrivers.size() + personsDrivingCars.size())); 197 : } 198 : 199 : bool 200 537 : AGWorkAndSchool::checkAndBuildTripConsistancy() { 201 : bool finish = false; 202 : int diff1, diff2; 203 : int arrTime; 204 : std::list<AGTrip>::iterator it1, it2; 205 : 206 1103 : while (!finish) { 207 : finish = true; 208 645 : for (it1 = tempAccTrip.begin(); it1 != tempAccTrip.end(); ++it1) { 209 216 : for (it2 = tempAccTrip.begin(); it2 != tempAccTrip.end(); ++it2) { 210 137 : if (it1 == it2) { 211 108 : continue; 212 : } 213 29 : diff1 = it2->getTime() - it1->getRideBackArrTime(this->timePerKm); 214 29 : diff2 = it1->getTime() - it2->getRideBackArrTime(this->timePerKm); 215 : 216 29 : if (diff1 < 0 || diff2 < 0) { 217 29 : if (diff2 < diff1) { 218 12 : arrTime = it2->getArrTime(this->timePerKm); 219 12 : it2->addLayOver(*it1); 220 12 : it2->setDepTime(it2->estimateDepTime(arrTime, this->timePerKm)); 221 12 : tempAccTrip.erase(it1); 222 : } else { 223 17 : arrTime = it1->getArrTime(this->timePerKm); 224 17 : it1->addLayOver(*it2); 225 17 : it1->setDepTime(it1->estimateDepTime(arrTime, this->timePerKm)); 226 17 : tempAccTrip.erase(it2); 227 : } 228 : finish = false; 229 : break; 230 : } 231 : } 232 : if (!finish) { 233 : break; // return to while 234 : } 235 : } 236 : } 237 537 : return finish; 238 : } 239 : 240 : bool 241 99 : AGWorkAndSchool::checkDriversScheduleMatching() { 242 : bool check = false; 243 : std::list<AGTrip>::iterator itAccT; 244 : std::list<AGTrip>::iterator itDriT; 245 : std::list<AGAdult>::iterator itA; 246 111 : for (itAccT = tempAccTrip.begin(); itAccT != tempAccTrip.end(); ++itAccT) { 247 14 : for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) { 248 2 : if (itAccT->getArrTime(this->timePerKm) < itDriT->getArrTime(this->timePerKm)) { 249 : check = true; 250 : } 251 : } 252 22 : for (itA = notNeedingDrivers.begin(); itA != notNeedingDrivers.end(); ++itA) { 253 10 : if (!itA->isWorking()) { 254 : check = true; 255 0 : } else if (itAccT->getRideBackArrTime(this->timePerKm) < itA->getWorkPosition().getOpening()) { 256 : check = true; 257 : } 258 : } 259 12 : if (!check) { //at least one trip is not performed by the existing drivers because it is to late for them 260 : return false; 261 : } 262 : check = false; 263 : } 264 : return true; 265 : } 266 : 267 : void 268 537 : AGWorkAndSchool::generateListTrips() { 269 : int arrTime; 270 : std::list<AGTrip>::iterator itAccT; 271 : std::list<AGTrip>::iterator itDriT; 272 : std::list<AGAdult>::iterator itA; 273 : bool alreadyDone; 274 : 275 : /** 276 : * 1 / 3 : Accompaniment 277 : */ 278 616 : for (itAccT = tempAccTrip.begin(); itAccT != tempAccTrip.end(); ++itAccT) { 279 : alreadyDone = false; 280 155 : for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) { 281 76 : if (!alreadyDone) { 282 70 : if (itAccT->getArrTime(this->timePerKm) < itDriT->getArrTime(this->timePerKm) && !alreadyDone) { 283 : //Add the accompaniment trip to the driver's trip OR new trip 284 28 : if (itAccT->getRideBackArrTime(this->timePerKm) < itDriT->getTime()) { 285 : //there is enough time to accompany people and go back home before going to work 286 56 : itAccT->setVehicleName(itDriT->getVehicleName()); 287 28 : itAccT->addLayOver(itAccT->getArr());//final destination is the last accompaniment stop: not the destination of the course 288 28 : itAccT->setArr(myHousehold->getPosition());//final destination of the whole trip: home 289 28 : myPartialActivityTrips.push_back(*itAccT); 290 : alreadyDone = true; 291 : } else { 292 : //the driver drives people to their working place or school and goes directly to work after that 293 0 : arrTime = itDriT->getArrTime(this->timePerKm); 294 0 : itDriT->addLayOver(*itAccT); 295 0 : itDriT->setDepTime(itDriT->estimateDepTime(arrTime, this->timePerKm)); 296 : //tempAccTrip.erase(itAccT); 297 : //--itAccT; //because of erasure 298 : alreadyDone = true; 299 : } 300 : } 301 : } 302 : } 303 : 304 95 : for (itA = notNeedingDrivers.begin(); itA != notNeedingDrivers.end(); ++itA) { 305 16 : if (!itA->isWorking() && !alreadyDone) { 306 16 : std::string nameC = getUnusedCar(); 307 16 : if (nameC.size() != 0) { 308 0 : itAccT->setVehicleName(getUnusedCar()); 309 0 : itAccT->addLayOver(itAccT->getArr()); 310 0 : itAccT->setArr(myHousehold->getPosition()); 311 0 : myPartialActivityTrips.push_back(*itAccT); 312 : alreadyDone = true; 313 : } 314 0 : } else if (itAccT->getRideBackArrTime(this->timePerKm) < itA->getWorkPosition().getOpening() && !alreadyDone) { 315 0 : std::string nameC = getUnusedCar(); 316 0 : if (nameC.size() != 0) { 317 0 : itAccT->setVehicleName(getUnusedCar()); 318 0 : itAccT->addLayOver(itAccT->getArr()); 319 0 : itAccT->setArr(myHousehold->getPosition()); 320 0 : myPartialActivityTrips.push_back(*itAccT); 321 : alreadyDone = true; 322 : } 323 : } 324 : } 325 : } 326 : 327 : /** 328 : * 2/3 : drivers way 329 : */ 330 1114 : for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) { 331 577 : myPartialActivityTrips.push_back(*itDriT); 332 : } 333 : 334 : /** 335 : * 3/3: way return 336 : */ 337 1114 : for (itA = personsDrivingCars.begin(); itA != personsDrivingCars.end(); ++itA) { 338 577 : for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) { 339 577 : if (itA->getWorkPosition().getPosition() == itDriT->getArr()) { 340 1154 : AGTrip trip(itA->getWorkPosition().getPosition(), myHousehold->getPosition(), itDriT->getVehicleName(), itA->getWorkPosition().getClosing()); 341 577 : myPartialActivityTrips.push_back(trip); 342 577 : tempTrip.erase(itDriT); 343 : break; 344 577 : } 345 : } 346 : } 347 537 : } 348 : 349 : std::string 350 16 : AGWorkAndSchool::getUnusedCar() { 351 16 : std::string nameCar = ""; 352 16 : std::string nameCarUsed = ""; 353 : //only two cars can be used in the household, so: the first one or the last one is not used. 354 16 : if (!tempTrip.empty()) { 355 0 : nameCarUsed = tempTrip.front().getVehicleName(); 356 16 : } else if (!myPartialActivityTrips.empty()) { 357 0 : nameCarUsed = myPartialActivityTrips.front().getVehicleName(); 358 : } 359 : 360 16 : if (nameCarUsed.size() != 0) { 361 0 : if (myHousehold->getCars().front().getName() == nameCarUsed) { 362 0 : nameCar = myHousehold->getCars().back().getName(); 363 : } else { 364 0 : nameCar = myHousehold->getCars().front().getName(); 365 : } 366 : } 367 16 : return nameCar; 368 : } 369 : 370 : void 371 0 : AGWorkAndSchool::makePossibleDriversDrive() { 372 : //give to a non working adult the ability to drive children or someone else. 373 0 : if ((int)(workingPeoplePossCar.size() + personsDrivingCars.size() + adultNeedingCarAccompaniment.size()) != myHousehold->getAdultNbr()) { 374 : std::list<AGAdult>::const_iterator itUA; 375 0 : for (itUA = myHousehold->getAdults().begin(); itUA != myHousehold->getAdults().end(); ++itUA) { 376 0 : if (! itUA->isWorking()) { 377 0 : notNeedingDrivers.push_back(*itUA); 378 : break; 379 : } 380 : } 381 : } 382 0 : } 383 : 384 : 385 : /****************************************************************************/