Eclipse SUMO - Simulation of Urban MObility
MSOverheadWire.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2002-2020 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 // Overhead wires for Electric (equipped with elecHybrid device) vehicles (Overhead wire segments, overhead wire sections, traction substations)
20 /****************************************************************************/
21 #include <config.h>
22 
23 #include <cassert>
24 #include <tuple>
25 #include <mutex>
26 #include <string.h>
27 
29 #include <utils/common/ToString.h>
30 #include <microsim/MSVehicleType.h>
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 
46 #include "MSOverheadWire.h"
47 #include "MSTrigger.h"
48 
49 
51 std::mutex ow_mutex;
52 
53 // ===========================================================================
54 // member method definitions
55 // ===========================================================================
56 
57 MSOverheadWire::MSOverheadWire(const std::string& overheadWireSegmentID, MSLane& lane, double startPos, double endPos, bool voltageSource) :
58  MSStoppingPlace(overheadWireSegmentID, std::vector<std::string>(), lane, startPos, endPos),
59  myVoltage(0),
60  myChargingVehicle(false),
61  myTotalCharge(0),
62  myChargingVehicles({}),
63  //TODORICE: some better structure with circuit pointers
64  myTractionSubstation(nullptr),
65  myVoltageSource(voltageSource),
66  myCircuitElementPos(nullptr),
67  myCircuitStartNodePos(nullptr),
68 myCircuitEndNodePos(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) {
79  if (myCircuitEndNodePos->getElements()->size() == 0) {
81  delete myCircuitEndNodePos;
82  }
83  if (myCircuitStartNodePos->getElements()->size() == 0) {
85  delete myCircuitStartNodePos;
86  }
88  delete myCircuitElementPos;
89  }
90 
93  //delete myTractionSubstation;
94  } else {
96  }
97  }
98 }
99 
100 
101 MSTractionSubstation::MSTractionSubstation(const std::string& substationId, double voltage) :
102  Named(substationId),
103  mySubstationVoltage(voltage),
104  myChargingVehicle(false),
105  myElecHybridCount(0),
106  myOverheadWireSegments(),
107  myElecHybrid(),
108  myCircuit(new Circuit()),
109  myForbiddenLanes(),
110  myOverheadWireClamps() {
111 }
112 
113 
114 void
116  MSLane& lane = const_cast<MSLane&>(newOverheadWireSegment->getLane());
117  if (lane.isInternal()) {
118  return;
119  }
120 
121  myOverheadWireSegments.push_back(newOverheadWireSegment);
122  newOverheadWireSegment->setTractionSubstation(this);
123 
125 #ifdef HAVE_EIGEN
126  if (newOverheadWireSegment->getCircuit()->getNode("negNode_ground") == nullptr) {
127  newOverheadWireSegment->getCircuit()->addNode("negNode_ground");
128  }
129 
130  // convention: pNode is at the beginning of the wire segment, nNode is at the end of the wire segment
131  newOverheadWireSegment->setCircuitStartNodePos(newOverheadWireSegment->getCircuit()->addNode("pNode_pos_" + newOverheadWireSegment->getID()));
132  newOverheadWireSegment->setCircuitEndNodePos(newOverheadWireSegment->getCircuit()->addNode("nNode_pos_" + newOverheadWireSegment->getID()));
133  newOverheadWireSegment->setCircuitElementPos(newOverheadWireSegment->getCircuit()->addElement("pos_" + newOverheadWireSegment->getID(), (newOverheadWireSegment->getLane().getLength())*WIRE_RESISTIVITY, newOverheadWireSegment->getCircuitStartNodePos(), newOverheadWireSegment->getCircuitEndNodePos(), Element::ElementType::RESISTOR_traction_wire));
134 #else
135  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
136 #endif
137  }
138 
139  const MSLane* connection = nullptr;
140  std::string ovrhdSegmentID = ""; //ID of outgoing or incoming overhead wire segment
141  MSOverheadWire* ovrhdSegment = nullptr; //pointer to outgoing or incoming overhead wire segment
142 
143  /* in version before SUMO 1.0.1 the function getOutgoingLanes() returning MSLane* exists,
144  in new version of SUMO the funciton getOutgoingViaLanes() returning MSLane* and MSEdge* pair exists */
145  // std::vector<const MSLane*> outgoing = lane.getOutgoingLanes();
146  const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane.getOutgoingViaLanes();
147  std::vector<const MSLane*> neigboringInnerLanes;
148  neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
149  for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
150  neigboringInnerLanes.push_back(outgoingLanesAndEdges[it].first);
151  }
152 
153  // Check if there is an overhead wire segment on the outgoing lane. If not, do nothing, otherwise find connnecting internal lanes and
154  // add all lane (this and inner) to circuit
155  for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
156  ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
157  // If the overhead wire segment is over the outgoing (not internal) lane
158  if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
159  ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
160  // If the outgoing overhead wire segment belongs to the same substation as newOverheadWireSegment
161  if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
162  connection = lane.getInternalFollowingLane(*it);
163  if (connection != nullptr) {
164  //is connection a forbidden lane?
165  if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
166  ovrhdSegment->getTractionSubstation()->isForbidden(lane.getInternalFollowingLane(connection)) ||
167  ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(*it)))) {
168  addOverheadWireInnerSegmentToCircuit(newOverheadWireSegment, ovrhdSegment, connection, lane.getInternalFollowingLane(connection), connection->getInternalFollowingLane(*it));
169  }
170 
171  } else {
173 #ifdef HAVE_EIGEN
174  Node* const unusedNode = newOverheadWireSegment->getCircuitEndNodePos();
175  for (MSOverheadWire* const wire : myOverheadWireSegments) {
176  if (wire->getCircuitStartNodePos() == unusedNode) {
177  wire->setCircuitStartNodePos(ovrhdSegment->getCircuitStartNodePos());
178  }
179  if (wire->getCircuitEndNodePos() == unusedNode) {
180  wire->setCircuitEndNodePos(ovrhdSegment->getCircuitStartNodePos());
181  }
182  }
183  newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitStartNodePos());
184  // newOverheadWireSegment->getCircuitElementPos()->setPosNode(ovrhdSegment->getCircuitEndNodePos());
185  // newOverheadWireSegment->setCircuitEndNodePos(ovrhdSegment->getCircuitStartNodePos());
186  // TODORICE change voltageSource pNode if necessary
187 #else
188  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
189 #endif
190  }
191  }
192  }
193  }
194  }
195 
196  // Check if an incoming lane has an overhead wire segment. If not, do nothing, otherwise find connnecting internal lanes and
197  // add all lane (this, incoming and inner)to circuit
198  neigboringInnerLanes = lane.getNormalIncomingLanes();
199  for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
200  ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
201  // If the overhead wire segment is over the incoming (not internal) lane
202  if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
203  ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
204  // If the incoming overhead wire segment belongs to the same substation as newOverheadWireSegment
205  if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
206  connection = (*it)->getInternalFollowingLane(&lane);
207  if (connection != nullptr) {
208  //is connection a forbidden lane?
209  if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
210  ovrhdSegment->getTractionSubstation()->isForbidden((*it)->getInternalFollowingLane(connection)) ||
211  ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(&lane)))) {
212  addOverheadWireInnerSegmentToCircuit(ovrhdSegment, newOverheadWireSegment, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(&lane));
213  }
214  } else {
216 #ifdef HAVE_EIGEN
217  Node* const unusedNode = newOverheadWireSegment->getCircuitStartNodePos();
218  for (MSOverheadWire* const ows : myOverheadWireSegments) {
219  if (ows->getCircuitStartNodePos() == unusedNode) {
220  ows->setCircuitStartNodePos(ovrhdSegment->getCircuitEndNodePos());
221  }
222  if (ows->getCircuitEndNodePos() == unusedNode) {
223  ows->setCircuitEndNodePos(ovrhdSegment->getCircuitEndNodePos());
224  }
225  }
226  newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitEndNodePos());
227  //newOverheadWireSegment->getCircuitElementPos()->setPosNode(ovrhdSegment->getCircuitEndNodePos());
228  //newOverheadWireSegment->setCircuitStartNode_pos(ovrhdSegment->getCircuitEndNodePos());
229  // TODORICE change voltageSource pNode if necessary
230 #else
231  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
232 #endif
233  }
234  }
235  }
236  }
237  }
238  if (MSGlobals::gOverheadWireSolver && newOverheadWireSegment->isThereVoltageSource()) {
239 #ifdef HAVE_EIGEN
240  newOverheadWireSegment->getCircuit()->addElement("voltage_source_" + newOverheadWireSegment->getID(), mySubstationVoltage, newOverheadWireSegment->getCircuitStartNodePos(), newOverheadWireSegment->getCircuit()->getNode("negNode_ground"), Element::ElementType::VOLTAGE_SOURCE_traction_wire);
241 #else
242  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
243 #endif
244  }
245 }
246 
247 
248 void
249 MSTractionSubstation::addOverheadWireInnerSegmentToCircuit(MSOverheadWire* incomingSegment, MSOverheadWire* outgoingSegment, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
250  if (frontConnection == nullptr && behindConnection == nullptr) {
251  //addOverheadWire from nNode of newOverheadWireSegment to pNode
252  //TODORICE
253  //what happens if getCircuit is different? (do not check TractionSubstation but rather getCircuit)
254  //what happens if the segment is not at the all length of the specific lane
255  //what happens if more segments belong to the one lane
256  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
257  myOverheadWireSegments.push_back(innerSegment);
258  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
260 #ifdef HAVE_EIGEN
261  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
262  innerSegment->setCircuitElementPos(elem);
263  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
264  innerSegment->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
265 #else
266  UNUSED_PARAMETER(outgoingSegment);
267  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
268 #endif
269  }
270  } else if (frontConnection != nullptr && behindConnection == nullptr) {
271  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
272  MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
273 
274  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
275  myOverheadWireSegments.push_back(innerSegment);
276  innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
277  myOverheadWireSegments.push_back(innerSegment2);
278 
280 #ifdef HAVE_EIGEN
281  Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
282  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
283  Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
284 
285  innerSegment->setCircuitElementPos(elem);
286  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
287  innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
288 
289  innerSegment2->setCircuitElementPos(elem2);
290  innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
291  innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
292 #else
293  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
294 #endif
295  }
296  } else if (frontConnection == nullptr && behindConnection != nullptr) {
297  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
298  MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
299 
300  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
301  myOverheadWireSegments.push_back(innerSegment);
302  innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
303  myOverheadWireSegments.push_back(innerSegment2);
304 
306 #ifdef HAVE_EIGEN
307  Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
308  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
309  Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
310 
311  innerSegment->setCircuitElementPos(elem);
312  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
313  innerSegment->setCircuitEndNodePos(betweenBehindNode_pos);
314 
315  innerSegment2->setCircuitElementPos(elem2);
316  innerSegment2->setCircuitStartNodePos(betweenBehindNode_pos);
317  innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
318 #else
319  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
320 #endif
321  }
322  } else if (frontConnection != nullptr && behindConnection != nullptr) {
323  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
324  MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
325  MSOverheadWire* innerSegment3 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
326 
327  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
328  myOverheadWireSegments.push_back(innerSegment);
329  innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
330  myOverheadWireSegments.push_back(innerSegment2);
331  innerSegment3->setTractionSubstation(incomingSegment->getTractionSubstation());
332  myOverheadWireSegments.push_back(innerSegment3);
333 
335 #ifdef HAVE_EIGEN
336  Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
337  Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
338  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
339  Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
340  Element* elem3 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
341 
342  innerSegment->setCircuitElementPos(elem);
343  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
344  innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
345 
346  innerSegment2->setCircuitElementPos(elem2);
347  innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
348  innerSegment2->setCircuitEndNodePos(betweenBehindNode_pos);
349 
350  innerSegment3->setCircuitElementPos(elem3);
351  innerSegment3->setCircuitStartNodePos(betweenBehindNode_pos);
352  innerSegment3->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
353 #else
354  WRITE_WARNING("Overhead circuit solver requested, but solver support not compiled in.");
355 #endif
356  }
357  }
358 }
359 
360 void MSTractionSubstation::addOverheadWireClampToCircuit(const std::string id, MSOverheadWire* startSegment, MSOverheadWire* endSegment) {
361  PositionVector pos_start = startSegment->getLane().getShape();
362  PositionVector pos_end = endSegment->getLane().getShape();
363  double distance = pos_start[0].distanceTo2D(pos_end.back());
364 
365  if (distance > 10) {
366  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.")
367  }
368  getCircuit()->addElement(id, distance * WIRE_RESISTIVITY, startSegment->getCircuitStartNodePos(), endSegment->getCircuitEndNodePos(), Element::ElementType::RESISTOR_traction_wire);
369 }
370 
371 void
373  //myOverheadWireSegments.push_back(static_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT)));
374  myOverheadWireSegments.erase(std::remove(myOverheadWireSegments.begin(), myOverheadWireSegments.end(), oldSegment), myOverheadWireSegments.end());
375 }
376 
377 void
379  std::lock_guard<std::mutex> guard(ow_mutex);
380  setChargingVehicle(true);
381  myChargingVehicles.push_back(&veh);
383 }
384 
385 void
387  std::lock_guard<std::mutex> guard(ow_mutex);
388  myChargingVehicles.erase(std::remove(myChargingVehicles.begin(), myChargingVehicles.end(), &veh), myChargingVehicles.end());
389  if (myChargingVehicles.size() == 0) {
390  setChargingVehicle(false);
391  }
392  //sort(myChargingVehicles.begin(), myChargingVehicles.end(), vehicle_position_sorter());
393 }
394 
395 void
397  ow_mutex.lock();
398 }
399 
400 void
402  ow_mutex.unlock();
403 }
404 
405 void
407  myElecHybrid.push_back(elecHybrid);
408 }
409 
410 void
412  myElecHybrid.erase(std::remove(myElecHybrid.begin(), myElecHybrid.end(), veh), myElecHybrid.end());
413 }
414 
415 void
417  std::cout << "substation " << getID() << " constrols segments: \n";
418  for (std::vector<MSOverheadWire*>::iterator it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); ++it) {
419  std::cout << " " << (*it)->getOverheadWireSegmentName() << "\n";
420  }
421 }
422 
423 
425  return toString(getID());
426 }
427 
429 }
430 
431 Circuit*
433  if (getTractionSubstation() != nullptr) {
434  return getTractionSubstation()->getCircuit();
435  }
436  return nullptr;
437 }
438 
439 double
441  return myVoltage;
442 }
443 
444 void
445 MSOverheadWire::setVoltage(double voltage) {
446  if (voltage < 0) {
447  WRITE_WARNING("New " + toString(SUMO_ATTR_VOLTAGE) + " for " + toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " isn't valid (" + toString(voltage) + ").")
448  } else {
449  myVoltage = voltage;
450  }
451 }
452 
453 void
455  myChargingVehicle = value;
456 }
457 
458 
459 void
461  myChargingVehicle = value;
462 }
463 
464 bool
465 MSOverheadWire::vehicleIsInside(const double position) const {
466  if ((position >= getBeginLanePosition()) && (position <= getEndLanePosition())) {
467  return true;
468  } else {
469  return false;
470  }
471 }
472 
473 
474 bool
476  return myChargingVehicle;
477 }
478 
479 bool
481  return myChargingVehicle;
482 }
483 
484 void
487 }
488 
489 void
492 }
493 
495  myForbiddenLanes.push_back(lane);
496 }
497 
499  for (std::vector<MSLane*>::iterator it = myForbiddenLanes.begin(); it != myForbiddenLanes.end(); ++it) {
500  if (lane == (*it)) {
501  return true;
502  }
503  }
504  return false;
505 }
506 
507 void
508 MSTractionSubstation::addClamp(const std::string& id, MSOverheadWire* startPos, MSOverheadWire* endPos) {
509  OverheadWireClamp clamp(id, startPos, endPos, false);
510  myOverheadWireClamps.push_back(clamp);
511 }
512 
514 MSTractionSubstation::findClamp(std::string clampId) {
515  for (auto it = myOverheadWireClamps.begin(); it != myOverheadWireClamps.end(); it++) {
516  if (it->id == clampId) {
517  return &(*it);
518  }
519  }
520  return nullptr;
521 }
522 
523 bool
525  if (myOverheadWireSegments.size() > 0 || myForbiddenLanes.size() > 0 || getCircuit()->getLastId() > 0) {
526  return true;
527  }
528  return false;
529 }
530 
531 void
533  if (!myChargingVehicle) {
534  // myCommandForSolvingCircuit = new StaticCommand<MSTractionSubstation>(&MSTractionSubstation::solveCircuit);
537  setChargingVehicle(true);
538  }
539 }
540 
541 SUMOTime
543  /*Circuit evaluation*/
544  setChargingVehicle(false);
545 
546 #ifdef HAVE_EIGEN
547  myCircuit->solve();
548 
549  // RICE_TODO: This is a relict of original code without alpha scaling, shall we still keep it here?
550  vector<Element*>* elecHybridSources = myCircuit->getCurrentSources();
551  double err = 0.0;
552  for (auto* it : *elecHybridSources) {
553  if (!ISNAN(it->getPowerWanted())) {
554  err = MAX2(abs(myCircuit->alphaBest * (it->getPowerWanted() - (-it->getCurrent()) * it->getVoltage())), err);
555  }
556  }
557 
558  // if (err > tolerance || veh_elem->getCurrent()*veh_elem->getVoltage() == NAN) {
559  // WRITE_WARNING("The tolerance of circuit evaluation was not reached until 10 iteration (err = " + toString(err) + "). The tolerance " + toString(tolerance) + " was not reached in iter = " + toString(15));
560  //}
561 
562  if (getCircuit()->alphaBest != 1.0) {
563  WRITE_WARNING("The requested total power could not be delivered by the overhead wire. Only " + toString(getCircuit()->alphaBest) + " of originally requested power was provided.");
564  }
565 #endif
566 
567  for (auto* it : myElecHybrid) {
568 
569  Element* vehElem = it->getVehElem();
570  double voltage = vehElem->getVoltage();
571  double current = -vehElem->getCurrent(); // Vehicle is a power source, hence its current flows in opposite direction
572 
573  it->setCurrentFromOverheadWire(current);
574  it->setVoltageOfOverheadWire(voltage);
575 
576  // Calulate energy charged
577  //
578  // RICE_TODO: This does not take into account the fact that `energyIn` may be lower than the energy requested
579  // due to `alpha` scaling ...
580  //
581  // myEnergyCharged = (TS * voltage * -current * myActOverheadWireSegment->getEfficency()) - (myConsum * 3600);
582  // double energyCharged = (TS * vehElem->getPowerWanted() / 3600) - (it->getConsum());
583  //
584  double energyIn = WATT2WATTHR(voltage * current); // [Wh]
585  double energyCharged = energyIn - it->getConsum(); // [Wh]
586 
587  // Convert from [Ws] to [Wh] (3600s / 1h):
588  it->setEnergyCharged(energyCharged);
589 
590  // Update Battery charge
591  it->setActualBatteryCapacity(it->getActualBatteryCapacity() + energyCharged);
592  // add charge value for output to myActOverheadWireSegment
593  it->getActOverheadWireSegment()->addChargeValueForOutput(energyCharged + it->getConsum(), it);
594  }
595 
596  return 0;
597 }
598 
599 void
600 MSOverheadWire::addChargeValueForOutput(double WCharged, MSDevice_ElecHybrid* elecHybrid, bool ischarging) {
601  std::string status = "charging";
602  if (!ischarging) {
603  status = "not-charging";
604  }
605 
606  // update total charge
607  myTotalCharge += WCharged;
608  // create charge row and insert it in myChargeValues
609  charge C(MSNet::getInstance()->getCurrentTimeStep(), elecHybrid->getHolder().getID(), elecHybrid->getHolder().getVehicleType().getID(),
610  status, WCharged, elecHybrid->getActualBatteryCapacity(), elecHybrid->getMaximumBatteryCapacity(),
611  elecHybrid->getVoltageOfOverheadWire(), myTotalCharge);
612  myChargeValues.push_back(C);
613 }
614 
615 
616 void
619  output.writeAttr(SUMO_ATTR_ID, myID);
620  if (getTractionSubstation() != nullptr) {
622  } else {
624  }
627  // start writting
628  if (myChargeValues.size() > 0) {
629  // First calculate charge for every vehicle
630  std::vector<double> charge;
631  std::vector<std::tuple<SUMOTime, SUMOTime, std::string> > vectorBeginEndCharge;
632  SUMOTime firsTimeStep = myChargeValues.at(0).timeStep;
633  // set first value
634  charge.push_back(0);
635  vectorBeginEndCharge.push_back(std::tuple<SUMOTime, SUMOTime, std::string>(firsTimeStep, 0, myChargeValues.at(0).vehicleID));
636  // iterate over charging values
637  for (std::vector<MSOverheadWire::charge>::const_iterator i = myChargeValues.begin(); i != myChargeValues.end(); i++) {
638  // update chargue
639  charge.back() += i->WCharged;
640  // update end time
641  std::get<1>(vectorBeginEndCharge.back()) = i->timeStep;
642  // update timestep of charge
643  firsTimeStep += 1000;
644  // check if charge is continuous. If not, open a new vehicle tag
645  if (((i + 1) != myChargeValues.end()) && (((i + 1)->timeStep) != firsTimeStep)) {
646  // set new firsTimeStep of charge
647  firsTimeStep = (i + 1)->timeStep;
648  charge.push_back(0);
649  vectorBeginEndCharge.push_back(std::tuple<SUMOTime, SUMOTime, std::string>(firsTimeStep, 0, (i + 1)->vehicleID));
650  }
651  }
652  // now write values
653  firsTimeStep = myChargeValues.at(0).timeStep;
654  int vehicleCounter = 0;
655  // open tag for first vehicle and write id and type of vehicle
656  output.openTag(SUMO_TAG_VEHICLE);
657  output.writeAttr(SUMO_ATTR_ID, myChargeValues.at(0).vehicleID);
658  output.writeAttr(SUMO_ATTR_TYPE, myChargeValues.at(0).vehicleType);
660  output.writeAttr(SUMO_ATTR_CHARGINGBEGIN, time2string(std::get<0>(vectorBeginEndCharge.at(0))));
661  output.writeAttr(SUMO_ATTR_CHARGINGEND, time2string(std::get<1>(vectorBeginEndCharge.at(0))));
662  output.writeAttr(SUMO_ATTR_MAXIMUMBATTERYCAPACITY, myChargeValues.at(0).maxBatteryCapacity);
663  // iterate over charging values
664  for (std::vector<MSOverheadWire::charge>::const_iterator i = myChargeValues.begin(); i != myChargeValues.end(); i++) {
665  // open tag for timestep and write all parameters
666  output.openTag(SUMO_TAG_STEP);
667  output.writeAttr(SUMO_ATTR_TIME, time2string(i->timeStep));
668  // charge values
669  output.writeAttr(SUMO_ATTR_CHARGING_STATUS, i->status);
670  output.writeAttr(SUMO_ATTR_ENERGYCHARGED, i->WCharged);
671  output.writeAttr(SUMO_ATTR_PARTIALCHARGE, i->totalEnergyCharged);
672  // charging values of charging station in this timestep
673  output.writeAttr(SUMO_ATTR_VOLTAGE, i->voltage);
674  // battery status of vehicle
675  output.writeAttr(SUMO_ATTR_ACTUALBATTERYCAPACITY, i->actualBatteryCapacity);
676  // close tag timestep
677  output.closeTag();
678  // update timestep of charge
679  firsTimeStep += 1000;
680  // check if charge is continuous. If not, open a new vehicle tag
681  if (((i + 1) != myChargeValues.end()) && (((i + 1)->timeStep) != firsTimeStep)) {
682  // set new firsTimeStep of charge
683  firsTimeStep = (i + 1)->timeStep;
684  // update counter
685  vehicleCounter++;
686  // close previous vehicle tag
687  output.closeTag();
688  // open tag for new vehicle and write id and type of vehicle
689  output.openTag(SUMO_TAG_VEHICLE);
690  output.writeAttr(SUMO_ATTR_ID, (i + 1)->vehicleID);
691  output.writeAttr(SUMO_ATTR_TYPE, (i + 1)->vehicleType);
692  output.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED_VEHICLE, charge.at(vehicleCounter));
693  output.writeAttr(SUMO_ATTR_CHARGINGBEGIN, std::get<0>(vectorBeginEndCharge.at(vehicleCounter)));
694  output.writeAttr(SUMO_ATTR_CHARGINGEND, std::get<1>(vectorBeginEndCharge.at(vehicleCounter)));
695  }
696  }
697  // close vehicle tag
698  output.closeTag();
699  }
700  // close charging station tag
701  output.closeTag();
702 }
703 
704 
705 /****************************************************************************/
std::mutex ow_mutex
#define WATT2WATTHR(_x)
const double WIRE_RESISTIVITY
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:276
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
long long int SUMOTime
Definition: SUMOTime.h:31
@ 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 provied by charging station at certain timestep
@ 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_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_TIME
trigger: the time of the step
@ SUMO_ATTR_CHARGING_STATUS
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:29
T ISNAN(T a)
Definition: StdDefs.h:114
T MAX2(T a, T b)
Definition: StdDefs.h:79
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:44
vector< Element * > * getCurrentSources()
Definition: Circuit.cpp:154
Element * addElement(string name, double value, Node *pNode, Node *nNode, Element::ElementType et)
Definition: Circuit.cpp:659
void eraseNode(Node *node)
Definition: Circuit.cpp:62
void replaceAndDeleteNode(Node *unusedNode, Node *newNode)
Definition: Circuit.cpp:701
Node * getNode(string name)
Definition: Circuit.cpp:98
double alphaBest
Best alpha scaling value.
Definition: Circuit.h:69
Node * addNode(string name)
Definition: Circuit.cpp:41
void eraseElement(Element *element)
Definition: Circuit.cpp:693
Base (microsim) event class.
Definition: Command.h:49
Node * getNegNode()
Definition: Element.cpp:109
double getCurrent()
Definition: Element.cpp:79
Node * getPosNode()
Definition: Element.cpp:106
double getVoltage()
Definition: Element.cpp:70
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:103
Representation of a lane in the micro simulation.
Definition: MSLane.h:82
double getLength() const
Returns the lane's length.
Definition: MSLane.h:539
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:2140
const std::vector< std::pair< const MSLane *, const MSEdge * > > getOutgoingViaLanes() const
get the list of outgoing lanes
Definition: MSLane.cpp:2644
bool isInternal() const
Definition: MSLane.cpp:2036
const PositionVector & getShape() const
Returns this lane's shape.
Definition: MSLane.h:476
std::vector< const MSLane * > getNormalIncomingLanes() const
get the list of all direct (disregarding internal predecessors) non-internal predecessor lanes of thi...
Definition: MSLane.cpp:2654
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:171
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:1107
MSEventControl * getEndOfTimestepEvents()
Returns the event control for events executed at the end of a time step.
Definition: MSNet.h:474
MSStoppingPlace * getStoppingPlace(const std::string &id, const SumoXMLTag category) const
Returns the named stopping place of the given category.
Definition: MSNet.cpp:1098
A class for sorting vehicle on lane under the overhead wire segment.
Definition of overhead wire segment.
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)
Node * getCircuitEndNodePos() const
void writeOverheadWireSegmentOutput(OutputDevice &output)
write charging station values
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.
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< charge > myChargeValues
vector with the charges of this charging station
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.
Node * getCircuitStartNodePos() const
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)
Circuit * getCircuit() const
void addSolvingCirucitToEndOfTimestepEvents()
bool isCharging() const
Return true if in the current time step the substation (overhead wire section) is charging a vehicle.
std::vector< MSDevice_ElecHybrid * > myElecHybrid
bool isForbidden(const MSLane *lane)
std::vector< MSLane * > myForbiddenLanes
void addVehicle(MSDevice_ElecHybrid *elecHybrid)
SUMOTime solveCircuit(SUMOTime currentTime)
void addOverheadWireSegmentToCircuit(MSOverheadWire *newOverheadWireSegment)
OverheadWireClamp * findClamp(std::string id)
MSTractionSubstation(const std::string &substationId, double voltage)
constructor
static Command * myCommandForSolvingCircuit
void addForbiddenLane(MSLane *lane)
void setChargingVehicle(bool value)
enable or disable charging vehicle
SUMOVehicle & getHolder() const
Returns the vehicle that holds this device.
const std::string & getID() const
Returns the name of the vehicle type.
Definition: MSVehicleType.h:90
Base class for objects which have an id.
Definition: Named.h:53
std::string myID
The name of the object.
Definition: Named.h:124
const std::string & getID() const
Returns the id.
Definition: Named.h:73
Definition: Node.h:31
vector< Element * > * getElements()
Definition: Node.cpp:99
void eraseElement(Element *element)
Definition: Node.cpp:46
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:60
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:239
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:58
A wrapper for a Command function.
struct to save information for the overhead wire segment output