Path: blob/main/src/activitygen/activities/AGWorkAndSchool.cpp
169678 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// activitygen module4// Copyright 2010 TUM (Technische Universitaet Muenchen, http://www.tum.de/)5// This program and the accompanying materials are made available under the6// terms of the Eclipse Public License 2.0 which is available at7// https://www.eclipse.org/legal/epl-2.0/8// This Source Code may also be made available under the following Secondary9// Licenses when the conditions for such availability set forth in the Eclipse10// Public License 2.0 are satisfied: GNU General Public License, version 211// or later which is available at12// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html13// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later14/****************************************************************************/15/// @file AGWorkAndSchool.cpp16/// @author Piotr Woznica17/// @author Daniel Krajzewicz18/// @author Michael Behrisch19/// @author Walter Bamberger20/// @date July 201021///22// Generates trips to work and to school23/****************************************************************************/24#include <config.h>2526#include <list>27#include <utils/common/SUMOVehicleClass.h>28#include <activitygen/city/AGCar.h>29#include <activitygen/city/AGChild.h>30#include <activitygen/city/AGHousehold.h>31#include <activitygen/city/AGStreet.h>32#include <activitygen/city/AGWorkPosition.h>33#include "AGWorkAndSchool.h"343536// ===========================================================================37// method definitions38// ===========================================================================39bool40AGWorkAndSchool::generateTrips() {41//buildDestinations();42// generation of the waiting list for the accompaniment43buildChildrenAccompaniment();4445buildWorkDestinations();4647if (myHousehold->getCarNbr() < (int)personsDrivingCars.size()) {48return false; //to rebuild the household49}50if (childrenNeedingCarAccompaniment.size() != 0 && myHousehold->getCarNbr() == 0) {51return false; //to rebuild the household52}53if (adultNeedingCarAccompaniment.size() != 0 && myHousehold->getCarNbr() == 0) {54return false;55}5657carAllocation();5859if (personsDrivingCars.empty() && notNeedingDrivers.empty()) {60genDone = true;61return true; // no trip to generate62}6364if (! carsToTrips()) {65return false;66}6768genDone = true;69return true;70}7172void73AGWorkAndSchool::buildChildrenAccompaniment() {74std::list<AGChild>::const_iterator itC;75for (itC = myHousehold->getChildren().begin(); itC != myHousehold->getChildren().end(); ++itC) {76if (itC->haveASchool()) {77if (this->availableTranspMeans(myHousehold->getPosition(), itC->getSchoolLocation()) == 0) {78//in this case the school is far from home and bus stations too79this->childrenNeedingCarAccompaniment.push_back(*itC);80}81}82}83}8485void86AGWorkAndSchool::buildWorkDestinations() {87std::list<AGAdult>::const_iterator itA;88for (itA = myHousehold->getAdults().begin(); itA != myHousehold->getAdults().end(); ++itA) {89if (itA->isWorking()) {90if (this->possibleTranspMean(itA->getWorkPosition().getPosition()) % 2 == 0) {91//not too close, to not being able to go by foot92if (this->possibleTranspMean(itA->getWorkPosition().getPosition()) > 4) {93//too far from home ==> Car or Bus AND Car and bus are possible94workingPeoplePossCar.push_back(*itA);95} else if (this->possibleTranspMean(itA->getWorkPosition().getPosition()) == 4) {96//only the car is possible (and there is one (use of possibleTranspMean))97if (myHousehold->getCarNbr() > (int)personsDrivingCars.size()) {98personsDrivingCars.push_back(*itA);99} else {100adultNeedingCarAccompaniment.push_back(*itA);101}102}103}104}105}106107// sometimes, people still have choice: when vehicles are available and their car take a bus.108std::list<AGAdult>::iterator it;109for (it = workingPeoplePossCar.begin(); it != workingPeoplePossCar.end(); ++it) {110if (possibleTranspMean(it->getWorkPosition().getPosition()) == 6 && myHousehold->getCarNbr() > (int)personsDrivingCars.size()) {111//car or bus (always because of workDestinations' construction) AND at least one car not used112if (myHousehold->getAdults().front().decide(this->carPreference)) {113personsDrivingCars.push_back(*it);114}115}116}117}118119void120AGWorkAndSchool::carAllocation() {121// only two adults are possibles: no car, 1 car, 2 cars and more122// the only choice case: 1 car / 2 adults needing this car (otherwise no choice problems)123if (! personsDrivingCars.empty() && ! adultNeedingCarAccompaniment.empty()) {124//in that case there is only one element in each list and only one car.125if (adultNeedingCarAccompaniment.front().getWorkPosition().getOpening() >= personsDrivingCars.front().getWorkPosition().getOpening()) {126//we will invert the driver and the accompanied127personsDrivingCars.push_back(adultNeedingCarAccompaniment.front());128adultNeedingCarAccompaniment.pop_front();129adultNeedingCarAccompaniment.push_back(personsDrivingCars.front());130personsDrivingCars.pop_front();131}132}133if (personsDrivingCars.empty() && ! childrenNeedingCarAccompaniment.empty()) {134//at least one adult exists because no household contains less than one adult135if ((int)workingPeoplePossCar.size() != myHousehold->getAdultNbr()) { //personsDrivingCars.size() + adultNeedingCarAccompaniment.size() is equal to 0136std::list<AGAdult>::const_iterator itUA;137for (itUA = myHousehold->getAdults().begin(); itUA != myHousehold->getAdults().end(); ++itUA) {138if (! itUA->isWorking()) {139notNeedingDrivers.push_back(*itUA);140break;141}142}143} else {144personsDrivingCars.push_back(workingPeoplePossCar.front());145workingPeoplePossCar.pop_front();146}147}148}149150bool151AGWorkAndSchool::carsToTrips() {152// check if the starting edge allows cars153if (!myHousehold->getPosition().getStreet().allows(SVC_PASSENGER)) {154return false;155}156std::list<AGAdult>::const_iterator itDriA;157std::list<AGCar>::const_iterator itCar = myHousehold->getCars().begin();158for (itDriA = personsDrivingCars.begin(); itDriA != personsDrivingCars.end(); ++itDriA) {159//check if the number of cars is lower than the number of drivers160if (itCar == myHousehold->getCars().end()) {161return false;162}163// check if the destination edge allows cars164if (!itDriA->getWorkPosition().getPosition().getStreet().allows(SVC_PASSENGER)) {165return false;166}167AGTrip trip(myHousehold->getPosition(), itDriA->getWorkPosition().getPosition(), *itCar, depHour(myHousehold->getPosition(), itDriA->getWorkPosition().getPosition(), itDriA->getWorkPosition().getOpening()));168++itCar;169tempTrip.push_back(trip);170}171172std::list<AGAdult>::iterator itAccA;173for (itAccA = adultNeedingCarAccompaniment.begin(); itAccA != adultNeedingCarAccompaniment.end(); ++itAccA) {174AGTrip trip(myHousehold->getPosition(), itAccA->getWorkPosition().getPosition(), depHour(myHousehold->getPosition(), itAccA->getWorkPosition().getPosition(), itAccA->getWorkPosition().getOpening()));175tempAccTrip.push_back(trip);176}177178std::list<AGChild>::iterator itAccC;179for (itAccC = childrenNeedingCarAccompaniment.begin(); itAccC != childrenNeedingCarAccompaniment.end(); ++itAccC) {180AGTrip trip(myHousehold->getPosition(), itAccC->getSchoolLocation(), depHour(myHousehold->getPosition(), itAccC->getSchoolLocation(), itAccC->getSchoolOpening()));181tempAccTrip.push_back(trip);182}183184checkAndBuildTripConsistancy();185if (isThereUnusedCar() && ! checkDriversScheduleMatching()) {186makePossibleDriversDrive();187}188189generateListTrips();190return true;191}192193bool194AGWorkAndSchool::isThereUnusedCar() {195return (myHousehold->getCarNbr() > static_cast<int>(notNeedingDrivers.size() + personsDrivingCars.size()));196}197198bool199AGWorkAndSchool::checkAndBuildTripConsistancy() {200bool finish = false;201int diff1, diff2;202int arrTime;203std::list<AGTrip>::iterator it1, it2;204205while (!finish) {206finish = true;207for (it1 = tempAccTrip.begin(); it1 != tempAccTrip.end(); ++it1) {208for (it2 = tempAccTrip.begin(); it2 != tempAccTrip.end(); ++it2) {209if (it1 == it2) {210continue;211}212diff1 = it2->getTime() - it1->getRideBackArrTime(this->timePerKm);213diff2 = it1->getTime() - it2->getRideBackArrTime(this->timePerKm);214215if (diff1 < 0 || diff2 < 0) {216if (diff2 < diff1) {217arrTime = it2->getArrTime(this->timePerKm);218it2->addLayOver(*it1);219it2->setDepTime(it2->estimateDepTime(arrTime, this->timePerKm));220tempAccTrip.erase(it1);221} else {222arrTime = it1->getArrTime(this->timePerKm);223it1->addLayOver(*it2);224it1->setDepTime(it1->estimateDepTime(arrTime, this->timePerKm));225tempAccTrip.erase(it2);226}227finish = false;228break;229}230}231if (!finish) {232break; // return to while233}234}235}236return finish;237}238239bool240AGWorkAndSchool::checkDriversScheduleMatching() {241bool check = false;242std::list<AGTrip>::iterator itAccT;243std::list<AGTrip>::iterator itDriT;244std::list<AGAdult>::iterator itA;245for (itAccT = tempAccTrip.begin(); itAccT != tempAccTrip.end(); ++itAccT) {246for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) {247if (itAccT->getArrTime(this->timePerKm) < itDriT->getArrTime(this->timePerKm)) {248check = true;249}250}251for (itA = notNeedingDrivers.begin(); itA != notNeedingDrivers.end(); ++itA) {252if (!itA->isWorking()) {253check = true;254} else if (itAccT->getRideBackArrTime(this->timePerKm) < itA->getWorkPosition().getOpening()) {255check = true;256}257}258if (!check) { //at least one trip is not performed by the existing drivers because it is to late for them259return false;260}261check = false;262}263return true;264}265266void267AGWorkAndSchool::generateListTrips() {268int arrTime;269std::list<AGTrip>::iterator itAccT;270std::list<AGTrip>::iterator itDriT;271std::list<AGAdult>::iterator itA;272bool alreadyDone;273274/**275* 1 / 3 : Accompaniment276*/277for (itAccT = tempAccTrip.begin(); itAccT != tempAccTrip.end(); ++itAccT) {278alreadyDone = false;279for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) {280if (!alreadyDone) {281if (itAccT->getArrTime(this->timePerKm) < itDriT->getArrTime(this->timePerKm) && !alreadyDone) {282//Add the accompaniment trip to the driver's trip OR new trip283if (itAccT->getRideBackArrTime(this->timePerKm) < itDriT->getTime()) {284//there is enough time to accompany people and go back home before going to work285itAccT->setVehicleName(itDriT->getVehicleName());286itAccT->addLayOver(itAccT->getArr());//final destination is the last accompaniment stop: not the destination of the course287itAccT->setArr(myHousehold->getPosition());//final destination of the whole trip: home288myPartialActivityTrips.push_back(*itAccT);289alreadyDone = true;290} else {291//the driver drives people to their working place or school and goes directly to work after that292arrTime = itDriT->getArrTime(this->timePerKm);293itDriT->addLayOver(*itAccT);294itDriT->setDepTime(itDriT->estimateDepTime(arrTime, this->timePerKm));295//tempAccTrip.erase(itAccT);296//--itAccT; //because of erasure297alreadyDone = true;298}299}300}301}302303for (itA = notNeedingDrivers.begin(); itA != notNeedingDrivers.end(); ++itA) {304if (!itA->isWorking() && !alreadyDone) {305std::string nameC = getUnusedCar();306if (nameC.size() != 0) {307itAccT->setVehicleName(getUnusedCar());308itAccT->addLayOver(itAccT->getArr());309itAccT->setArr(myHousehold->getPosition());310myPartialActivityTrips.push_back(*itAccT);311alreadyDone = true;312}313} else if (itAccT->getRideBackArrTime(this->timePerKm) < itA->getWorkPosition().getOpening() && !alreadyDone) {314std::string nameC = getUnusedCar();315if (nameC.size() != 0) {316itAccT->setVehicleName(getUnusedCar());317itAccT->addLayOver(itAccT->getArr());318itAccT->setArr(myHousehold->getPosition());319myPartialActivityTrips.push_back(*itAccT);320alreadyDone = true;321}322}323}324}325326/**327* 2/3 : drivers way328*/329for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) {330myPartialActivityTrips.push_back(*itDriT);331}332333/**334* 3/3: way return335*/336for (itA = personsDrivingCars.begin(); itA != personsDrivingCars.end(); ++itA) {337for (itDriT = tempTrip.begin(); itDriT != tempTrip.end(); ++itDriT) {338if (itA->getWorkPosition().getPosition() == itDriT->getArr()) {339AGTrip trip(itA->getWorkPosition().getPosition(), myHousehold->getPosition(), itDriT->getVehicleName(), itA->getWorkPosition().getClosing());340myPartialActivityTrips.push_back(trip);341tempTrip.erase(itDriT);342break;343}344}345}346}347348std::string349AGWorkAndSchool::getUnusedCar() {350std::string nameCar = "";351std::string nameCarUsed = "";352//only two cars can be used in the household, so: the first one or the last one is not used.353if (!tempTrip.empty()) {354nameCarUsed = tempTrip.front().getVehicleName();355} else if (!myPartialActivityTrips.empty()) {356nameCarUsed = myPartialActivityTrips.front().getVehicleName();357}358359if (nameCarUsed.size() != 0) {360if (myHousehold->getCars().front().getName() == nameCarUsed) {361nameCar = myHousehold->getCars().back().getName();362} else {363nameCar = myHousehold->getCars().front().getName();364}365}366return nameCar;367}368369void370AGWorkAndSchool::makePossibleDriversDrive() {371//give to a non working adult the ability to drive children or someone else.372if ((int)(workingPeoplePossCar.size() + personsDrivingCars.size() + adultNeedingCarAccompaniment.size()) != myHousehold->getAdultNbr()) {373std::list<AGAdult>::const_iterator itUA;374for (itUA = myHousehold->getAdults().begin(); itUA != myHousehold->getAdults().end(); ++itUA) {375if (! itUA->isWorking()) {376notNeedingDrivers.push_back(*itUA);377break;378}379}380}381}382383384/****************************************************************************/385386387