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 : /****************************************************************************/
|