Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
MSOverheadWire.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2002-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/****************************************************************************/
19// Member method definitions for MSOverheadWire and MSTractionSubstation.
20/****************************************************************************/
21#include <config.h>
22
23#include <cassert>
24#include <tuple>
25#include <mutex>
26#include <string.h>
27
32#include <microsim/MSJunction.h>
33#include <microsim/MSLane.h>
34#include <microsim/MSLink.h>
35#include <microsim/MSNet.h>
37
38// due to gOverheadWireSolver
39#include <microsim/MSGlobals.h>
40
41// due to solving circuit as endEndOfTimestepEvents
45
47#include "MSOverheadWire.h"
48
49
51static std::mutex ow_mutex;
52
53// ===========================================================================
54// MSOverheadWire
55// ===========================================================================
56
57MSOverheadWire::MSOverheadWire(const std::string& overheadWireSegmentID, MSLane& lane, double startPos, double endPos, bool voltageSource) :
58 MSStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT, std::vector<std::string>(), lane, startPos, endPos),
59 myVoltage(0),
60 myChargingVehicle(false),
61 myTotalCharge(0),
62 myChargingVehicles({}),
63 // RICE_TODO: think about some better structure storing circuit pointers below
64 myTractionSubstation(nullptr),
65 myVoltageSource(voltageSource),
66 myCircuitElementPos(nullptr),
67 myCircuitStartNodePos(nullptr),
68myCircuitEndNodePos(nullptr) {
69 if (getBeginLanePosition() > getEndLanePosition()) {
70 WRITE_WARNING(toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " doesn't have a valid range (" + toString(getBeginLanePosition()) + " < " + toString(getEndLanePosition()) + ").");
71 }
72}
73
75 if (myTractionSubstation != nullptr) {
80 if (myCircuitEndNodePos->getElements()->size() == 0) {
83 }
84 if (myCircuitStartNodePos->getElements()->size() == 0) {
87 }
88 }
89
92 //RICE_TODO We should "delete myTractionSubstation;" here ...
93 } else {
95 }
96 }
97}
98
99
100void
102 std::lock_guard<std::mutex> guard(ow_mutex);
103 setChargingVehicle(true);
104 myChargingVehicles.push_back(&veh);
106}
107
108void
110 std::lock_guard<std::mutex> guard(ow_mutex);
111 myChargingVehicles.erase(std::remove(myChargingVehicles.begin(), myChargingVehicles.end(), &veh), myChargingVehicles.end());
112 if (myChargingVehicles.size() == 0) {
113 setChargingVehicle(false);
114 }
115 //sort(myChargingVehicles.begin(), myChargingVehicles.end(), vehicle_position_sorter());
116}
117
118void
120 ow_mutex.lock();
121}
122
123void
125 ow_mutex.unlock();
126}
127
128void
130 myElecHybrid.push_back(elecHybrid);
131}
132
133void
135 myElecHybrid.erase(std::remove(myElecHybrid.begin(), myElecHybrid.end(), veh), myElecHybrid.end());
136}
137
138void
140 std::cout << "substation " << getID() << " constrols segments: \n";
141 for (std::vector<MSOverheadWire*>::iterator it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); ++it) {
142 std::cout << " " << (*it)->getOverheadWireSegmentName() << "\n";
143 }
144}
145
146
150
153
154Circuit*
156 if (getTractionSubstation() != nullptr) {
158 }
159 return nullptr;
160}
161
162double
164 return myVoltage;
165}
166
167void
169 if (voltage < 0) {
170 WRITE_WARNING("New " + toString(SUMO_ATTR_VOLTAGE) + " for " + toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " isn't valid (" + toString(voltage) + ").")
171 } else {
172 myVoltage = voltage;
173 }
174}
175
176void
180
181
182void
186
187bool
188MSOverheadWire::vehicleIsInside(const double position) const {
189 if ((position >= getBeginLanePosition()) && (position <= getEndLanePosition())) {
190 return true;
191 } else {
192 return false;
193 }
194}
195
196
197bool
201
202
203void
204MSOverheadWire::addChargeValueForOutput(double WCharged, MSDevice_ElecHybrid* elecHybrid, bool ischarging) {
205 std::string status = "charging";
206 if (!ischarging) {
207 status = "not-charging";
208 }
209
210 // update total charge
211 myTotalCharge += WCharged;
212 // create charge row and insert it in myChargeValues
213 const std::string vehID = elecHybrid->getHolder().getID();
214 if (myChargeValues.count(vehID) == 0) {
215 myChargedVehicles.push_back(vehID);
216 }
217 Charge C(MSNet::getInstance()->getCurrentTimeStep(), elecHybrid->getHolder().getID(), elecHybrid->getHolder().getVehicleType().getID(),
218 status, WCharged, elecHybrid->getActualBatteryCapacity(), elecHybrid->getMaximumBatteryCapacity(),
220 myChargeValues[vehID].push_back(C);
221}
222
223
224void
226 int chargingSteps = 0;
227 std::vector<SUMOTime> chargingSteps_list;
228 for (const auto& item : myChargeValues) {
229 for (auto it : item.second) {
230 if (std::find(chargingSteps_list.begin(), chargingSteps_list.end(), it.timeStep) == chargingSteps_list.end()) {
231 chargingSteps_list.push_back(it.timeStep);
232 }
233 }
234 }
235 chargingSteps = (int) chargingSteps_list.size();
237 output.writeAttr(SUMO_ATTR_ID, myID);
238 if (getTractionSubstation() != nullptr) {
240 } else {
242 }
244
245 // RICE_TODO QUESTION myChargeValues.size() vs. chargingSteps
246 // myChargeValues.size() is the number of vehicles charging sometimes from this overheadwire segment during simulation
247 // chargingSteps is now the sum of chargingSteps of each vehicle, but takes also into account that at the given
248 // step more than one vehicle may be charged from this segment
249 output.writeAttr(SUMO_ATTR_CHARGINGSTEPS, chargingSteps);
250 // output.writeAttr(SUMO_ATTR_EDGE, getLane().getEdge());
252
253 // Start writing
254 if (myChargeValues.size() > 0) {
255 for (const std::string& vehID : myChargedVehicles) {
256 int iStart = 0;
257 const auto& chargeSteps = myChargeValues[vehID];
258 while (iStart < (int)chargeSteps.size()) {
259 int iEnd = iStart + 1;
260 double charged = chargeSteps[iStart].WCharged;
261 while (iEnd < (int)chargeSteps.size() && chargeSteps[iEnd].timeStep == chargeSteps[iEnd - 1].timeStep + DELTA_T) {
262 charged += chargeSteps[iEnd].WCharged;
263 iEnd++;
264 }
265 writeVehicle(output, chargeSteps, iStart, iEnd, charged);
266 iStart = iEnd;
267 }
268 }
269 }
270 // close charging station tag
271 output.closeTag();
272}
273
274
275void
276MSOverheadWire::writeVehicle(OutputDevice& out, const std::vector<Charge>& chargeSteps, int iStart, int iEnd, double charged) {
277 const Charge& first = chargeSteps[iStart];
279 out.writeAttr(SUMO_ATTR_ID, first.vehicleID);
283 out.writeAttr(SUMO_ATTR_CHARGINGEND, time2string(chargeSteps[iEnd - 1].timeStep));
285 for (int i = iStart; i < iEnd; i++) {
286 const Charge& c = chargeSteps[i];
289 // charge values
293 // charging values of charging station in this timestep
295 // battery status of vehicle
297 // close tag timestep
298 out.closeTag();
299 }
300 out.closeTag();
301}
302
303
304// ===========================================================================
305// MSTractionSubstation
306// ===========================================================================
307// RICE_TODO Split MSTractionSubstation and MSOverheadWire?
308// Probably no as the traction substation cannot stand alone and is always
309// used together with the overhead wire. It is a bit disorganised, though.
310
311MSTractionSubstation::MSTractionSubstation(const std::string& substationId, double voltage, double currentLimit) :
312 Named(substationId),
313 myChargingVehicle(false),
314 myElecHybridCount(0),
315 mySubstationVoltage(voltage),
316 myCircuit(new Circuit(currentLimit)),
317 myTotalEnergy(0)
318{}
319
320
321
322void
324 MSLane& lane = const_cast<MSLane&>(newOverheadWireSegment->getLane());
325 if (lane.isInternal()) {
326 return;
327 }
328
329 // RICE_TODO: consider the possibility of having more segments that belong to one lane.
330
331 myOverheadWireSegments.push_back(newOverheadWireSegment);
332 newOverheadWireSegment->setTractionSubstation(this);
333
335#ifdef HAVE_EIGEN
336 Circuit* circuit = newOverheadWireSegment->getCircuit();
337 const std::string segmentID = newOverheadWireSegment->getID();
338
339 if (circuit->getNode("negNode_ground") == nullptr) {
340 circuit->addNode("negNode_ground");
341 }
342
343 // convention: pNode is at the beginning of the wire segment, nNode is at the end of the wire segment
344 newOverheadWireSegment->setCircuitStartNodePos(circuit->addNode("pNode_pos_" + segmentID));
345 newOverheadWireSegment->setCircuitEndNodePos(circuit->addNode("nNode_pos_" + segmentID));
346 // RICE_TODO: to use startPos and endPos of ovhdsegment: set the length of wire here properly
347 newOverheadWireSegment->setCircuitElementPos(
348 circuit->addElement("pos_" + segmentID,
349 (newOverheadWireSegment->getLane().getLength()) * WIRE_RESISTIVITY,
350 newOverheadWireSegment->getCircuitStartNodePos(),
351 newOverheadWireSegment->getCircuitEndNodePos(),
353#else
354 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
355#endif
356 }
357
358 const MSLane* connection = nullptr;
359 std::string ovrhdSegmentID = ""; //ID of outgoing or incoming overhead wire segment
360 MSOverheadWire* ovrhdSegment = nullptr; //pointer to outgoing or incoming overhead wire segment
361
362 // RICE_TODO: simplify the code, two similar code-blocks below
363 // RICE_TODO: to use startPos and endPos of ovhdsegment: if endPos+EPS > newOverheadWireSegment->getLane().getLength(),
364 // and the outgoing lanes will be skipped as there is no wire at the end of the lane
365
366 /* in version before SUMO 1.0.1 the function getOutgoingLanes() returning MSLane* exists,
367 in new version of SUMO the funciton getOutgoingViaLanes() returning MSLane* and MSEdge* pair exists */
368 // std::vector<const MSLane*> outgoing = lane.getOutgoingLanes();
369 const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane.getOutgoingViaLanes();
370 std::vector<const MSLane*> neigboringInnerLanes;
371 neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
372 for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
373 neigboringInnerLanes.push_back(outgoingLanesAndEdges[it].first);
374 }
375
376 // Check if there is an overhead wire segment on the outgoing lane. If not, do nothing, otherwise find connnecting internal lanes and
377 // add all lanes (this and inner) to circuit
378 for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
379 ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
380 // If the overhead wire segment is over the outgoing (not internal) lane
381 if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
382 ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
383 // If the outgoing overhead wire segment belongs to the same substation as newOverheadWireSegment
384 // RICE_TODO: define what happens if the traction stations are different (overhead wire should continue over inner segments but it is unclear to which traction substation or even circuit it should be connected)
385 if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
386 connection = lane.getInternalFollowingLane(*it);
387 if (connection != nullptr) {
388 //is connection a forbidden lane?
389 if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
390 ovrhdSegment->getTractionSubstation()->isForbidden(lane.getInternalFollowingLane(connection)) ||
391 ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(*it)))) {
392 addOverheadWireInnerSegmentToCircuit(newOverheadWireSegment, ovrhdSegment, connection, lane.getInternalFollowingLane(connection), connection->getInternalFollowingLane(*it));
393 }
394
395 } else {
397#ifdef HAVE_EIGEN
398 Node* const unusedNode = newOverheadWireSegment->getCircuitEndNodePos();
399 for (MSOverheadWire* const ows : myOverheadWireSegments) {
400 if (ows->getCircuitStartNodePos() == unusedNode) {
401 ows->setCircuitStartNodePos(ovrhdSegment->getCircuitStartNodePos());
402 }
403 if (ows->getCircuitEndNodePos() == unusedNode) {
404 ows->setCircuitEndNodePos(ovrhdSegment->getCircuitStartNodePos());
405 }
406 }
407 newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitStartNodePos());
408#else
409 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
410#endif
411 }
412 }
413 }
414 }
415 }
416
417 // RICE_TODO: to use startPos and endPos of ovhdsegment: if startPos-EPS < 0,
418 // and the incoming lanes will be skipped as there is no wire at the beginning of the lane
419
420 // This is the same as above, only this time checking the wires on some incoming lanes. If some of them
421 // has an overhead wire segment, find the connnecting internal lanes and add all lanes (the internal
422 // and this) to the circuit, otherwise do nothing.
423 neigboringInnerLanes = lane.getNormalIncomingLanes();
424 for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
425 ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
426 // If the overhead wire segment is over the incoming (not internal) lane
427 if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
428 ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
429 // If the incoming overhead wire segment belongs to the same substation as newOverheadWireSegment
430 // RICE_TODO: define what happens if the traction stations are different (overhead wire should continue over inner segments but it is unclear to which traction substation or even circuit it should be connected)
431 if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
432 connection = (*it)->getInternalFollowingLane(&lane);
433 if (connection != nullptr) {
434 //is connection a forbidden lane?
435 if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
436 ovrhdSegment->getTractionSubstation()->isForbidden((*it)->getInternalFollowingLane(connection)) ||
437 ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(&lane)))) {
438 addOverheadWireInnerSegmentToCircuit(ovrhdSegment, newOverheadWireSegment, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(&lane));
439 }
440 } else {
442#ifdef HAVE_EIGEN
443 Node* const unusedNode = newOverheadWireSegment->getCircuitStartNodePos();
444 for (MSOverheadWire* const ows : myOverheadWireSegments) {
445 if (ows->getCircuitStartNodePos() == unusedNode) {
446 ows->setCircuitStartNodePos(ovrhdSegment->getCircuitEndNodePos());
447 }
448 if (ows->getCircuitEndNodePos() == unusedNode) {
449 ows->setCircuitEndNodePos(ovrhdSegment->getCircuitEndNodePos());
450 }
451 }
452 newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitEndNodePos());
453#else
454 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
455#endif
456 }
457 }
458 }
459 }
460 }
461
462 if (MSGlobals::gOverheadWireSolver && newOverheadWireSegment->isThereVoltageSource()) {
463#ifdef HAVE_EIGEN
464 newOverheadWireSegment->getCircuit()->addElement(
465 "voltage_source_" + newOverheadWireSegment->getID(),
467 newOverheadWireSegment->getCircuitStartNodePos(),
468 newOverheadWireSegment->getCircuit()->getNode("negNode_ground"),
470#else
471 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
472#endif
473 }
474}
475
476
477void
478MSTractionSubstation::addOverheadWireInnerSegmentToCircuit(MSOverheadWire* incomingSegment, MSOverheadWire* outgoingSegment, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
479 if (frontConnection == nullptr && behindConnection == nullptr) {
480 // addOverheadWire from nNode of newOverheadWireSegment to pNode
481 MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
482 myOverheadWireSegments.push_back(innerSegment);
483 innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
485#ifdef HAVE_EIGEN
486 Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
487 innerSegment->setCircuitElementPos(elem);
488 innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
489 innerSegment->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
490#else
491 UNUSED_PARAMETER(outgoingSegment);
492 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
493#endif
494 }
495 } else if (frontConnection != nullptr && behindConnection == nullptr) {
496 MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
497 MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
498
499 innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
500 myOverheadWireSegments.push_back(innerSegment);
501 innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
502 myOverheadWireSegments.push_back(innerSegment2);
503
505#ifdef HAVE_EIGEN
506 Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
507 Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
508 Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
509
510 innerSegment->setCircuitElementPos(elem);
511 innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
512 innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
513
514 innerSegment2->setCircuitElementPos(elem2);
515 innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
516 innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
517#else
518 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
519#endif
520 }
521 } else if (frontConnection == nullptr && behindConnection != nullptr) {
522 MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
523 MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
524
525 innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
526 myOverheadWireSegments.push_back(innerSegment);
527 innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
528 myOverheadWireSegments.push_back(innerSegment2);
529
531#ifdef HAVE_EIGEN
532 Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
533 Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
534 Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
535
536 innerSegment->setCircuitElementPos(elem);
537 innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
538 innerSegment->setCircuitEndNodePos(betweenBehindNode_pos);
539
540 innerSegment2->setCircuitElementPos(elem2);
541 innerSegment2->setCircuitStartNodePos(betweenBehindNode_pos);
542 innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
543#else
544 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
545#endif
546 }
547 } else if (frontConnection != nullptr && behindConnection != nullptr) {
548 MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
549 MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
550 MSOverheadWire* innerSegment3 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
551
552 innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
553 myOverheadWireSegments.push_back(innerSegment);
554 innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
555 myOverheadWireSegments.push_back(innerSegment2);
556 innerSegment3->setTractionSubstation(incomingSegment->getTractionSubstation());
557 myOverheadWireSegments.push_back(innerSegment3);
558
560#ifdef HAVE_EIGEN
561 Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
562 Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
563 Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
564 Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
565 Element* elem3 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
566
567 innerSegment->setCircuitElementPos(elem);
568 innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
569 innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
570
571 innerSegment2->setCircuitElementPos(elem2);
572 innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
573 innerSegment2->setCircuitEndNodePos(betweenBehindNode_pos);
574
575 innerSegment3->setCircuitElementPos(elem3);
576 innerSegment3->setCircuitStartNodePos(betweenBehindNode_pos);
577 innerSegment3->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
578#else
579 WRITE_WARNING(TL("Overhead circuit solver requested, but solver support not compiled in."));
580#endif
581 }
582 }
583}
584
585
586void MSTractionSubstation::addOverheadWireClampToCircuit(const std::string id, MSOverheadWire* startSegment, MSOverheadWire* endSegment) {
587 PositionVector pos_start = startSegment->getLane().getShape();
588 PositionVector pos_end = endSegment->getLane().getShape();
589 double distance = pos_start[0].distanceTo2D(pos_end.back());
590
591 if (distance > 10) {
592 WRITE_WARNING("The distance between two overhead wires during adding overhead wire clamp '" + id + "' defined for traction substation '" + startSegment->getTractionSubstation()->getID() + "' is " + toString(distance) + " m.")
593 }
595}
596
597
598void
600 //myOverheadWireSegments.push_back(static_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT)));
601 myOverheadWireSegments.erase(std::remove(myOverheadWireSegments.begin(), myOverheadWireSegments.end(), oldSegment), myOverheadWireSegments.end());
602}
603
604
605bool
609
610
611void
615
616
617void
621
622
624 myForbiddenLanes.push_back(lane);
625}
626
627
629 for (std::vector<MSLane*>::iterator it = myForbiddenLanes.begin(); it != myForbiddenLanes.end(); ++it) {
630 if (lane == (*it)) {
631 return true;
632 }
633 }
634 return false;
635}
636
637
638void
639MSTractionSubstation::addClamp(const std::string& id, MSOverheadWire* startPos, MSOverheadWire* endPos) {
640 OverheadWireClamp clamp(id, startPos, endPos, false);
641 myOverheadWireClamps.push_back(clamp);
642}
643
644
647 for (auto it = myOverheadWireClamps.begin(); it != myOverheadWireClamps.end(); it++) {
648 if (it->id == clampId) {
649 return &(*it);
650 }
651 }
652 return nullptr;
653}
654
655
656bool
658 if (myOverheadWireSegments.size() > 0 || myForbiddenLanes.size() > 0 || getCircuit()->getLastId() > 0) {
659 return true;
660 }
661 return false;
662}
663
664
665void
667 if (!myChargingVehicle) {
668 // myCommandForSolvingCircuit = new StaticCommand<MSTractionSubstation>(&MSTractionSubstation::solveCircuit);
671 setChargingVehicle(true);
672 }
673}
674
675
678 /*Circuit evaluation*/
679 setChargingVehicle(false);
680
681#ifdef HAVE_EIGEN
682
683 // RICE_TODO: Allow for updating current limits in each time step if changed e.g. via traci or similar
684 // getCircuit()->setCurrentLimit(myCurrentLimit);
685
686 // Solve the electrical circuit
687 myCircuit->solve();
688
689 if (myCircuit->getAlphaBest() != 1.0) {
690 WRITE_WARNINGF(TL("The requested total power could not be delivered by the overhead wire. Only % of originally requested power was provided."), toString(myCircuit->getAlphaBest()));
691 }
692#endif
693
694 // RICE_TODO: verify what happens if eigen is not defined?
695 // Note: addSolvingCircuitToEndOfTimestepEvents() and thus solveCircuit() should be called from notifyMove only if eigen is defined.
697
698 for (auto* it : myElecHybrid) {
699
700 Element* vehElem = it->getVehElem();
701 double voltage = vehElem->getVoltage();
702 double current = -vehElem->getCurrent(); // Vehicle is a power source, hence its current (returned by getCurrent()) flows in opposite direction
703
704 it->setCurrentFromOverheadWire(current);
705 it->setVoltageOfOverheadWire(voltage);
706
707 // Calulate energy charged
708 double energyIn = WATT2WATTHR(voltage * current); // [Wh]
709
710 // Compute energy charged into/from battery considering recuperation and propulsion efficiency (not considering battery capacity)
711 double energyCharged = it->computeChargedEnergy(energyIn);
712
713 // Update energy saved in the battery pack and return trully charged energy considering limits of battery
714 double realEnergyCharged = it->storeEnergyToBattery(energyCharged);
715
716 it->setEnergyCharged(realEnergyCharged);
717
718 // Add energy wasted to the total sum
719 it->updateTotalEnergyWasted(energyCharged - realEnergyCharged);
720 // Add the energy provided by the overhead wire segment to the output of the segment
721 it->getActOverheadWireSegment()->addChargeValueForOutput(energyIn, it);
722 }
723
724 return 0;
725}
726
727void
728MSTractionSubstation::addChargeValueForOutput(double energy, double current, double alpha, Circuit::alphaFlag alphaReason) {
729 std::string status = "";
730
731 myTotalEnergy += energy; //[Wh]
732
733 std::string vehicleIDs = "";
734 for (std::vector<MSDevice_ElecHybrid*>::iterator it = myElecHybrid.begin(); it != myElecHybrid.end(); it++) {
735 vehicleIDs += (*it)->getID() + " ";
736 }
737 //vehicleIDs.erase(vehicleIDs.end());
738 // TODO vehicleIDs should not be empty, but in some case, it is (due to teleporting of vehicle?)
739 if (!vehicleIDs.empty()) {
740 vehicleIDs.pop_back();
741 }
742
743 std::string currents = "";
744 currents = myCircuit->getCurrentsOfCircuitSource(currents);
745
746 // create charge row and insert it in myChargeValues
747 chargeTS C(MSNet::getInstance()->getCurrentTimeStep(), getID(), vehicleIDs, energy, current, currents, mySubstationVoltage, status,
748 (int)myElecHybrid.size(), (int)getCircuit()->getNumVoltageSources(), alpha, alphaReason);
749 myChargeValues.push_back(C);
750}
751
752void
755 output.writeAttr(SUMO_ATTR_ID, myID);
757 double length = 0;
758 for (auto it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); it++) {
759 length += (*it)->getEndLanePosition() - (*it)->getBeginLanePosition();
760 }
761 output.writeAttr(SUMO_ATTR_LENGTH, length);
762 output.writeAttr("numVoltageSources", myCircuit->getNumVoltageSources());
763 output.writeAttr("numClamps", myOverheadWireClamps.size());
765
766 // start writting
767 if (myChargeValues.size() > 0) {
768 // iterate over charging values
769 for (std::vector<MSTractionSubstation::chargeTS>::const_iterator i = myChargeValues.begin(); i != myChargeValues.end(); i++) {
770 // open tag for timestep and write all parameters
771 output.openTag(SUMO_TAG_STEP);
772 output.writeAttr(SUMO_ATTR_TIME, time2string(i->timeStep));
773 // charge values
774 output.writeAttr("vehicleIDs", i->vehicleIDs);
775 output.writeAttr("numVehicles", i->numVehicles);
776 // same number of numVoltageSources for all time, parameter is written in the superordinate tag
777 //output.writeAttr("numVoltageSources", i->numVoltageSources);
778 // charging status is always ""
779 //output.writeAttr(SUMO_ATTR_CHARGING_STATUS, i->status);
780 output.writeAttr(SUMO_ATTR_ENERGYCHARGED, i->energy);
782 output.writeAttr("currents", i->currentsString);
783 // charging values of charging station in this timestep
784 output.writeAttr(SUMO_ATTR_VOLTAGE, i->voltage);
785 output.writeAttr(SUMO_ATTR_ALPHACIRCUITSOLVER, i->alpha);
786 output.writeAttr("alphaFlag", i->alphaReason);
787 // close tag timestep
788 output.closeTag();
789 // update timestep of charge
790 }
791 }
792 // close charging station tag
793 output.closeTag();
794}
795
796/****************************************************************************/
long long int SUMOTime
Definition GUI.h:36
static std::mutex ow_mutex
#define WATT2WATTHR(_x)
const double WIRE_RESISTIVITY
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
SUMOTime DELTA_T
Definition SUMOTime.cpp:38
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:69
@ SUMO_TAG_TRACTION_SUBSTATION
A traction substation.
@ SUMO_TAG_STEP
trigger: a step description
@ SUMO_TAG_VEHICLE
description of a vehicle
@ SUMO_TAG_OVERHEAD_WIRE_SEGMENT
An overhead wire segment.
@ SUMO_ATTR_TRACTIONSUBSTATIONID
@ SUMO_ATTR_PARTIALCHARGE
energy provided by charging station at certain timestep
@ SUMO_ATTR_LANE
@ SUMO_ATTR_TOTALENERGYCHARGED
@ SUMO_ATTR_VOLTAGE
voltage of the traction substation [V]
@ SUMO_ATTR_MAXIMUMBATTERYCAPACITY
Maxium battery capacity.
@ SUMO_ATTR_TOTALENERGYCHARGED_VEHICLE
total energy charged into a single vehicle
@ SUMO_ATTR_ACTUALBATTERYCAPACITY
@ SUMO_ATTR_ENERGYCHARGED
tgotal of Energy charged
@ SUMO_ATTR_TYPE
@ SUMO_ATTR_LENGTH
@ SUMO_ATTR_CHARGINGSTEPS
number of steps that a vehicle is charging
@ SUMO_ATTR_CHARGINGEND
timesteps in which charging ends
@ SUMO_ATTR_CHARGINGBEGIN
timestep in which charging begins
@ SUMO_ATTR_ID
@ SUMO_ATTR_ALPHACIRCUITSOLVER
@ SUMO_ATTR_CURRENTFROMOVERHEADWIRE
@ SUMO_ATTR_TIME
trigger: the time of the step
@ SUMO_ATTR_CHARGING_STATUS
#define UNUSED_PARAMETER(x)
Definition StdDefs.h:30
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
double getAlphaBest()
return alphaBest variable, the best alpha scaling value
Definition Circuit.h:127
Node * addNode(std::string name)
Definition Circuit.cpp:42
int getNumVoltageSources()
Definition Circuit.cpp:979
Element * addElement(std::string name, double value, Node *pNode, Node *nNode, Element::ElementType et)
Definition Circuit.cpp:787
void eraseNode(Node *node)
Definition Circuit.cpp:62
double getTotalCurrentOfCircuitSources()
The sum of voltage source currents in the circuit.
Definition Circuit.cpp:156
void replaceAndDeleteNode(Node *unusedNode, Node *newNode)
Definition Circuit.cpp:839
double getTotalPowerOfCircuitSources()
The sum of voltage source powers in the circuit.
Definition Circuit.cpp:148
alphaFlag
Flag of alpha scaling parameter.
Definition Circuit.h:96
alphaFlag getAlphaReason()
return the reason why alpha scaling value has been used
Definition Circuit.h:132
Node * getNode(std::string name)
Definition Circuit.cpp:98
std::string & getCurrentsOfCircuitSource(std::string &currents)
List of currents of voltage sources as a string.
Definition Circuit.cpp:165
void eraseElement(Element *element)
Definition Circuit.cpp:831
Base (microsim) event class.
Definition Command.h:50
Node * getNegNode()
Definition Element.cpp:115
double getCurrent()
Definition Element.cpp:85
Node * getPosNode()
Definition Element.cpp:112
double getVoltage()
Definition Element.cpp:76
@ RESISTOR_traction_wire
Definition Element.h:54
@ VOLTAGE_SOURCE_traction_wire
Definition Element.h:56
A device which collects info on the vehicle trip (mainly on departure and arrival)
double getVoltageOfOverheadWire() const
Get actual voltage on the overhead wire segment.
double getMaximumBatteryCapacity() const
Get the total vehicle's Battery Capacity in kWh.
double getActualBatteryCapacity() const
Get the actual vehicle's Battery Capacity in kWh.
virtual void addEvent(Command *operation, SUMOTime execTimeStep=-1)
Adds an Event.
static bool gOverheadWireSolver
Definition MSGlobals.h:121
Representation of a lane in the micro simulation.
Definition MSLane.h:84
double getLength() const
Returns the lane's length.
Definition MSLane.h:606
const MSLane * getInternalFollowingLane(const MSLane *const) const
returns the internal lane leading to the given lane or nullptr, if there is none
Definition MSLane.cpp:2681
const std::vector< std::pair< const MSLane *, const MSEdge * > > getOutgoingViaLanes() const
get the list of outgoing lanes
Definition MSLane.cpp:3248
bool isInternal() const
Definition MSLane.cpp:2546
std::vector< const MSLane * > getNormalIncomingLanes() const
get the list of all direct (disregarding internal predecessors) non-internal predecessor lanes of thi...
Definition MSLane.cpp:3258
virtual const PositionVector & getShape(bool) const
Definition MSLane.h:294
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:186
MSEventControl * getEndOfTimestepEvents()
Returns the event control for events executed at the end of a time step.
Definition MSNet.h:481
std::string getStoppingPlaceID(const MSLane *lane, const double pos, const SumoXMLTag category) const
Returns the stop of the given category close to the given position.
Definition MSNet.cpp:1402
MSStoppingPlace * getStoppingPlace(const std::string &id, const SumoXMLTag category) const
Returns the named stopping place of the given category.
Definition MSNet.cpp:1381
A class for sorting vehicle on lane under the overhead wire segment.
Definition of overhead wire segment.
std::map< std::string, std::vector< Charge > > myChargeValues
map with the charges of this charging station (key = vehicleID)
void unlock() const
void setCircuitEndNodePos(Node *node)
bool myChargingVehicle
Check if in the current TimeStep overheadWireSegment is charging a vehicle.
Element * myCircuitElementPos
Node * myCircuitEndNodePos
void setCircuitElementPos(Element *element)
static void writeVehicle(OutputDevice &out, const std::vector< Charge > &chargeSteps, int iStart, int iEnd, double charged)
void writeOverheadWireSegmentOutput(OutputDevice &output)
write overhead wire segment values
Node * getCircuitStartNodePos() const
void setChargingVehicle(bool value)
enable or disable charging vehicle
bool vehicleIsInside(const double position) const
Check if a vehicle is inside in the Charge Station.
Node * getCircuitEndNodePos() const
MSOverheadWire(const std::string &overheadWireSegmentID, MSLane &lane, double startPos, double endPos, bool voltageSource)
constructor
void setCircuitStartNodePos(Node *node)
std::string getOverheadWireSegmentName()
double myTotalCharge
total energy charged by this charging station
Node * myCircuitStartNodePos
void lock() const
void setVoltage(double voltage)
Set overhead wire's voltage.
std::vector< std::string > myChargedVehicles
order vehicles by time of first charge
bool isThereVoltageSource() const
bool isCharging() const
Return true if in the current time step charging station is charging a vehicle.
Circuit * getCircuit() const
double getVoltage() const
Get overhead wire's voltage.
void addChargeValueForOutput(double WCharged, MSDevice_ElecHybrid *elecHybrid, bool ischarging=1)
add charge value for output
MSTractionSubstation * getTractionSubstation() const
void eraseVehicle(SUMOVehicle &veh)
void setTractionSubstation(MSTractionSubstation *substation)
MSTractionSubstation * myTractionSubstation
Parameter, Pointer to the electrical substation (by default is nullptr)
std::vector< SUMOVehicle * > myChargingVehicles
void addVehicle(SUMOVehicle &veh)
~MSOverheadWire()
destructor
double myVoltage
Overhead wire's voltage.
A lane area vehicles can halt at.
double getBeginLanePosition() const
Returns the begin position of this stop.
double getEndLanePosition() const
Returns the end position of this stop.
const MSLane & getLane() const
Returns the lane this stop is located at.
bool myChargingVehicle
Check if in the current TimeStep substation (overhead wire section) is charging a vehicle.
void eraseVehicle(MSDevice_ElecHybrid *elecHybrid)
void addOverheadWireInnerSegmentToCircuit(MSOverheadWire *incomingSegment, MSOverheadWire *outgoingSegment, const MSLane *connection, const MSLane *frontConnection, const MSLane *behindConnection)
std::size_t numberOfOverheadSegments() const
~MSTractionSubstation()
destructor
void eraseOverheadWireSegmentFromCircuit(MSOverheadWire *oldWireSegment)
std::vector< OverheadWireClamp > myOverheadWireClamps
std::vector< MSOverheadWire * > myOverheadWireSegments
void addOverheadWireClampToCircuit(const std::string id, MSOverheadWire *startSegment, MSOverheadWire *endSegment)
void addClamp(const std::string &id, MSOverheadWire *startPos, MSOverheadWire *endPos)
void addSolvingCircuitToEndOfTimestepEvents()
bool isCharging() const
Return true if in the current time step the substation (overhead wire section) is charging a vehicle.
void writeTractionSubstationOutput(OutputDevice &output)
write traction substation values
std::vector< MSDevice_ElecHybrid * > myElecHybrid
bool isForbidden(const MSLane *lane)
std::vector< MSLane * > myForbiddenLanes
Circuit * getCircuit() const
void addVehicle(MSDevice_ElecHybrid *elecHybrid)
std::vector< chargeTS > myChargeValues
SUMOTime solveCircuit(SUMOTime currentTime)
void addOverheadWireSegmentToCircuit(MSOverheadWire *newOverheadWireSegment)
MSTractionSubstation(const std::string &substationId, double voltage, double currentLimit)
Constructor instantiates a substation providing certain voltage and a maximum current.
OverheadWireClamp * findClamp(std::string id)
Find an overhead wire clamp by its ID.
static Command * myCommandForSolvingCircuit
void addForbiddenLane(MSLane *lane)
void setChargingVehicle(bool value)
enable or disable charging vehicle
void addChargeValueForOutput(double energy, double current, double alpha, Circuit::alphaFlag alphaReason)
add charge value for output
SUMOVehicle & getHolder() const
Returns the vehicle that holds this device.
const std::string & getID() const
Returns the name of the vehicle type.
Base class for objects which have an id.
Definition Named.h:54
std::string myID
The name of the object.
Definition Named.h:125
const std::string & getID() const
Returns the id.
Definition Named.h:74
Definition Node.h:39
std::vector< Element * > * getElements()
Definition Node.cpp:101
Static storage of an output device and its base (abstract) implementation.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
A list of positions.
virtual const MSVehicleType & getVehicleType() const =0
Returns the object's "vehicle" type.
Representation of a vehicle.
Definition SUMOVehicle.h:62
A wrapper for a Command function.
Definition json.hpp:4471
struct to save information for the overhead wire segment output
std::string status
status
struct to save information for the traction substation output