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.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 
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 
49 
51 static std::mutex ow_mutex;
52 
53 // ===========================================================================
54 // MSOverheadWire
55 // ===========================================================================
56 
57 MSOverheadWire::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),
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  delete myCircuitElementPos;
80  if (myCircuitEndNodePos->getElements()->size() == 0) {
82  delete myCircuitEndNodePos;
83  }
84  if (myCircuitStartNodePos->getElements()->size() == 0) {
86  delete myCircuitStartNodePos;
87  }
88  }
89 
92  //RICE_TODO We should "delete myTractionSubstation;" here ...
93  } else {
95  }
96  }
97 }
98 
99 
100 void
102  std::lock_guard<std::mutex> guard(ow_mutex);
103  setChargingVehicle(true);
104  myChargingVehicles.push_back(&veh);
106 }
107 
108 void
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 
118 void
120  ow_mutex.lock();
121 }
122 
123 void
125  ow_mutex.unlock();
126 }
127 
128 void
130  myElecHybrid.push_back(elecHybrid);
131 }
132 
133 void
135  myElecHybrid.erase(std::remove(myElecHybrid.begin(), myElecHybrid.end(), veh), myElecHybrid.end());
136 }
137 
138 void
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 
148  return toString(getID());
149 }
150 
152 }
153 
154 Circuit*
156  if (getTractionSubstation() != nullptr) {
157  return getTractionSubstation()->getCircuit();
158  }
159  return nullptr;
160 }
161 
162 double
164  return myVoltage;
165 }
166 
167 void
168 MSOverheadWire::setVoltage(double voltage) {
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 
176 void
178  myChargingVehicle = value;
179 }
180 
181 
182 void
184  myChargingVehicle = value;
185 }
186 
187 bool
188 MSOverheadWire::vehicleIsInside(const double position) const {
189  if ((position >= getBeginLanePosition()) && (position <= getEndLanePosition())) {
190  return true;
191  } else {
192  return false;
193  }
194 }
195 
196 
197 bool
199  return myChargingVehicle;
200 }
201 
202 
203 void
204 MSOverheadWire::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(),
219  elecHybrid->getVoltageOfOverheadWire(), myTotalCharge);
220  myChargeValues[vehID].push_back(C);
221 }
222 
223 
224 void
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());
251  output.writeAttr(SUMO_ATTR_LANE, getLane().getID());
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 
275 void
276 MSOverheadWire::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];
287  out.openTag(SUMO_TAG_STEP);
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 
311 MSTractionSubstation::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 
322 void
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(),
352  Element::ElementType::RESISTOR_traction_wire));
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"),
469  Element::ElementType::VOLTAGE_SOURCE_traction_wire);
470 #else
471  WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
472 #endif
473  }
474 }
475 
476 
477 void
478 MSTractionSubstation::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 
586 void 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  }
594  getCircuit()->addElement(id, distance * WIRE_RESISTIVITY, startSegment->getCircuitStartNodePos(), endSegment->getCircuitEndNodePos(), Element::ElementType::RESISTOR_traction_wire);
595 }
596 
597 
598 void
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 
605 bool
607  return myChargingVehicle;
608 }
609 
610 
611 void
614 }
615 
616 
617 void
620 }
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 
638 void
639 MSTractionSubstation::addClamp(const std::string& id, MSOverheadWire* startPos, MSOverheadWire* endPos) {
640  OverheadWireClamp clamp(id, startPos, endPos, false);
641  myOverheadWireClamps.push_back(clamp);
642 }
643 
644 
646 MSTractionSubstation::findClamp(std::string clampId) {
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 
656 bool
658  if (myOverheadWireSegments.size() > 0 || myForbiddenLanes.size() > 0 || getCircuit()->getLastId() > 0) {
659  return true;
660  }
661  return false;
662 }
663 
664 
665 void
667  if (!myChargingVehicle) {
668  // myCommandForSolvingCircuit = new StaticCommand<MSTractionSubstation>(&MSTractionSubstation::solveCircuit);
671  setChargingVehicle(true);
672  }
673 }
674 
675 
676 SUMOTime
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: addSolvingCirucitToEndOfTimestepEvents() 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 
727 void
728 MSTractionSubstation::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 
752 void
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);
781  output.writeAttr(SUMO_ATTR_CURRENTFROMOVERHEADWIRE, i->current);
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:35
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 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: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
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:118
Representation of a lane in the micro simulation.
Definition: MSLane.h:84
double getLength() const
Returns the lane's length.
Definition: MSLane.h:598
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:2651
const std::vector< std::pair< const MSLane *, const MSEdge * > > getOutgoingViaLanes() const
get the list of outgoing lanes
Definition: MSLane.cpp:3207
bool isInternal() const
Definition: MSLane.cpp:2516
std::vector< const MSLane * > getNormalIncomingLanes() const
get the list of all direct (disregarding internal predecessors) non-internal predecessor lanes of thi...
Definition: MSLane.cpp:3217
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:182
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:1373
MSEventControl * getEndOfTimestepEvents()
Returns the event control for events executed at the end of a time step.
Definition: MSNet.h:481
MSStoppingPlace * getStoppingPlace(const std::string &id, const SumoXMLTag category) const
Returns the named stopping place of the given category.
Definition: MSNet.cpp:1364
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:91
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.
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:254
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.
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