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-2022 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 
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 
47 #include "MSOverheadWire.h"
48 #include "MSTrigger.h"
49 
50 
52 static std::mutex ow_mutex;
53 
54 // ===========================================================================
55 // MSOverheadWire
56 // ===========================================================================
57 
58 MSOverheadWire::MSOverheadWire(const std::string& overheadWireSegmentID, MSLane& lane, double startPos, double endPos, bool voltageSource) :
59  MSStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT, std::vector<std::string>(), lane, startPos, endPos),
60  myVoltage(0),
61  myChargingVehicle(false),
62  myTotalCharge(0),
63  myChargingVehicles({}),
64  // RICE_TODO: think about some better structure storing circuit pointers below
65  myTractionSubstation(nullptr),
66  myVoltageSource(voltageSource),
67  myCircuitElementPos(nullptr),
68  myCircuitStartNodePos(nullptr),
69 myCircuitEndNodePos(nullptr) {
70  if (getBeginLanePosition() > getEndLanePosition()) {
71  WRITE_WARNING(toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " doesn't have a valid range (" + toString(getBeginLanePosition()) + " < " + toString(getEndLanePosition()) + ").");
72  }
73 }
74 
76  if (myTractionSubstation != nullptr) {
80  delete myCircuitElementPos;
81  if (myCircuitEndNodePos->getElements()->size() == 0) {
83  delete myCircuitEndNodePos;
84  }
85  if (myCircuitStartNodePos->getElements()->size() == 0) {
87  delete myCircuitStartNodePos;
88  }
89  }
90 
93  //RICE_TODO We should "delete myTractionSubstation;" here ...
94  } else {
96  }
97  }
98 }
99 
100 
101 void
103  std::lock_guard<std::mutex> guard(ow_mutex);
104  setChargingVehicle(true);
105  myChargingVehicles.push_back(&veh);
107 }
108 
109 void
111  std::lock_guard<std::mutex> guard(ow_mutex);
112  myChargingVehicles.erase(std::remove(myChargingVehicles.begin(), myChargingVehicles.end(), &veh), myChargingVehicles.end());
113  if (myChargingVehicles.size() == 0) {
114  setChargingVehicle(false);
115  }
116  //sort(myChargingVehicles.begin(), myChargingVehicles.end(), vehicle_position_sorter());
117 }
118 
119 void
121  ow_mutex.lock();
122 }
123 
124 void
126  ow_mutex.unlock();
127 }
128 
129 void
131  myElecHybrid.push_back(elecHybrid);
132 }
133 
134 void
136  myElecHybrid.erase(std::remove(myElecHybrid.begin(), myElecHybrid.end(), veh), myElecHybrid.end());
137 }
138 
139 void
141  std::cout << "substation " << getID() << " constrols segments: \n";
142  for (std::vector<MSOverheadWire*>::iterator it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); ++it) {
143  std::cout << " " << (*it)->getOverheadWireSegmentName() << "\n";
144  }
145 }
146 
147 
149  return toString(getID());
150 }
151 
153 }
154 
155 Circuit*
157  if (getTractionSubstation() != nullptr) {
158  return getTractionSubstation()->getCircuit();
159  }
160  return nullptr;
161 }
162 
163 double
165  return myVoltage;
166 }
167 
168 void
169 MSOverheadWire::setVoltage(double voltage) {
170  if (voltage < 0) {
171  WRITE_WARNING("New " + toString(SUMO_ATTR_VOLTAGE) + " for " + toString(SUMO_TAG_OVERHEAD_WIRE_SEGMENT) + " with ID = " + getID() + " isn't valid (" + toString(voltage) + ").")
172  } else {
173  myVoltage = voltage;
174  }
175 }
176 
177 void
179  myChargingVehicle = value;
180 }
181 
182 
183 void
185  myChargingVehicle = value;
186 }
187 
188 bool
189 MSOverheadWire::vehicleIsInside(const double position) const {
190  if ((position >= getBeginLanePosition()) && (position <= getEndLanePosition())) {
191  return true;
192  } else {
193  return false;
194  }
195 }
196 
197 
198 bool
200  return myChargingVehicle;
201 }
202 
203 
204 void
205 MSOverheadWire::addChargeValueForOutput(double WCharged, MSDevice_ElecHybrid* elecHybrid, bool ischarging) {
206  std::string status = "charging";
207  if (!ischarging) {
208  status = "not-charging";
209  }
210 
211  // update total charge
212  myTotalCharge += WCharged;
213  // create charge row and insert it in myChargeValues
214  const std::string vehID = elecHybrid->getHolder().getID();
215  if (myChargeValues.count(vehID) == 0) {
216  myChargedVehicles.push_back(vehID);
217  }
218  Charge C(MSNet::getInstance()->getCurrentTimeStep(), elecHybrid->getHolder().getID(), elecHybrid->getHolder().getVehicleType().getID(),
219  status, WCharged, elecHybrid->getActualBatteryCapacity(), elecHybrid->getMaximumBatteryCapacity(),
220  elecHybrid->getVoltageOfOverheadWire(), myTotalCharge);
221  myChargeValues[vehID].push_back(C);
222 }
223 
224 
225 void
227  int chargingSteps = 0;
228  std::vector<SUMOTime> chargingSteps_list;
229  for (const auto& item : myChargeValues) {
230  for (auto it : item.second) {
231  if (std::find(chargingSteps_list.begin(), chargingSteps_list.end(), it.timeStep) == chargingSteps_list.end()) {
232  chargingSteps_list.push_back(it.timeStep);
233  }
234  }
235  }
236  chargingSteps = (int) chargingSteps_list.size();
238  output.writeAttr(SUMO_ATTR_ID, myID);
239  if (getTractionSubstation() != nullptr) {
241  } else {
243  }
245 
246  // RICE_TODO QUESTION myChargeValues.size() vs. chargingSteps
247  // myChargeValues.size() is the number of vehicles charging sometimes from this overheadwire segment during simulation
248  // chargingSteps is now the sum of chargingSteps of each vehicle, but takes also into account that at the given
249  // step more than one vehicle may be charged from this segment
250  output.writeAttr(SUMO_ATTR_CHARGINGSTEPS, chargingSteps);
251  // output.writeAttr(SUMO_ATTR_EDGE, getLane().getEdge());
252  output.writeAttr(SUMO_ATTR_LANE, getLane().getID());
253 
254  // Start writing
255  if (myChargeValues.size() > 0) {
256  for (const std::string& vehID : myChargedVehicles) {
257  int iStart = 0;
258  const auto& chargeSteps = myChargeValues[vehID];
259  while (iStart < (int)chargeSteps.size()) {
260  int iEnd = iStart + 1;
261  double charged = chargeSteps[iStart].WCharged;
262  while (iEnd < (int)chargeSteps.size() && chargeSteps[iEnd].timeStep == chargeSteps[iEnd - 1].timeStep + DELTA_T) {
263  charged += chargeSteps[iEnd].WCharged;
264  iEnd++;
265  }
266  writeVehicle(output, chargeSteps, iStart, iEnd, charged);
267  iStart = iEnd;
268  }
269  }
270  }
271  // close charging station tag
272  output.closeTag();
273 }
274 
275 
276 void
277 MSOverheadWire::writeVehicle(OutputDevice& out, const std::vector<Charge>& chargeSteps, int iStart, int iEnd, double charged) {
278  const Charge& first = chargeSteps[iStart];
280  out.writeAttr(SUMO_ATTR_ID, first.vehicleID);
284  out.writeAttr(SUMO_ATTR_CHARGINGEND, time2string(chargeSteps[iEnd - 1].timeStep));
286  for (int i = iStart; i < iEnd; i++) {
287  const Charge& c = chargeSteps[i];
288  out.openTag(SUMO_TAG_STEP);
290  // charge values
294  // charging values of charging station in this timestep
296  // battery status of vehicle
298  // close tag timestep
299  out.closeTag();
300  }
301  out.closeTag();
302 }
303 
304 
305 // ===========================================================================
306 // MSTractionSubstation
307 // ===========================================================================
308 // RICE_TODO Split MSTractionSubstation and MSOverheadWire?
309 // Probably no as the traction substation cannot stand alone and is always
310 // used together with the overhead wire. It is a bit disorganised, though.
311 
312 MSTractionSubstation::MSTractionSubstation(const std::string& substationId, double voltage, double currentLimit) :
313  Named(substationId),
314  myChargingVehicle(false),
315  myElecHybridCount(0),
316  mySubstationVoltage(voltage),
317  myCircuit(new Circuit(currentLimit)),
318  myTotalEnergy(0)
319 {}
320 
321 
322 
323 void
325  MSLane& lane = const_cast<MSLane&>(newOverheadWireSegment->getLane());
326  if (lane.isInternal()) {
327  return;
328  }
329 
330  // RICE_TODO: consider the possibility of having more segments that belong to one lane.
331 
332  myOverheadWireSegments.push_back(newOverheadWireSegment);
333  newOverheadWireSegment->setTractionSubstation(this);
334 
336 #ifdef HAVE_EIGEN
337  Circuit* circuit = newOverheadWireSegment->getCircuit();
338  const std::string segmentID = newOverheadWireSegment->getID();
339 
340  if (circuit->getNode("negNode_ground") == nullptr) {
341  circuit->addNode("negNode_ground");
342  }
343 
344  // convention: pNode is at the beginning of the wire segment, nNode is at the end of the wire segment
345  newOverheadWireSegment->setCircuitStartNodePos(circuit->addNode("pNode_pos_" + segmentID));
346  newOverheadWireSegment->setCircuitEndNodePos(circuit->addNode("nNode_pos_" + segmentID));
347  // RICE_TODO: to use startPos and endPos of ovhdsegment: set the length of wire here properly
348  newOverheadWireSegment->setCircuitElementPos(
349  circuit->addElement("pos_" + segmentID,
350  (newOverheadWireSegment->getLane().getLength()) * WIRE_RESISTIVITY,
351  newOverheadWireSegment->getCircuitStartNodePos(),
352  newOverheadWireSegment->getCircuitEndNodePos(),
353  Element::ElementType::RESISTOR_traction_wire));
354 #else
355  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
356 #endif
357  }
358 
359  const MSLane* connection = nullptr;
360  std::string ovrhdSegmentID = ""; //ID of outgoing or incoming overhead wire segment
361  MSOverheadWire* ovrhdSegment = nullptr; //pointer to outgoing or incoming overhead wire segment
362 
363  // RICE_TODO: simplify the code, two similar code-blocks below
364  // RICE_TODO: to use startPos and endPos of ovhdsegment: if endPos+EPS > newOverheadWireSegment->getLane().getLength(),
365  // and the outgoing lanes will be skipped as there is no wire at the end of the lane
366 
367  /* in version before SUMO 1.0.1 the function getOutgoingLanes() returning MSLane* exists,
368  in new version of SUMO the funciton getOutgoingViaLanes() returning MSLane* and MSEdge* pair exists */
369  // std::vector<const MSLane*> outgoing = lane.getOutgoingLanes();
370  const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane.getOutgoingViaLanes();
371  std::vector<const MSLane*> neigboringInnerLanes;
372  neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
373  for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
374  neigboringInnerLanes.push_back(outgoingLanesAndEdges[it].first);
375  }
376 
377  // Check if there is an overhead wire segment on the outgoing lane. If not, do nothing, otherwise find connnecting internal lanes and
378  // add all lanes (this and inner) to circuit
379  for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
380  ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
381  // If the overhead wire segment is over the outgoing (not internal) lane
382  if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
383  ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
384  // If the outgoing overhead wire segment belongs to the same substation as newOverheadWireSegment
385  // 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)
386  if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
387  connection = lane.getInternalFollowingLane(*it);
388  if (connection != nullptr) {
389  //is connection a forbidden lane?
390  if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
391  ovrhdSegment->getTractionSubstation()->isForbidden(lane.getInternalFollowingLane(connection)) ||
392  ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(*it)))) {
393  addOverheadWireInnerSegmentToCircuit(newOverheadWireSegment, ovrhdSegment, connection, lane.getInternalFollowingLane(connection), connection->getInternalFollowingLane(*it));
394  }
395 
396  } else {
398 #ifdef HAVE_EIGEN
399  Node* const unusedNode = newOverheadWireSegment->getCircuitEndNodePos();
400  for (MSOverheadWire* const ows : myOverheadWireSegments) {
401  if (ows->getCircuitStartNodePos() == unusedNode) {
402  ows->setCircuitStartNodePos(ovrhdSegment->getCircuitStartNodePos());
403  }
404  if (ows->getCircuitEndNodePos() == unusedNode) {
405  ows->setCircuitEndNodePos(ovrhdSegment->getCircuitStartNodePos());
406  }
407  }
408  newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitStartNodePos());
409 #else
410  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
411 #endif
412  }
413  }
414  }
415  }
416  }
417 
418  // RICE_TODO: to use startPos and endPos of ovhdsegment: if startPos-EPS < 0,
419  // and the incoming lanes will be skipped as there is no wire at the beginning of the lane
420 
421  // This is the same as above, only this time checking the wires on some incoming lanes. If some of them
422  // has an overhead wire segment, find the connnecting internal lanes and add all lanes (the internal
423  // and this) to the circuit, otherwise do nothing.
424  neigboringInnerLanes = lane.getNormalIncomingLanes();
425  for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
426  ovrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
427  // If the overhead wire segment is over the incoming (not internal) lane
428  if (ovrhdSegmentID != "" && !(*it)->isInternal()) {
429  ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(ovrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
430  // If the incoming overhead wire segment belongs to the same substation as newOverheadWireSegment
431  // 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)
432  if (ovrhdSegment->getTractionSubstation() == newOverheadWireSegment->getTractionSubstation()) {
433  connection = (*it)->getInternalFollowingLane(&lane);
434  if (connection != nullptr) {
435  //is connection a forbidden lane?
436  if (!(ovrhdSegment->getTractionSubstation()->isForbidden(connection) ||
437  ovrhdSegment->getTractionSubstation()->isForbidden((*it)->getInternalFollowingLane(connection)) ||
438  ovrhdSegment->getTractionSubstation()->isForbidden(connection->getInternalFollowingLane(&lane)))) {
439  addOverheadWireInnerSegmentToCircuit(ovrhdSegment, newOverheadWireSegment, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(&lane));
440  }
441  } else {
443 #ifdef HAVE_EIGEN
444  Node* const unusedNode = newOverheadWireSegment->getCircuitStartNodePos();
445  for (MSOverheadWire* const ows : myOverheadWireSegments) {
446  if (ows->getCircuitStartNodePos() == unusedNode) {
447  ows->setCircuitStartNodePos(ovrhdSegment->getCircuitEndNodePos());
448  }
449  if (ows->getCircuitEndNodePos() == unusedNode) {
450  ows->setCircuitEndNodePos(ovrhdSegment->getCircuitEndNodePos());
451  }
452  }
453  newOverheadWireSegment->getCircuit()->replaceAndDeleteNode(unusedNode, ovrhdSegment->getCircuitEndNodePos());
454 #else
455  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
456 #endif
457  }
458  }
459  }
460  }
461  }
462 
463  if (MSGlobals::gOverheadWireSolver && newOverheadWireSegment->isThereVoltageSource()) {
464 #ifdef HAVE_EIGEN
465  newOverheadWireSegment->getCircuit()->addElement(
466  "voltage_source_" + newOverheadWireSegment->getID(),
468  newOverheadWireSegment->getCircuitStartNodePos(),
469  newOverheadWireSegment->getCircuit()->getNode("negNode_ground"),
470  Element::ElementType::VOLTAGE_SOURCE_traction_wire);
471 #else
472  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
473 #endif
474  }
475 }
476 
477 
478 void
479 MSTractionSubstation::addOverheadWireInnerSegmentToCircuit(MSOverheadWire* incomingSegment, MSOverheadWire* outgoingSegment, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
480  if (frontConnection == nullptr && behindConnection == nullptr) {
481  // addOverheadWire from nNode of newOverheadWireSegment to pNode
482  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
483  myOverheadWireSegments.push_back(innerSegment);
484  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
486 #ifdef HAVE_EIGEN
487  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
488  innerSegment->setCircuitElementPos(elem);
489  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
490  innerSegment->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
491 #else
492  UNUSED_PARAMETER(outgoingSegment);
493  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
494 #endif
495  }
496  } else if (frontConnection != nullptr && behindConnection == nullptr) {
497  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
498  MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
499 
500  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
501  myOverheadWireSegments.push_back(innerSegment);
502  innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
503  myOverheadWireSegments.push_back(innerSegment2);
504 
506 #ifdef HAVE_EIGEN
507  Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
508  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
509  Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
510 
511  innerSegment->setCircuitElementPos(elem);
512  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
513  innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
514 
515  innerSegment2->setCircuitElementPos(elem2);
516  innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
517  innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
518 #else
519  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
520 #endif
521  }
522  } else if (frontConnection == nullptr && behindConnection != nullptr) {
523  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
524  MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
525 
526  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
527  myOverheadWireSegments.push_back(innerSegment);
528  innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
529  myOverheadWireSegments.push_back(innerSegment2);
530 
532 #ifdef HAVE_EIGEN
533  Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
534  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
535  Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
536 
537  innerSegment->setCircuitElementPos(elem);
538  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
539  innerSegment->setCircuitEndNodePos(betweenBehindNode_pos);
540 
541  innerSegment2->setCircuitElementPos(elem2);
542  innerSegment2->setCircuitStartNodePos(betweenBehindNode_pos);
543  innerSegment2->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
544 #else
545  WRITE_WARNING("Overhead circuit solver requested, but solver support (Eigen) not compiled in.");
546 #endif
547  }
548  } else if (frontConnection != nullptr && behindConnection != nullptr) {
549  MSOverheadWire* innerSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + frontConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
550  MSOverheadWire* innerSegment2 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + connection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
551  MSOverheadWire* innerSegment3 = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace("ovrhd_inner_" + behindConnection->getID(), SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
552 
553  innerSegment->setTractionSubstation(incomingSegment->getTractionSubstation());
554  myOverheadWireSegments.push_back(innerSegment);
555  innerSegment2->setTractionSubstation(incomingSegment->getTractionSubstation());
556  myOverheadWireSegments.push_back(innerSegment2);
557  innerSegment3->setTractionSubstation(incomingSegment->getTractionSubstation());
558  myOverheadWireSegments.push_back(innerSegment3);
559 
561 #ifdef HAVE_EIGEN
562  Node* betweenFrontNode_pos = incomingSegment->getCircuit()->addNode("betweenFrontNode_pos_" + connection->getID());
563  Node* betweenBehindNode_pos = incomingSegment->getCircuit()->addNode("betweenBehindNode_pos_" + connection->getID());
564  Element* elem = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + frontConnection->getID(), (frontConnection->getLength()) * WIRE_RESISTIVITY, incomingSegment->getCircuitEndNodePos(), betweenFrontNode_pos, Element::ElementType::RESISTOR_traction_wire);
565  Element* elem2 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + connection->getID(), (connection->getLength()) * WIRE_RESISTIVITY, betweenFrontNode_pos, betweenBehindNode_pos, Element::ElementType::RESISTOR_traction_wire);
566  Element* elem3 = incomingSegment->getCircuit()->addElement("pos_ovrhd_inner_" + behindConnection->getID(), (behindConnection->getLength()) * WIRE_RESISTIVITY, betweenBehindNode_pos, outgoingSegment->getCircuitStartNodePos(), Element::ElementType::RESISTOR_traction_wire);
567 
568  innerSegment->setCircuitElementPos(elem);
569  innerSegment->setCircuitStartNodePos(incomingSegment->getCircuitEndNodePos());
570  innerSegment->setCircuitEndNodePos(betweenFrontNode_pos);
571 
572  innerSegment2->setCircuitElementPos(elem2);
573  innerSegment2->setCircuitStartNodePos(betweenFrontNode_pos);
574  innerSegment2->setCircuitEndNodePos(betweenBehindNode_pos);
575 
576  innerSegment3->setCircuitElementPos(elem3);
577  innerSegment3->setCircuitStartNodePos(betweenBehindNode_pos);
578  innerSegment3->setCircuitEndNodePos(outgoingSegment->getCircuitStartNodePos());
579 #else
580  WRITE_WARNING("Overhead circuit solver requested, but solver support not compiled in.");
581 #endif
582  }
583  }
584 }
585 
586 
587 void MSTractionSubstation::addOverheadWireClampToCircuit(const std::string id, MSOverheadWire* startSegment, MSOverheadWire* endSegment) {
588  PositionVector pos_start = startSegment->getLane().getShape();
589  PositionVector pos_end = endSegment->getLane().getShape();
590  double distance = pos_start[0].distanceTo2D(pos_end.back());
591 
592  if (distance > 10) {
593  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.")
594  }
595  getCircuit()->addElement(id, distance * WIRE_RESISTIVITY, startSegment->getCircuitStartNodePos(), endSegment->getCircuitEndNodePos(), Element::ElementType::RESISTOR_traction_wire);
596 }
597 
598 
599 void
601  //myOverheadWireSegments.push_back(static_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overheadWireSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT)));
602  myOverheadWireSegments.erase(std::remove(myOverheadWireSegments.begin(), myOverheadWireSegments.end(), oldSegment), myOverheadWireSegments.end());
603 }
604 
605 
606 bool
608  return myChargingVehicle;
609 }
610 
611 
612 void
615 }
616 
617 
618 void
621 }
622 
623 
625  myForbiddenLanes.push_back(lane);
626 }
627 
628 
630  for (std::vector<MSLane*>::iterator it = myForbiddenLanes.begin(); it != myForbiddenLanes.end(); ++it) {
631  if (lane == (*it)) {
632  return true;
633  }
634  }
635  return false;
636 }
637 
638 
639 void
640 MSTractionSubstation::addClamp(const std::string& id, MSOverheadWire* startPos, MSOverheadWire* endPos) {
641  OverheadWireClamp clamp(id, startPos, endPos, false);
642  myOverheadWireClamps.push_back(clamp);
643 }
644 
645 
647 MSTractionSubstation::findClamp(std::string clampId) {
648  for (auto it = myOverheadWireClamps.begin(); it != myOverheadWireClamps.end(); it++) {
649  if (it->id == clampId) {
650  return &(*it);
651  }
652  }
653  return nullptr;
654 }
655 
656 
657 bool
659  if (myOverheadWireSegments.size() > 0 || myForbiddenLanes.size() > 0 || getCircuit()->getLastId() > 0) {
660  return true;
661  }
662  return false;
663 }
664 
665 
666 void
668  if (!myChargingVehicle) {
669  // myCommandForSolvingCircuit = new StaticCommand<MSTractionSubstation>(&MSTractionSubstation::solveCircuit);
672  setChargingVehicle(true);
673  }
674 }
675 
676 
677 SUMOTime
679  /*Circuit evaluation*/
680  setChargingVehicle(false);
681 
682 #ifdef HAVE_EIGEN
683 
684  // RICE_TODO: Allow for updating current limits in each time step if changed e.g. via traci or similar
685  // getCircuit()->setCurrentLimit(myCurrentLimit);
686 
687  // Solve the electrical circuit
688  myCircuit->solve();
689 
690  if (myCircuit->getAlphaBest() != 1.0) {
691  WRITE_WARNING("The requested total power could not be delivered by the overhead wire. Only " + toString(myCircuit->getAlphaBest()) + " of originally requested power was provided.");
692  }
693 #endif
694 
695  // RICE_TODO: verify what happens if eigen is not defined?
696  // Note: addSolvingCirucitToEndOfTimestepEvents() and thus solveCircuit() should be called from notifyMove only if eigen is defined.
698 
699  for (auto* it : myElecHybrid) {
700 
701  Element* vehElem = it->getVehElem();
702  double voltage = vehElem->getVoltage();
703  double current = -vehElem->getCurrent(); // Vehicle is a power source, hence its current (returned by getCurrent()) flows in opposite direction
704 
705  it->setCurrentFromOverheadWire(current);
706  it->setVoltageOfOverheadWire(voltage);
707 
708  // Calulate energy charged
709  double energyIn = WATT2WATTHR(voltage * current); // [Wh]
710 
711  // Compute energy charged into/from battery considering recuperation and propulsion efficiency (not considering battery capacity)
712  double energyCharged = it->computeChargedEnergy(energyIn);
713 
714  // Update energy saved in the battery pack and return trully charged energy considering limits of battery
715  double realEnergyCharged = it->storeEnergyToBattery(energyCharged);
716 
717  it->setEnergyCharged(realEnergyCharged);
718 
719  // Add energy wasted to the total sum
720  it->updateTotalEnergyWasted(energyCharged - realEnergyCharged);
721  // Add the energy provided by the overhead wire segment to the output of the segment
722  it->getActOverheadWireSegment()->addChargeValueForOutput(energyIn, it);
723  }
724 
725  return 0;
726 }
727 
728 void
729 MSTractionSubstation::addChargeValueForOutput(double energy, double current, double alpha, Circuit::alphaFlag alphaReason) {
730  std::string status = "";
731 
732  myTotalEnergy += energy; //[Wh]
733 
734  std::string vehicleIDs = "";
735  for (std::vector<MSDevice_ElecHybrid*>::iterator it = myElecHybrid.begin(); it != myElecHybrid.end(); it++) {
736  vehicleIDs += (*it)->getID() + " ";
737  }
738  //vehicleIDs.erase(vehicleIDs.end());
739  // TODO vehicleIDs should not be empty, but in some case, it is (due to teleporting of vehicle?)
740  if (!vehicleIDs.empty()) {
741  vehicleIDs.pop_back();
742  }
743 
744  std::string currents = "";
745  currents = myCircuit->getCurrentsOfCircuitSource(currents);
746 
747  // create charge row and insert it in myChargeValues
748  chargeTS C(MSNet::getInstance()->getCurrentTimeStep(), getID(), vehicleIDs, energy, current, currents, mySubstationVoltage, status,
749  (int)myElecHybrid.size(), (int)getCircuit()->getNumVoltageSources(), alpha, alphaReason);
750  myChargeValues.push_back(C);
751 }
752 
753 void
756  output.writeAttr(SUMO_ATTR_ID, myID);
758  double length = 0;
759  for (auto it = myOverheadWireSegments.begin(); it != myOverheadWireSegments.end(); it++) {
760  length += (*it)->getEndLanePosition() - (*it)->getBeginLanePosition();
761  }
762  output.writeAttr(SUMO_ATTR_LENGTH, length);
763  output.writeAttr("numVoltageSources", myCircuit->getNumVoltageSources());
764  output.writeAttr("numClamps", myOverheadWireClamps.size());
766 
767  // start writting
768  if (myChargeValues.size() > 0) {
769  // iterate over charging values
770  for (std::vector<MSTractionSubstation::chargeTS>::const_iterator i = myChargeValues.begin(); i != myChargeValues.end(); i++) {
771  // open tag for timestep and write all parameters
772  output.openTag(SUMO_TAG_STEP);
773  output.writeAttr(SUMO_ATTR_TIME, time2string(i->timeStep));
774  // charge values
775  output.writeAttr("vehicleIDs", i->vehicleIDs);
776  output.writeAttr("numVehicles", i->numVehicles);
777  // same number of numVoltageSources for all time, parameter is written in the superordinate tag
778  //output.writeAttr("numVoltageSources", i->numVoltageSources);
779  // charging status is always ""
780  //output.writeAttr(SUMO_ATTR_CHARGING_STATUS, i->status);
781  output.writeAttr(SUMO_ATTR_ENERGYCHARGED, i->energy);
782  output.writeAttr(SUMO_ATTR_CURRENTFROMOVERHEADWIRE, i->current);
783  output.writeAttr("currents", i->currentsString);
784  // charging values of charging station in this timestep
785  output.writeAttr(SUMO_ATTR_VOLTAGE, i->voltage);
786  output.writeAttr(SUMO_ATTR_ALPHACIRCUITSOLVER, i->alpha);
787  output.writeAttr("alphaFlag", i->alphaReason);
788  // close tag timestep
789  output.closeTag();
790  // update timestep of charge
791  }
792  }
793  // close charging station tag
794  output.closeTag();
795 }
796 
797 /****************************************************************************/
static std::mutex ow_mutex
#define WATT2WATTHR(_x)
const double WIRE_RESISTIVITY
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:280
SUMOTime DELTA_T
Definition: SUMOTime.cpp:37
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
long long int SUMOTime
Definition: SUMOTime.h:32
@ 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 provied 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:126
Node * addNode(std::string name)
Definition: Circuit.cpp:55
int getNumVoltageSources()
Definition: Circuit.cpp:995
Element * addElement(std::string name, double value, Node *pNode, Node *nNode, Element::ElementType et)
Definition: Circuit.cpp:803
void eraseNode(Node *node)
Definition: Circuit.cpp:75
double getTotalCurrentOfCircuitSources()
The sum of voltage source currents in the circuit.
Definition: Circuit.cpp:169
void replaceAndDeleteNode(Node *unusedNode, Node *newNode)
Definition: Circuit.cpp:855
double getTotalPowerOfCircuitSources()
The sum of voltage source powers in the circuit.
Definition: Circuit.cpp:161
alphaFlag
Flag of alpha scaling parameter.
Definition: Circuit.h:95
alphaFlag getAlphaReason()
return the reason why alpha scaling value has been used
Definition: Circuit.h:131
Node * getNode(std::string name)
Definition: Circuit.cpp:111
std::string & getCurrentsOfCircuitSource(std::string &currents)
List of currents of voltage sources as a string.
Definition: Circuit.cpp:178
void eraseElement(Element *element)
Definition: Circuit.cpp:847
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
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:109
Representation of a lane in the micro simulation.
Definition: MSLane.h:82
double getLength() const
Returns the lane's length.
Definition: MSLane.h:541
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:2226
const std::vector< std::pair< const MSLane *, const MSEdge * > > getOutgoingViaLanes() const
get the list of outgoing lanes
Definition: MSLane.cpp:2739
bool isInternal() const
Definition: MSLane.cpp:2122
const PositionVector & getShape() const
Returns this lane's shape.
Definition: MSLane.h:478
std::vector< const MSLane * > getNormalIncomingLanes() const
get the list of all direct (disregarding internal predecessors) non-internal predecessor lanes of thi...
Definition: MSLane.cpp:2749
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:174
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:1259
MSEventControl * getEndOfTimestepEvents()
Returns the event control for events executed at the end of a time step.
Definition: MSNet.h:479
MSStoppingPlace * getStoppingPlace(const std::string &id, const SumoXMLTag category) const
Returns the named stopping place of the given category.
Definition: MSNet.cpp:1250
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)
Node * getCircuitEndNodePos() const
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
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< 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.
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.
void writeTractionSubstationOutput(OutputDevice &output)
write traction substation values
std::vector< MSDevice_ElecHybrid * > myElecHybrid
bool isForbidden(const MSLane *lane)
std::vector< MSLane * > myForbiddenLanes
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.
Definition: MSVehicleType.h:90
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:100
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
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:248
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:60
A wrapper for a Command function.
struct to save information for the overhead wire segment output
std::string status
status
struct to save information for the traction substation output