From 65432913daec8c212581ff43723a5c1dfe13e8a6 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 21 Jun 2022 18:06:42 +0200 Subject: [PATCH 01/17] Applying a lot of code improvements to DBFS algo & surrounding classes --- CHANGELOG.md | 1 + input/samples/vn_simona/vn_simona.conf | 32 ++-- .../ie3/simona/agent/grid/DBFSAlgorithm.scala | 160 ++++++++---------- .../ie3/simona/agent/grid/GridAgentData.scala | 149 +++++++++------- .../agent/grid/GridResultsSupport.scala | 55 +++++- .../simona/agent/grid/PowerFlowSupport.scala | 43 +++-- .../agent/grid/ReceivedValuesStore.scala | 36 ++-- .../model/participant/SystemParticipant.scala | 4 +- 8 files changed, 277 insertions(+), 203 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f372b56d17..bf10b92971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Made SimonaConfig.BaseRuntimeConfig serializable [#36](https://github.com/ie3-institute/simona/issues/36) - Adapt to new simonaAPI snapshot [#95](https://github.com/ie3-institute/simona/issues/95) - Update Sphinx to 4.5.0 as well as extensions [#214](https://github.com/ie3-institute/simona/issues/214) +- Improved code quality in and around DBFS algorithm [#265](https://github.com/ie3-institute/simona/issues/265) ### Fixed - Location of `vn_simona` test grid (was partially in Berlin and Dortmund) [#72](https://github.com/ie3-institute/simona/issues/72) diff --git a/input/samples/vn_simona/vn_simona.conf b/input/samples/vn_simona/vn_simona.conf index 2c7f52f91a..666aeaf75c 100644 --- a/input/samples/vn_simona/vn_simona.conf +++ b/input/samples/vn_simona/vn_simona.conf @@ -67,21 +67,23 @@ simona.output.participant.defaultConfig = { powerRequestReply = false simulationResult = true } -simona.output.participant.individualConfigs = [{ - notifier = "pv" - powerRequestReply = false - simulationResult = true -}] -simona.output.participant.individualConfigs = [{ - notifier = "wec" - powerRequestReply = false - simulationResult = true -}] -simona.output.participant.individualConfigs = [{ - notifier = "evcs" - powerRequestReply = false - simulationResult = true -}] +simona.output.participant.individualConfigs = [ + { + notifier = "pv" + powerRequestReply = false + simulationResult = true + }, + { + notifier = "wec" + powerRequestReply = false + simulationResult = true + }, + { + notifier = "evcs" + powerRequestReply = false + simulationResult = true + } +] ################################################################## # Runtime Configuration // todo refactor as this naming is misleading and partly unneeded diff --git a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala index 750cb51e44..6db2ed592c 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala @@ -45,13 +45,11 @@ import edu.ie3.simona.ontology.messages.VoltageMessage.{ import edu.ie3.simona.ontology.trigger.Trigger._ import edu.ie3.simona.util.TickUtil._ import edu.ie3.util.quantities.PowerSystemUnits._ -import edu.ie3.util.quantities.{PowerSystemUnits, QuantityUtil} import tech.units.indriya.quantity.Quantities import java.time.{Duration, ZonedDateTime} import java.util.UUID -import javax.measure.Quantity -import javax.measure.quantity.{Dimensionless, ElectricPotential} +import javax.measure.quantity.ElectricPotential import scala.concurrent.{ExecutionContext, Future} /** Trait that is normally mixed into every [[GridAgent]] to enable distributed @@ -119,6 +117,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { // we just received either all provided slack voltage values or all provided power values val updatedGridAgentBaseData: GridAgentBaseData = receivedValues match { case receivedPowers: ReceivedPowerValues => + /* Can be a message from an asset or a message from an inferior grid */ gridAgentBaseData.updateWithReceivedPowerValues(receivedPowers) case receivedSlacks: ReceivedSlackValues => gridAgentBaseData.updateWithReceivedSlackVoltages(receivedSlacks) @@ -154,7 +153,6 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { allValuesReceived, updatedGridAgentBaseData ) - } // if we receive a request for slack voltages from our inferior grids we want to answer it @@ -171,10 +169,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { // we either have voltages ready calculated (not the first sweep) or we don't have them here // -> return calculated value or target voltage as physical value - val (slackE, slackF): ( - Quantity[ElectricPotential], - Quantity[ElectricPotential] - ) = + val (slackE, slackF) = (gridAgentBaseData.sweepValueStores.get(currentSweepNo) match { case Some(result) => Some(result, currentSweepNo) @@ -226,7 +221,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { uuid == nodeUuid && isSlack } .map(_.vTarget) - .getOrElse(Quantities.getQuantity(1d, PowerSystemUnits.PU)) + .getOrElse(Quantities.getQuantity(1d, PU)) val vSlack = vTarget .multiply( gridAgentBaseData.gridEnv.gridModel.mainRefSystem.nominalVoltage @@ -368,12 +363,11 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { gridAgentBaseData: GridAgentBaseData ) => // inform my child grids about the end of this grid simulation - gridAgentBaseData.inferiorGridGates.foreach(inferiorGridGate => { - gridAgentBaseData.gridEnv - .subnetGateToActorRef(inferiorGridGate) ! FinishGridSimulationTrigger( - currentTick - ) - }) + gridAgentBaseData.inferiorGridGates + .map { inferiorGridGate => + gridAgentBaseData.gridEnv.subnetGateToActorRef(inferiorGridGate) + } + .foreach(_ ! FinishGridSimulationTrigger(currentTick)) // inform every system participant about the end of this grid simulation gridAgentBaseData.gridEnv.nodeToAssetAgents.foreach { case (_, actors) => @@ -1005,7 +999,8 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { * a map contains a mapping between nodes and the [[ActorRef]] s located @ * those nodes * @param refSystem - * the reference system of the [[GridModel]] of this [[GridAgent]] + * the reference system of the [[edu.ie3.simona.model.grid.GridModel]] of + * this [[GridAgent]] * @param askTimeout * a timeout for the request * @return @@ -1028,53 +1023,46 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { Some( Future .sequence( - nodeToAssetAgents - .flatten(nodeUuidWithActorRefs => { - val assetActorRefs = nodeUuidWithActorRefs._2 - val nodeUuid = nodeUuidWithActorRefs._1 - assetActorRefs.map(assetAgent => { - - val (eInPu, fInPU): ( - Quantity[Dimensionless], - Quantity[Dimensionless] - ) = - sweepValueStore match { - case Some(sweepValueStore) => - val assetNodeVoltageInSi = refSystem.vInSi( - sweepValueStore.sweepData - .find(_.nodeUuid == nodeUuid) - .getOrElse( - throw new DBFSAlgorithmException( - s"Provided Sweep value store contains no data for node with id $nodeUuid" - ) + nodeToAssetAgents.flatten { case (nodeUuid, assetActorRefs) => + assetActorRefs.map(assetAgent => { + + val (eInPu, fInPU) = + sweepValueStore match { + case Some(sweepValueStore) => + val (pInSi, qInSi) = refSystem.vInSi( + sweepValueStore.sweepData + .find(_.nodeUuid == nodeUuid) + .getOrElse( + throw new DBFSAlgorithmException( + s"Provided Sweep value store contains no data for node with id $nodeUuid" ) - .stateData - .voltage - ) - ( - refSystem.vInPu(assetNodeVoltageInSi._1), - refSystem.vInPu(assetNodeVoltageInSi._2) - ) - case None => - ( - Quantities.getQuantity(1, PU), - Quantities.getQuantity(0, PU) - ) - } - - (assetAgent ? RequestAssetPowerMessage( - currentTick, - QuantityUtil.asComparable(eInPu), - QuantityUtil.asComparable(fInPU) - )).map { - case providedPowerValuesMessage: AssetPowerChangedMessage => - (assetAgent, Some(providedPowerValuesMessage)) - case assetPowerUnchangedMessage: AssetPowerUnchangedMessage => - (assetAgent, Some(assetPowerUnchangedMessage)) - }.mapTo[ActorPowerRequestResponse] - }) + ) + .stateData + .voltage + ) + ( + refSystem.vInPu(pInSi), + refSystem.vInPu(qInSi) + ) + case None => + ( + Quantities.getQuantity(1, PU), + Quantities.getQuantity(0, PU) + ) + } + + (assetAgent ? RequestAssetPowerMessage( + currentTick, + eInPu, + fInPU + )).map { + case providedPowerValuesMessage: AssetPowerChangedMessage => + (assetAgent, Some(providedPowerValuesMessage)) + case assetPowerUnchangedMessage: AssetPowerUnchangedMessage => + (assetAgent, Some(assetPowerUnchangedMessage)) + }.mapTo[ActorPowerRequestResponse] }) - .toVector + }.toVector ) .map(ReceivedAssetPowerValues) .pipeTo(self) @@ -1108,29 +1096,30 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { s"asking inferior grids for power values: {}", inferiorGridGates ) - if (inferiorGridGates.nonEmpty) - Some( - Future - .sequence( - inferiorGridGates.map(inferiorGridGate => { - val inferiorGridAgent = - subGridGateToActorRef(inferiorGridGate) - (inferiorGridAgent ? RequestGridPowerMessage( + Option.when(inferiorGridGates.nonEmpty) { + Future + .sequence( + inferiorGridGates + .map { subGridGate => + subGridGateToActorRef( + subGridGate + ) -> subGridGate.getSuperiorNode.getUuid + } + .map { case (inferiorGridAgentRef, inferiorGridGateNodes) => + (inferiorGridAgentRef ? RequestGridPowerMessage( currentSweepNo, - inferiorGridGate.getSuperiorNode.getUuid + inferiorGridGateNodes )).map { case provideGridPowerMessage: ProvideGridPowerMessage => - (inferiorGridAgent, Option(provideGridPowerMessage)) + (inferiorGridAgentRef, Some(provideGridPowerMessage)) case FailedPowerFlow => - (inferiorGridAgent, Some(FailedPowerFlow)) - }.mapTo[ActorPowerRequestResponse] - }) - ) - .map(ReceivedGridPowerValues) - .pipeTo(self) - ) - else - None + (inferiorGridAgentRef, Some(FailedPowerFlow)) + } + } + ) + .map(ReceivedGridPowerValues) + .pipeTo(self) + } } /** Triggers an execution of the akka `ask` pattern for all slack voltages of @@ -1179,11 +1168,12 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { None } - /** Create an instance of [[PowerFlowResults]] and send it to all listener - * Note: in the future this one could become a bottleneck for power flow - * calculation timesteps. For performance improvements one might consider - * putting this into a future with pipeTo. One has to consider how to deal - * with unfinished futures in shutdown phase then + /** Create an instance of + * [[edu.ie3.simona.event.ResultEvent.PowerFlowResultEvent]] and send it to + * all listener Note: in the future this one could become a bottleneck for + * power flow calculation timesteps. For performance improvements one might + * consider putting this into a future with pipeTo. One has to consider how + * to deal with unfinished futures in shutdown phase then * * @param gridAgentBaseData * the grid agent base data diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala index 89064b0318..3c548716d6 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala @@ -19,9 +19,9 @@ import edu.ie3.simona.agent.grid.ReceivedValues.{ ReceivedSlackValues } import edu.ie3.simona.model.grid.{GridModel, RefSystem} -import edu.ie3.simona.ontology.messages.PowerMessage import edu.ie3.simona.ontology.messages.PowerMessage.{ FailedPowerFlow, + PowerResponseMessage, ProvidePowerMessage } @@ -183,7 +183,9 @@ object GridAgentData { // we expect power values from inferior grids and assets val assetAndGridPowerValuesReady = receivedValueStore.nodeToReceivedPower.values.forall(vector => - vector.forall(actorRefOption => actorRefOption._2.isDefined) + vector.forall { case (_, powerResponseOpt) => + powerResponseOpt.isDefined + } ) // we expect slack voltages only from our superior grids (if any) val slackVoltageValuesReady = @@ -219,58 +221,19 @@ object GridAgentData { receivedValueStore.nodeToReceivedPower ) { case ( - updatedNodeToReceivedPowerValuesMap, - (senderRef, powerValueMessageOpt) + nodeToReceivedPowerValuesMapWithAddedPowerResponse, + (senderRef, Some(powerResponseMessage)) ) => - // extract the nodeUuid that corresponds to the sender's actorRef and check if we expect a message from the sender - val nodeUuid = powerValueMessageOpt match { - case Some( - powerValuesMessage: ProvidePowerMessage - ) => // can either be an AssetPowerChangedMessage or an AssetPowerUnchangedMessage - uuid(updatedNodeToReceivedPowerValuesMap, senderRef, replace) - .getOrElse( - throw new RuntimeException( - s"$actorName Received asset power values msg $powerValuesMessage " + - s"from $senderRef which is not in my power values nodes map or which cannot be replaced!" - ) - ) - ._1 - case Some(FailedPowerFlow) => - uuid(updatedNodeToReceivedPowerValuesMap, senderRef, replace) - .getOrElse( - throw new RuntimeException( - s"$actorName Received failed power flow message " + - s"from $senderRef which is not in my power values nodes map or which cannot be replaced!" - ) - ) - ._1 - case None => - throw new RuntimeException( - s"Received a 'None' as provided asset power from '$senderRef'. Provision of 'None' is not supported." + - s"Either a changed power or an unchanged power message is expected!" - ) - case unknownMsg => - throw new RuntimeException( - s"$actorName Unknown message received. Can't process message $unknownMsg." - ) - } - - // update the values in the received map - val receivedVector = updatedNodeToReceivedPowerValuesMap - .getOrElse( - nodeUuid, - throw new RuntimeException( - s"NodeId $nodeUuid is not part of nodeToReceivedPowerValuesMap!" - ) - ) - .filterNot { case (k, v) => - k == senderRef & - (if (!replace) v.isEmpty else v.isDefined) - } :+ (senderRef, powerValueMessageOpt) - - updatedNodeToReceivedPowerValuesMap - .updated(nodeUuid, receivedVector) - + updateNodalReceivedVector( + powerResponseMessage, + nodeToReceivedPowerValuesMapWithAddedPowerResponse, + senderRef, + replace + ) + case (_, (senderRef, None)) => + throw new RuntimeException( + s"Received a 'None' as provided power from '$senderRef'. Provision of 'None' is not supported." + ) } this.copy( receivedValueStore = receivedValueStore @@ -296,19 +259,83 @@ object GridAgentData { nodeToReceivedPower: Map[UUID, Vector[ActorPowerRequestResponse]], senderRef: ActorRef, replace: Boolean - ): Option[ - (UUID, Vector[(ActorRef, Option[PowerMessage.PowerResponseMessage])]) - ] = { + ): Option[UUID] = nodeToReceivedPower - .find( - _._2.exists(actorRefPowerValueOpt => - actorRefPowerValueOpt._1 == senderRef && + .find { case (_, receivedPowerMessages) => + receivedPowerMessages.exists { case (ref, maybePowerResponse) => + ref == senderRef && (if (!replace) - actorRefPowerValueOpt._2.isEmpty + maybePowerResponse.isEmpty else - actorRefPowerValueOpt._2.isDefined) + maybePowerResponse.isDefined) + } + } + .map { case (uuid, _) => uuid } + + /** Identify and update the vector of already received information. + * + * @param powerResponse + * Optional power response message + * @param nodeToReceived + * Mapping from node uuid to received values + * @param senderRef + * Reference of current sender + * @param replace + * If existing values may be replaced or not + * @return + * The nodal uuid as well as the updated collection of received + * information + */ + private def updateNodalReceivedVector( + powerResponse: PowerResponseMessage, + nodeToReceived: Map[UUID, Vector[ + (ActorRef, Option[PowerResponseMessage]) + ]], + senderRef: ActorRef, + replace: Boolean + ): Map[UUID, Vector[(ActorRef, Option[PowerResponseMessage])]] = { + // extract the nodeUuid that corresponds to the sender's actorRef and check if we expect a message from the sender + val nodeUuid = powerResponse match { + case powerValuesMessage: ProvidePowerMessage => + uuid(nodeToReceived, senderRef, replace) + .getOrElse( + throw new RuntimeException( + s"$actorName Received asset power values msg $powerValuesMessage " + + s"from $senderRef which is not in my power values nodes map or which cannot be replaced!" + ) + ) + case FailedPowerFlow => + uuid(nodeToReceived, senderRef, replace) + .getOrElse( + throw new RuntimeException( + s"$actorName Received failed power flow message " + + s"from $senderRef which is not in my power values nodes map or which cannot be replaced!" + ) + ) + case unknownMsg => + throw new RuntimeException( + s"$actorName Unknown message received. Can't process message $unknownMsg." + ) + } + + // update the values in the received map + val receivedVector = nodeToReceived + .getOrElse( + nodeUuid, + throw new RuntimeException( + s"NodeId $nodeUuid is not part of nodeToReceivedPowerValuesMap!" ) ) + /* Filter out the entry, that belongs to the given sender and is not defined, yet, in case no replacement shall + * be made. If a replacement is desired, filter out the already provided data. */ + .filterNot { case (k, v) => + k == senderRef & + (if (!replace) v.isEmpty else v.isDefined) + } :+ (senderRef, Some(powerResponse)) + + /* Actually update the map and hand it back */ + nodeToReceived + .updated(nodeUuid, receivedVector) } /** Update this [[GridAgentBaseData]] with [[ReceivedSlackValues]] and diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala index a4784dd0d1..1bf271592a 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala @@ -520,12 +520,12 @@ private[grid] trait GridResultsSupport { (iMag, iAng) } - /** Calculate the voltage magnitude and the voltage angle in physical units + /** Calculate the current magnitude and the current angle in physical units * based on a provided electric current in p.u. and the nominal referenced * electric current. The arctangent "only" calculates the angle between the * complex current and it's real part. This means, that i = (i_real, i_imag) * and i' = (-i_real, -i_imag) will lead to the same angle. However, for - * power system simulation, the absolute orientation in the complex plain + * power system simulation, the absolute orientation in the complex plane * with regard to the positive real axis is of interest. Therefore, * additional 180° are added, if the real part of the current is negative. * @@ -542,15 +542,52 @@ private[grid] trait GridResultsSupport { ): (ComparableQuantity[ElectricCurrent], ComparableQuantity[Angle]) = ( iNominal.multiply(iPu.abs).asComparable, - Quantities.getQuantity( - atan(iPu.imag / iPu.real).toDegrees + max( - 0.0, - -signum(iPu.real) - ) * 180.0, - PowerSystemUnits.DEGREE_GEOM - ) + angle(iPu) ) + /** Correct the offset of an angle dependent on the direction. If the + * direction is negative, 180° are added + */ + private val offSetCorrection + : (ComparableQuantity[Angle], Double) => ComparableQuantity[Angle] = + (angle: ComparableQuantity[Angle], dir: Double) => + if (dir < 0) { + angle.add(Quantities.getQuantity(180d, PowerSystemUnits.DEGREE_GEOM)) + } else { + angle + } + + /** Calculate the angle of the complex value given. The angle has the full + * orientation on the complex plane. + * + * @param cplx + * The complex value + * @return + * The angle of the complex value + */ + private def angle(cplx: Complex): ComparableQuantity[Angle] = cplx match { + case _ if cplx.abs == 0 => + /* The complex value has no magnitude, therefore define the angle to zero */ + Quantities.getQuantity(0d, PowerSystemUnits.DEGREE_GEOM) + case Complex(real, imag) => + /* Calculate the angle between real and imaginary part */ + val baseAngle = atan(imag / real).toDegrees + + if (baseAngle.isNaN) { + /* There is only an imaginary part */ + offSetCorrection( + Quantities.getQuantity(90d, PowerSystemUnits.DEGREE_GEOM), + imag + ) + } else { + /* Correct the angle for full orientation in the complex plane */ + offSetCorrection( + Quantities.getQuantity(baseAngle, PowerSystemUnits.DEGREE_GEOM), + real + ) + } + } + /** Calculates the electric current of a two-port element @ port i (=A) and j * (=B) based on the provided voltages @ each port and the corresponding * admittances. All values in p.u. diff --git a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala index ebf017b22d..60f0eddc73 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala @@ -32,7 +32,8 @@ import javax.measure.Quantity import javax.measure.quantity.{Dimensionless, ElectricPotential} import tech.units.indriya.quantity.Quantities -import scala.annotation.tailrec +import scala.collection.mutable +import scala.util.{Failure, Success, Try} /** Support and helper methods for power flow calculations provided by * [[edu.ie3.powerflow]] @@ -98,7 +99,7 @@ trait PowerFlowSupport { .get(nodeModel.uuid) match { case Some(actorRefsWithPower) => val (p, q) = actorRefsWithPower - .map(_._2) + .map { case (_, powerMsg) => powerMsg } .collect { case Some(providePowerMessage: ProvidePowerMessage) => providePowerMessage @@ -116,9 +117,9 @@ trait PowerFlowSupport { Quantities.getQuantity(0, mainRefSystemPowerUnit), Quantities.getQuantity(0, mainRefSystemPowerUnit) ) - )((pqSum, powerMessage) => { - (pqSum._1.add(powerMessage.p), pqSum._2.add(powerMessage.q)) - }) + ) { case ((pSum, qSum), powerMessage) => + (pSum.add(powerMessage.p), qSum.add(powerMessage.q)) + } new Complex( gridMainRefSystem.pInPu(p).getValue.doubleValue(), @@ -200,7 +201,7 @@ trait PowerFlowSupport { val nodeStateData = sweepValueStoreData.stateData val targetVoltage = if (nodeStateData.nodeType == NodeType.SL) { val receivedSlackVoltage = receivedSlackValues.values - .flatMap(_._2) + .flatMap { case (_, slackVoltageMsg) => slackVoltageMsg } .find(_.nodeUuid == sweepValueStoreData.nodeUuid) .getOrElse( throw new RuntimeException( @@ -245,15 +246,16 @@ trait PowerFlowSupport { validResult: ValidNewtonRaphsonPFResult, gridModel: GridModel ): String = { - val debugString = new StringBuilder("Power flow result: ") + val debugString = new mutable.StringBuilder("Power flow result: ") validResult.nodeData.foreach(nodeStateData => { - // get idx - val idx = nodeStateData.index + // get node index + val nodeIndex = nodeStateData.index // get nodeUUID val uuid = gridModel.nodeUuidToIndexMap - .find(_._2 == idx) + .find { case (_, index) => index == nodeIndex } + .map { case (uuid, _) => uuid } .getOrElse(throw new RuntimeException("NODE NOT FOUND REMOVE THIS ")) - ._1 + // get nodeId from UUID val nodeId = gridModel.gridComponents.nodes .find(_.uuid == uuid) @@ -417,7 +419,6 @@ trait PowerFlowSupport { * @return * The result of newton raphson power flow calculation */ - @tailrec protected final def newtonRaphsonPF( gridModel: GridModel, maxIterations: Int, @@ -445,10 +446,13 @@ trait PowerFlowSupport { // / execute val powerFlow = NewtonRaphsonPF(epsilon, maxIterations, admittanceMatrix) - powerFlow.calculate( - operatingPoint, - Some(forcedSlackNodeVoltage) - ) match { + + Try { + powerFlow.calculate( + operatingPoint, + Some(forcedSlackNodeVoltage) + ) + }.map { case _: PowerFlowResult.FailedPowerFlowResult if epsilons.size > 1 => // if we can relax, we relax val epsilonsLeft = epsilons.drop(1) @@ -462,6 +466,13 @@ trait PowerFlowSupport { ) case result => result + } match { + case Success(result) => result + case Failure(exception) => + throw new DBFSAlgorithmException( + s"Power flow calculation in subnet ${gridModel.subnetNo} failed.", + exception + ) } case None => throw new DBFSAlgorithmException( diff --git a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala index e3d9c74bb9..fd848f5cf7 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala @@ -97,22 +97,28 @@ case object ReceivedValuesStore { (uuid, actorRefs.toVector.map(actorRef => actorRef -> None)) } - /* Add everything, that I expect from my sub ordinate grid agents */ - inferiorSubGridGateToActorRef.foldLeft(assetsToReceivedPower) { - case (subOrdinateToReceivedPower, (gate, inferiorSubGridRef)) => - val couplingNodeUuid = gate.getSuperiorNode.getUuid + /* Add everything, that I expect from my sub ordinate grid agents. Build distinct pairs of sending actor reference + * and target node */ + inferiorSubGridGateToActorRef + .map { case (gate, reference) => + reference -> gate.getSuperiorNode.getUuid + } + .foldLeft(assetsToReceivedPower) { + case ( + subOrdinateToReceivedPower, + (inferiorSubGridRef, couplingNodeUuid) + ) => + /* Check, if there is already something expected for the given coupling node and add reference to the subordinate + * grid agent */ + val actorRefToMessage = subOrdinateToReceivedPower + .getOrElse( + couplingNodeUuid, + Vector.empty[(ActorRef, Option[ProvidePowerMessage])] + ) :+ (inferiorSubGridRef -> None) - /* Check, if there is yet something expected for the given coupling node and add reference to the subordinate - * grid agent */ - val actorRefToMessage = subOrdinateToReceivedPower - .getOrElse( - couplingNodeUuid, - Vector.empty[(ActorRef, Option[ProvidePowerMessage])] - ) :+ (inferiorSubGridRef -> None) - - /* Update the existing map */ - subOrdinateToReceivedPower + (couplingNodeUuid -> actorRefToMessage) - } + /* Update the existing map */ + subOrdinateToReceivedPower + (couplingNodeUuid -> actorRefToMessage) + } } /** Composes an empty [[NodeToReceivedSlackVoltage]] with all options diff --git a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala index 3331b2280d..a3fa10be44 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala @@ -154,10 +154,10 @@ abstract class SystemParticipant[CD <: CalcRelevantData]( KILOVOLTAMPERE ) - if (apparentPower.isGreaterThan(sMax)) { + if (apparentPower.isGreaterThan(sMax.multiply(1.05))) { logger.warn( s"The var characteristics \'$qControl\' of model \'$id\' ($uuid) imposes an apparent " + - s"power (= $apparentPower) that exceeds " + + s"power (= $apparentPower) that exceeds 105 % of " + s"rated apparent power specifications (= $sMax). " + s"Therefore, setting reactive power output to the to the upper limit " + s"in correspondence to the existing active power $activePower." From b58e452aed089c53415a23420a4c6e25bd44e491 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 21 Jun 2022 21:28:09 +0200 Subject: [PATCH 02/17] Fixing creation of empty NodeToReceivedPower map --- .../scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala index fd848f5cf7..3a698ba8ca 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala @@ -99,7 +99,7 @@ case object ReceivedValuesStore { /* Add everything, that I expect from my sub ordinate grid agents. Build distinct pairs of sending actor reference * and target node */ - inferiorSubGridGateToActorRef + inferiorSubGridGateToActorRef.toVector .map { case (gate, reference) => reference -> gate.getSuperiorNode.getUuid } From a03cc70d32d486373b867ed0cb9b6784efc7d62c Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 22 Jun 2022 00:04:53 +0200 Subject: [PATCH 03/17] Consolidate ReceivedValues and ReceivedValuesStore field types --- .../ie3/simona/agent/grid/DBFSAlgorithm.scala | 53 ++++++++----------- .../ie3/simona/agent/grid/GridAgentData.scala | 25 +++------ .../simona/agent/grid/PowerFlowSupport.scala | 2 +- .../simona/agent/grid/ReceivedValues.scala | 11 ++-- .../agent/grid/ReceivedValuesStore.scala | 22 +++++--- 5 files changed, 52 insertions(+), 61 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala index 6db2ed592c..c4087ac91a 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala @@ -510,12 +510,8 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { // if yes, we do another PF with adapted values // if no, we are done with the pf and ready to report to our parent grid val changed = receivedPowerValues.values.exists { - case (_, providePowerResponseMsgOpt) => - providePowerResponseMsgOpt.exists { - case _: AssetPowerChangedMessage => true - case _ => false - } - case _ => false + case (_, _: AssetPowerChangedMessage) => true + case _ => false } if (changed) { @@ -1057,10 +1053,10 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { fInPU )).map { case providedPowerValuesMessage: AssetPowerChangedMessage => - (assetAgent, Some(providedPowerValuesMessage)) + (assetAgent, providedPowerValuesMessage) case assetPowerUnchangedMessage: AssetPowerUnchangedMessage => - (assetAgent, Some(assetPowerUnchangedMessage)) - }.mapTo[ActorPowerRequestResponse] + (assetAgent, assetPowerUnchangedMessage) + } }) }.toVector ) @@ -1111,9 +1107,9 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { inferiorGridGateNodes )).map { case provideGridPowerMessage: ProvideGridPowerMessage => - (inferiorGridAgentRef, Some(provideGridPowerMessage)) + (inferiorGridAgentRef, provideGridPowerMessage) case FailedPowerFlow => - (inferiorGridAgentRef, Some(FailedPowerFlow)) + (inferiorGridAgentRef, FailedPowerFlow) } } ) @@ -1147,25 +1143,22 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { s"asking superior grids for slack voltage values: {}", superiorGridGates ) - if (superiorGridGates.nonEmpty) - Some( - Future - .sequence( - superiorGridGates.map(superiorGridGate => { - val superiorGridAgent = subGridGateToActorRef(superiorGridGate) - (superiorGridAgent ? RequestSlackVoltageMessage( - currentSweepNo, - superiorGridGate.getSuperiorNode.getUuid - )).map(providedSlackValues => - (superiorGridAgent, Option(providedSlackValues)) - ).mapTo[(ActorRef, Option[ProvideSlackVoltageMessage])] - }) - ) - .map(ReceivedSlackValues) - .pipeTo(self) - ) - else - None + Option.when(superiorGridGates.nonEmpty) { + Future + .sequence( + superiorGridGates.map(superiorGridGate => { + val superiorGridAgent = subGridGateToActorRef(superiorGridGate) + (superiorGridAgent ? RequestSlackVoltageMessage( + currentSweepNo, + superiorGridGate.getSuperiorNode.getUuid + )).map { case providedSlackValues: ProvideSlackVoltageMessage => + (superiorGridAgent, providedSlackValues) + } + }) + ) + .map(ReceivedSlackValues) + .pipeTo(self) + } } /** Create an instance of diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala index 3c548716d6..2064f73efa 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala @@ -6,7 +6,6 @@ package edu.ie3.simona.agent.grid -import java.util.UUID import akka.actor.ActorRef import akka.event.LoggingAdapter import edu.ie3.datamodel.graph.SubGridGate @@ -14,10 +13,10 @@ import edu.ie3.datamodel.models.input.container.SubGridContainer import edu.ie3.powerflow.model.PowerFlowResult import edu.ie3.powerflow.model.PowerFlowResult.SuccessFullPowerFlowResult.ValidNewtonRaphsonPFResult import edu.ie3.simona.agent.grid.ReceivedValues.{ - ActorPowerRequestResponse, ReceivedPowerValues, ReceivedSlackValues } +import edu.ie3.simona.agent.grid.ReceivedValuesStore.NodeToReceivedPower import edu.ie3.simona.model.grid.{GridModel, RefSystem} import edu.ie3.simona.ontology.messages.PowerMessage.{ FailedPowerFlow, @@ -25,6 +24,8 @@ import edu.ie3.simona.ontology.messages.PowerMessage.{ ProvidePowerMessage } +import java.util.UUID + sealed trait GridAgentData /** Contains all state data of [[GridAgent]] @@ -222,7 +223,7 @@ object GridAgentData { ) { case ( nodeToReceivedPowerValuesMapWithAddedPowerResponse, - (senderRef, Some(powerResponseMessage)) + (senderRef, powerResponseMessage) ) => updateNodalReceivedVector( powerResponseMessage, @@ -230,10 +231,6 @@ object GridAgentData { senderRef, replace ) - case (_, (senderRef, None)) => - throw new RuntimeException( - s"Received a 'None' as provided power from '$senderRef'. Provision of 'None' is not supported." - ) } this.copy( receivedValueStore = receivedValueStore @@ -256,7 +253,7 @@ object GridAgentData { * @return */ private def uuid( - nodeToReceivedPower: Map[UUID, Vector[ActorPowerRequestResponse]], + nodeToReceivedPower: NodeToReceivedPower, senderRef: ActorRef, replace: Boolean ): Option[UUID] = @@ -288,9 +285,7 @@ object GridAgentData { */ private def updateNodalReceivedVector( powerResponse: PowerResponseMessage, - nodeToReceived: Map[UUID, Vector[ - (ActorRef, Option[PowerResponseMessage]) - ]], + nodeToReceived: NodeToReceivedPower, senderRef: ActorRef, replace: Boolean ): Map[UUID, Vector[(ActorRef, Option[PowerResponseMessage])]] = { @@ -356,7 +351,7 @@ object GridAgentData { ) { case ( nodeToSlackVoltageUpdated, - (senderRef, maybeSlackValues @ Some(slackValues)) + (senderRef, slackValues) ) => val nodeUuid: UUID = slackValues.nodeUuid @@ -364,7 +359,7 @@ object GridAgentData { .get(nodeUuid) match { case Some(None) => /* Slack voltage is expected and not yet received */ - nodeToSlackVoltageUpdated + (nodeUuid -> maybeSlackValues) + nodeToSlackVoltageUpdated + (nodeUuid -> Some(slackValues)) case Some(Some(_)) => throw new RuntimeException( s"Already received slack value for node $nodeUuid!" @@ -374,10 +369,6 @@ object GridAgentData { s"Received slack value for node $nodeUuid from $senderRef which is not in my slack values nodes list!" ) } - case (_, (senderRef, None)) => - throw new RuntimeException( - s"Received an empty voltage message from $senderRef" - ) } this.copy( receivedValueStore = receivedValueStore.copy( diff --git a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala index 60f0eddc73..1c8df3e67d 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala @@ -201,7 +201,7 @@ trait PowerFlowSupport { val nodeStateData = sweepValueStoreData.stateData val targetVoltage = if (nodeStateData.nodeType == NodeType.SL) { val receivedSlackVoltage = receivedSlackValues.values - .flatMap { case (_, slackVoltageMsg) => slackVoltageMsg } + .map { case (_, slackVoltageMsg) => slackVoltageMsg } .find(_.nodeUuid == sweepValueStoreData.nodeUuid) .getOrElse( throw new RuntimeException( diff --git a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValues.scala b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValues.scala index 48047822a5..7c578c858a 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValues.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValues.scala @@ -15,12 +15,10 @@ import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessag */ sealed trait ReceivedValues -case object ReceivedValues { +object ReceivedValues { - type ActorPowerRequestResponse = - (ActorRef, Option[PowerResponseMessage]) - type ActorSlackVoltageRequestResponse = - (ActorRef, Option[ProvideSlackVoltageMessage]) + type ActorPowerRequestResponse = (ActorRef, PowerResponseMessage) + type ActorSlackVoltageRequestResponse = (ActorRef, ProvideSlackVoltageMessage) sealed trait ReceivedPowerValues extends ReceivedValues { def values: Vector[ActorPowerRequestResponse] @@ -29,6 +27,7 @@ case object ReceivedValues { /** Wrapper for received asset power values (p, q) * * @param values + * the asset power values and their senders */ final case class ReceivedAssetPowerValues( values: Vector[ActorPowerRequestResponse] @@ -37,6 +36,7 @@ case object ReceivedValues { /** Wrapper for received grid power values (p, q) * * @param values + * the grid power values and their senders */ final case class ReceivedGridPowerValues( values: Vector[ActorPowerRequestResponse] @@ -45,6 +45,7 @@ case object ReceivedValues { /** Wrapper for received slack voltage values (v) * * @param values + * the slack voltage values and their senders */ final case class ReceivedSlackValues( values: Vector[ActorSlackVoltageRequestResponse] diff --git a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala index 3a698ba8ca..6c5ec5780f 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala @@ -6,14 +6,20 @@ package edu.ie3.simona.agent.grid -import java.util.UUID - import akka.actor.ActorRef import edu.ie3.datamodel.graph.SubGridGate -import edu.ie3.simona.agent.grid.ReceivedValues.ActorPowerRequestResponse -import edu.ie3.simona.ontology.messages.PowerMessage.ProvidePowerMessage +import edu.ie3.simona.agent.grid.ReceivedValuesStore.{ + NodeToReceivedPower, + NodeToReceivedSlackVoltage +} +import edu.ie3.simona.ontology.messages.PowerMessage.{ + PowerResponseMessage, + ProvidePowerMessage +} import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage +import java.util.UUID + /** Value store that contains all data that should be received by the * [[GridAgent]] from other agents. The mapping is structured as the uuid of a * node to a tuple of either a vector of actorRefs to @@ -34,14 +40,14 @@ import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessag * [[GridAgent]] s if any */ final case class ReceivedValuesStore private ( - nodeToReceivedPower: Map[UUID, Vector[ActorPowerRequestResponse]], - nodeToReceivedSlackVoltage: Map[UUID, Option[ProvideSlackVoltageMessage]] + nodeToReceivedPower: NodeToReceivedPower, + nodeToReceivedSlackVoltage: NodeToReceivedSlackVoltage ) -case object ReceivedValuesStore { +object ReceivedValuesStore { type NodeToReceivedPower = - Map[UUID, Vector[(ActorRef, Option[ProvidePowerMessage])]] + Map[UUID, Vector[(ActorRef, Option[PowerResponseMessage])]] type NodeToReceivedSlackVoltage = Map[UUID, Option[ProvideSlackVoltageMessage]] From 91c4de811387ff5fb7b02a64efb748fa41fbc325 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 22 Jun 2022 09:43:02 +0200 Subject: [PATCH 04/17] Refactoring --- .../ie3/simona/agent/grid/GridAgentData.scala | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala index 2064f73efa..580a0fc769 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala @@ -238,37 +238,6 @@ object GridAgentData { ) } - /** Find the uuid of the grid node the provided actor sender ref is located - * on. - * - * @param nodeToReceivedPower - * a mapping of a grid node uuid to all actors and their optionally - * already provided power responses - * @param senderRef - * the actor whose node uuid should be determined - * @param replace - * if true, it is checked if the sender has already provided power - * values, which should be replaced, if false, it is checked if the - * sender has no yet provided power values - * @return - */ - private def uuid( - nodeToReceivedPower: NodeToReceivedPower, - senderRef: ActorRef, - replace: Boolean - ): Option[UUID] = - nodeToReceivedPower - .find { case (_, receivedPowerMessages) => - receivedPowerMessages.exists { case (ref, maybePowerResponse) => - ref == senderRef && - (if (!replace) - maybePowerResponse.isEmpty - else - maybePowerResponse.isDefined) - } - } - .map { case (uuid, _) => uuid } - /** Identify and update the vector of already received information. * * @param powerResponse @@ -333,6 +302,37 @@ object GridAgentData { .updated(nodeUuid, receivedVector) } + /** Find the uuid of the grid node the provided actor sender ref is located + * on. + * + * @param nodeToReceivedPower + * a mapping of a grid node uuid to all actors and their optionally + * already provided power responses + * @param senderRef + * the actor whose node uuid should be determined + * @param replace + * if true, it is checked if the sender has already provided power + * values, which should be replaced, if false, it is checked if the + * sender has no yet provided power values + * @return + */ + private def uuid( + nodeToReceivedPower: NodeToReceivedPower, + senderRef: ActorRef, + replace: Boolean + ): Option[UUID] = + nodeToReceivedPower + .find { case (_, receivedPowerMessages) => + receivedPowerMessages.exists { case (ref, maybePowerResponse) => + ref == senderRef && + (if (!replace) + maybePowerResponse.isEmpty + else + maybePowerResponse.isDefined) + } + } + .map { case (uuid, _) => uuid } + /** Update this [[GridAgentBaseData]] with [[ReceivedSlackValues]] and * return a copy of this [[GridAgentBaseData]] for further processing * From 6b1b7d611ac7f15c8d6b2eaa6091a30239c205d3 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Sun, 26 Jun 2022 17:38:11 +0200 Subject: [PATCH 05/17] Apply suggestions from code review variable naming Co-authored-by: t-ober <63147366+t-ober@users.noreply.github.com> --- src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala index c4087ac91a..c89f8c18f7 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala @@ -1101,10 +1101,10 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { subGridGate ) -> subGridGate.getSuperiorNode.getUuid } - .map { case (inferiorGridAgentRef, inferiorGridGateNodes) => + .map { case (inferiorGridAgentRef, inferiorGridGateNode) => (inferiorGridAgentRef ? RequestGridPowerMessage( currentSweepNo, - inferiorGridGateNodes + inferiorGridGateNode )).map { case provideGridPowerMessage: ProvideGridPowerMessage => (inferiorGridAgentRef, provideGridPowerMessage) From 30b2ea7a1925d3aa6bacbf3dcdd9b40876b3fa86 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Sun, 26 Jun 2022 17:39:09 +0200 Subject: [PATCH 06/17] Update src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala anonymous function simplification Co-authored-by: t-ober <63147366+t-ober@users.noreply.github.com> --- src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala index c89f8c18f7..4e246e7132 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala @@ -364,8 +364,8 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { ) => // inform my child grids about the end of this grid simulation gridAgentBaseData.inferiorGridGates - .map { inferiorGridGate => - gridAgentBaseData.gridEnv.subnetGateToActorRef(inferiorGridGate) + .map { + gridAgentBaseData.gridEnv.subnetGateToActorRef(_) } .foreach(_ ! FinishGridSimulationTrigger(currentTick)) From 64a40f33c9d05cf2aeae9a47187ed67ad94161ff Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Sun, 26 Jun 2022 17:50:43 +0200 Subject: [PATCH 07/17] A bunch of code style improvements per Thomas' suggestions --- .../edu/ie3/simona/agent/grid/DBFSAlgorithm.scala | 14 +++++++------- .../edu/ie3/simona/agent/grid/GridAgentData.scala | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala index 4e246e7132..1a62da2bee 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala @@ -364,7 +364,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { ) => // inform my child grids about the end of this grid simulation gridAgentBaseData.inferiorGridGates - .map { + .map { gridAgentBaseData.gridEnv.subnetGateToActorRef(_) } .foreach(_ ! FinishGridSimulationTrigger(currentTick)) @@ -1025,7 +1025,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { val (eInPu, fInPU) = sweepValueStore match { case Some(sweepValueStore) => - val (pInSi, qInSi) = refSystem.vInSi( + val (eInSi, fInSi) = refSystem.vInSi( sweepValueStore.sweepData .find(_.nodeUuid == nodeUuid) .getOrElse( @@ -1037,8 +1037,8 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { .voltage ) ( - refSystem.vInPu(pInSi), - refSystem.vInPu(qInSi) + refSystem.vInPu(eInSi), + refSystem.vInPu(fInSi) ) case None => ( @@ -1096,10 +1096,10 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport { Future .sequence( inferiorGridGates - .map { subGridGate => + .map { inferiorGridGate => subGridGateToActorRef( - subGridGate - ) -> subGridGate.getSuperiorNode.getUuid + inferiorGridGate + ) -> inferiorGridGate.getSuperiorNode.getUuid } .map { case (inferiorGridAgentRef, inferiorGridGateNode) => (inferiorGridAgentRef ? RequestGridPowerMessage( diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala index 580a0fc769..a645434e0b 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala @@ -183,11 +183,11 @@ object GridAgentData { val allRequestedDataReceived: Boolean = { // we expect power values from inferior grids and assets val assetAndGridPowerValuesReady = - receivedValueStore.nodeToReceivedPower.values.forall(vector => - vector.forall { case (_, powerResponseOpt) => + receivedValueStore.nodeToReceivedPower.values.forall { + _.forall { case (_, powerResponseOpt) => powerResponseOpt.isDefined } - ) + } // we expect slack voltages only from our superior grids (if any) val slackVoltageValuesReady = receivedValueStore.nodeToReceivedSlackVoltage.values @@ -261,7 +261,7 @@ object GridAgentData { // extract the nodeUuid that corresponds to the sender's actorRef and check if we expect a message from the sender val nodeUuid = powerResponse match { case powerValuesMessage: ProvidePowerMessage => - uuid(nodeToReceived, senderRef, replace) + getNodeUuidForSender(nodeToReceived, senderRef, replace) .getOrElse( throw new RuntimeException( s"$actorName Received asset power values msg $powerValuesMessage " + @@ -269,7 +269,7 @@ object GridAgentData { ) ) case FailedPowerFlow => - uuid(nodeToReceived, senderRef, replace) + getNodeUuidForSender(nodeToReceived, senderRef, replace) .getOrElse( throw new RuntimeException( s"$actorName Received failed power flow message " + @@ -316,7 +316,7 @@ object GridAgentData { * sender has no yet provided power values * @return */ - private def uuid( + private def getNodeUuidForSender( nodeToReceivedPower: NodeToReceivedPower, senderRef: ActorRef, replace: Boolean From 5847a7e529b984591af0801a200ec14940f0adbe Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Sun, 26 Jun 2022 22:19:19 +0200 Subject: [PATCH 08/17] Refactoring iMagAndAngle and adding commentary --- .../agent/grid/GridResultsSupport.scala | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala index 1bf271592a..24977c38e4 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala @@ -7,9 +7,6 @@ package edu.ie3.simona.agent.grid import akka.event.LoggingAdapter - -import java.time.ZonedDateTime -import java.util.UUID import breeze.math.Complex import edu.ie3.datamodel.models.StandardUnits import edu.ie3.datamodel.models.input.connector.ConnectorPort @@ -34,14 +31,15 @@ import edu.ie3.simona.model.grid._ import edu.ie3.util.quantities.PowerSystemUnits import edu.ie3.util.scala.quantities.QuantityUtil import edu.ie3.util.scala.quantities.QuantityUtil._ - -import javax.measure.Quantity -import javax.measure.quantity.{Angle, ElectricCurrent} import tech.units.indriya.ComparableQuantity import tech.units.indriya.quantity.Quantities import tech.units.indriya.unit.Units -import scala.math.{asin, atan, copySign, max, pow, signum, sqrt} +import java.time.ZonedDateTime +import java.util.UUID +import javax.measure.Quantity +import javax.measure.quantity.{Angle, ElectricCurrent} +import scala.math._ /** Trait that holds methods to convert the results of a power flow calculation * to their corresponding [[edu.ie3.datamodel.models.result.ResultEntity]] @@ -527,7 +525,8 @@ private[grid] trait GridResultsSupport { * and i' = (-i_real, -i_imag) will lead to the same angle. However, for * power system simulation, the absolute orientation in the complex plane * with regard to the positive real axis is of interest. Therefore, - * additional 180° are added, if the real part of the current is negative. + * additional 180 degrees are added, if the real part of the current is + * negative. * * @param iPu * the electric current in p.u. @@ -542,22 +541,10 @@ private[grid] trait GridResultsSupport { ): (ComparableQuantity[ElectricCurrent], ComparableQuantity[Angle]) = ( iNominal.multiply(iPu.abs).asComparable, - angle(iPu) + complexToAngle(iPu) ) - /** Correct the offset of an angle dependent on the direction. If the - * direction is negative, 180° are added - */ - private val offSetCorrection - : (ComparableQuantity[Angle], Double) => ComparableQuantity[Angle] = - (angle: ComparableQuantity[Angle], dir: Double) => - if (dir < 0) { - angle.add(Quantities.getQuantity(180d, PowerSystemUnits.DEGREE_GEOM)) - } else { - angle - } - - /** Calculate the angle of the complex value given. The angle has the full + /** Calculate the angle of the complex value given. The angle has the proper * orientation on the complex plane. * * @param cplx @@ -565,28 +552,41 @@ private[grid] trait GridResultsSupport { * @return * The angle of the complex value */ - private def angle(cplx: Complex): ComparableQuantity[Angle] = cplx match { - case _ if cplx.abs == 0 => - /* The complex value has no magnitude, therefore define the angle to zero */ - Quantities.getQuantity(0d, PowerSystemUnits.DEGREE_GEOM) - case Complex(real, imag) => - /* Calculate the angle between real and imaginary part */ - val baseAngle = atan(imag / real).toDegrees - - if (baseAngle.isNaN) { - /* There is only an imaginary part */ - offSetCorrection( + private def complexToAngle(cplx: Complex): ComparableQuantity[Angle] = + cplx match { + case Complex(0d, 0d) => + /* The complex value has no magnitude, therefore define the angle to zero */ + Quantities.getQuantity(0d, PowerSystemUnits.DEGREE_GEOM) + case Complex(0d, imag) => + /* There is only an imaginary part: + Angle can be 90 or 270 degrees, depending on sign of the imaginary part */ + angleOffsetCorrection( Quantities.getQuantity(90d, PowerSystemUnits.DEGREE_GEOM), imag ) - } else { - /* Correct the angle for full orientation in the complex plane */ - offSetCorrection( + case Complex(real, imag) => + /* Both real and imaginary part are != 0. This means atan can be used to + compute an angle between -90 and 90 degrees. To compute the actual angle + (between -180 and 180 degrees), 180 degrees have to be added in case that + the real part is negative. */ + val baseAngle = atan(imag / real).toDegrees + angleOffsetCorrection( Quantities.getQuantity(baseAngle, PowerSystemUnits.DEGREE_GEOM), real ) - } - } + } + + /** Correct the offset of an angle dependent on the direction. If the + * direction is negative, 180 degrees are added + */ + private def angleOffsetCorrection( + angle: ComparableQuantity[Angle], + dir: Double + ): ComparableQuantity[Angle] = + if (dir < 0) + angle.add(Quantities.getQuantity(180d, PowerSystemUnits.DEGREE_GEOM)) + else + angle /** Calculates the electric current of a two-port element @ port i (=A) and j * (=B) based on the provided voltages @ each port and the corresponding From b506cdb8e332cf759a48940156f27d6bbc704753 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Sun, 26 Jun 2022 22:45:13 +0200 Subject: [PATCH 09/17] Refactoring NodeToReceivedPower to use maps as values instead of vectors --- .../ie3/simona/agent/grid/GridAgentData.scala | 17 +++++--------- .../agent/grid/ReceivedValuesStore.scala | 22 ++++++++++--------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala index a645434e0b..b9cdb65561 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala @@ -257,7 +257,7 @@ object GridAgentData { nodeToReceived: NodeToReceivedPower, senderRef: ActorRef, replace: Boolean - ): Map[UUID, Vector[(ActorRef, Option[PowerResponseMessage])]] = { + ): NodeToReceivedPower = { // extract the nodeUuid that corresponds to the sender's actorRef and check if we expect a message from the sender val nodeUuid = powerResponse match { case powerValuesMessage: ProvidePowerMessage => @@ -283,23 +283,18 @@ object GridAgentData { } // update the values in the received map - val receivedVector = nodeToReceived + val nodeReceived = nodeToReceived .getOrElse( nodeUuid, throw new RuntimeException( s"NodeId $nodeUuid is not part of nodeToReceivedPowerValuesMap!" ) - ) - /* Filter out the entry, that belongs to the given sender and is not defined, yet, in case no replacement shall - * be made. If a replacement is desired, filter out the already provided data. */ - .filterNot { case (k, v) => - k == senderRef & - (if (!replace) v.isEmpty else v.isDefined) - } :+ (senderRef, Some(powerResponse)) + ) + + // add or update entry in map of node entries + (senderRef -> Some(powerResponse)) /* Actually update the map and hand it back */ - nodeToReceived - .updated(nodeUuid, receivedVector) + nodeToReceived.updated(nodeUuid, nodeReceived) } /** Find the uuid of the grid node the provided actor sender ref is located diff --git a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala index 6c5ec5780f..b48be36cad 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala @@ -47,7 +47,7 @@ final case class ReceivedValuesStore private ( object ReceivedValuesStore { type NodeToReceivedPower = - Map[UUID, Vector[(ActorRef, Option[PowerResponseMessage])]] + Map[UUID, Map[ActorRef, Option[PowerResponseMessage]]] type NodeToReceivedSlackVoltage = Map[UUID, Option[ProvideSlackVoltageMessage]] @@ -100,27 +100,29 @@ object ReceivedValuesStore { /* Collect everything, that I expect from my asset agents */ val assetsToReceivedPower: NodeToReceivedPower = nodeToAssetAgents.collect { case (uuid: UUID, actorRefs: Set[ActorRef]) => - (uuid, actorRefs.toVector.map(actorRef => actorRef -> None)) + (uuid, actorRefs.map(actorRef => actorRef -> None).toMap) } - /* Add everything, that I expect from my sub ordinate grid agents. Build distinct pairs of sending actor reference - * and target node */ - inferiorSubGridGateToActorRef.toVector + /* Add everything, that I expect from my sub ordinate grid agents. + * Build distinct pairs of sending actor reference and target node. + * Convert to sequence first, since the map operation conflates + * key/value pairs with the same key */ + inferiorSubGridGateToActorRef.toSeq .map { case (gate, reference) => reference -> gate.getSuperiorNode.getUuid } .foldLeft(assetsToReceivedPower) { case ( subOrdinateToReceivedPower, - (inferiorSubGridRef, couplingNodeUuid) + inferiorSubGridRef -> couplingNodeUuid ) => - /* Check, if there is already something expected for the given coupling node and add reference to the subordinate - * grid agent */ + /* Check, if there is already something expected for the given coupling node + * and add reference to the subordinate grid agent */ val actorRefToMessage = subOrdinateToReceivedPower .getOrElse( couplingNodeUuid, - Vector.empty[(ActorRef, Option[ProvidePowerMessage])] - ) :+ (inferiorSubGridRef -> None) + Map.empty[ActorRef, Option[ProvidePowerMessage]] + ) + (inferiorSubGridRef -> None) /* Update the existing map */ subOrdinateToReceivedPower + (couplingNodeUuid -> actorRefToMessage) From abf2ac21cfd2118332fa6cd10bc913a30ab0ab15 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Sun, 26 Jun 2022 22:55:26 +0200 Subject: [PATCH 10/17] Fixing ReceivedValuesStoreSpec: Maps instead of Vectors --- .../agent/grid/ReceivedValuesStoreSpec.scala | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/src/test/scala/edu/ie3/simona/agent/grid/ReceivedValuesStoreSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/ReceivedValuesStoreSpec.scala index 1101e77528..4d28723650 100644 --- a/src/test/scala/edu/ie3/simona/agent/grid/ReceivedValuesStoreSpec.scala +++ b/src/test/scala/edu/ie3/simona/agent/grid/ReceivedValuesStoreSpec.scala @@ -92,19 +92,13 @@ class ReceivedValuesStoreSpec receivedValuesStore.nodeToReceivedPower.size shouldBe 3 receivedValuesStore.nodeToReceivedPower( UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546") - ) shouldBe Vector( - (actorProbe1.ref, None) - ) + ) shouldBe Map(actorProbe1.ref -> None) receivedValuesStore.nodeToReceivedPower( UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97") - ) shouldBe Vector( - (actorProbe2.ref, None) - ) + ) shouldBe Map(actorProbe2.ref -> None) receivedValuesStore.nodeToReceivedPower( UUID.fromString("5cd55ab5-a7d2-499f-a25f-6dbc3845c5e8") - ) shouldBe Vector( - (actorProbe3.ref, None) - ) + ) shouldBe Map(actorProbe3.ref -> None) receivedValuesStore.nodeToReceivedSlackVoltage.size shouldBe 1 receivedValuesStore.nodeToReceivedSlackVoltage( @@ -141,14 +135,12 @@ class ReceivedValuesStoreSpec receivedValuesStore.nodeToReceivedPower.size shouldBe 2 receivedValuesStore.nodeToReceivedPower( UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546") - ) shouldBe Vector( - (actorProbe1.ref, None) - ) + ) shouldBe Map(actorProbe1.ref -> None) receivedValuesStore.nodeToReceivedPower( UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97") - ) shouldBe Vector( - (actorProbe2.ref, None), - (actorProbe3.ref, None) + ) shouldBe Map( + actorProbe2.ref -> None, + actorProbe3.ref -> None ) } @@ -169,19 +161,13 @@ class ReceivedValuesStoreSpec receivedValuesStore.nodeToReceivedPower.size shouldBe 3 receivedValuesStore.nodeToReceivedPower( UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546") - ) shouldBe Vector( - (actorProbe1.ref, None) - ) + ) shouldBe Map(actorProbe1.ref -> None) receivedValuesStore.nodeToReceivedPower( UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97") - ) shouldBe Vector( - (actorProbe2.ref, None) - ) + ) shouldBe Map(actorProbe2.ref -> None) receivedValuesStore.nodeToReceivedPower( UUID.fromString("5cd55ab5-a7d2-499f-a25f-6dbc3845c5e8") - ) shouldBe Vector( - (actorProbe3.ref, None) - ) + ) shouldBe Map(actorProbe3.ref -> None) } @@ -231,14 +217,10 @@ class ReceivedValuesStoreSpec receivedValuesStore.nodeToReceivedPower.size shouldBe 2 receivedValuesStore.nodeToReceivedPower( UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546") - ) shouldBe Vector( - (actorProbe1.ref, None) - ) + ) shouldBe Map(actorProbe1.ref -> None) receivedValuesStore.nodeToReceivedPower( UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97") - ) shouldBe Vector( - (actorProbe2.ref, None) - ) + ) shouldBe Map(actorProbe2.ref -> None) } From ad0dd43675685fa14e114218962c2c657ff70502 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Mon, 27 Jun 2022 18:40:01 +0200 Subject: [PATCH 11/17] Simplifying PowerFlowSupport a bit --- .../edu/ie3/simona/agent/grid/PowerFlowSupport.scala | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala index 1c8df3e67d..e284ab3537 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala @@ -82,8 +82,7 @@ trait PowerFlowSupport { val mainRefSystemPowerUnit = gridMainRefSystem.nominalPower.getUnit - nodes.foldLeft(Array.empty[PresetData])((operatingPoint, nodeModel) => { - + nodes.map { nodeModel => // note: currently we only support pq nodes as we not distinguish between pq/pv nodes - // when slack emulators or pv-node assets are added this needs to be considered here val nodeType = if (nodeModel.isSlack) NodeType.SL else NodeType.PQ @@ -159,12 +158,8 @@ trait PowerFlowSupport { else 1.0) } - val presetNodeData = - PresetData(nodeIdx, nodeType, apparentPower, targetVoltageInPu.abs) - - operatingPoint :+ presetNodeData - - }) + PresetData(nodeIdx, nodeType, apparentPower, targetVoltageInPu.abs) + }.toArray } /** Composes the current operation point needed by From 12751558b96202d11ed2adff36ae8116889cc26a Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Tue, 28 Jun 2022 23:50:43 +0200 Subject: [PATCH 12/17] Fixing PowerFlowSupport --- .../scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala index e284ab3537..428c26e596 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala @@ -82,7 +82,7 @@ trait PowerFlowSupport { val mainRefSystemPowerUnit = gridMainRefSystem.nominalPower.getUnit - nodes.map { nodeModel => + nodes.toArray.map { nodeModel => // note: currently we only support pq nodes as we not distinguish between pq/pv nodes - // when slack emulators or pv-node assets are added this needs to be considered here val nodeType = if (nodeModel.isSlack) NodeType.SL else NodeType.PQ @@ -159,7 +159,7 @@ trait PowerFlowSupport { } PresetData(nodeIdx, nodeType, apparentPower, targetVoltageInPu.abs) - }.toArray + } } /** Composes the current operation point needed by From 8eb08c31e9364c8503c3554ba1ae5c902417150d Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Wed, 29 Jun 2022 15:46:58 +0200 Subject: [PATCH 13/17] Renaming method in GridAgentData --- src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala index b9cdb65561..b43ee091d3 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala @@ -225,7 +225,7 @@ object GridAgentData { nodeToReceivedPowerValuesMapWithAddedPowerResponse, (senderRef, powerResponseMessage) ) => - updateNodalReceivedVector( + updateNodalReceivedPower( powerResponseMessage, nodeToReceivedPowerValuesMapWithAddedPowerResponse, senderRef, @@ -252,7 +252,7 @@ object GridAgentData { * The nodal uuid as well as the updated collection of received * information */ - private def updateNodalReceivedVector( + private def updateNodalReceivedPower( powerResponse: PowerResponseMessage, nodeToReceived: NodeToReceivedPower, senderRef: ActorRef, From 9d24f9fa44a70553d88d5e04088fe42bee1ff9c3 Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Thu, 30 Jun 2022 11:58:58 +0200 Subject: [PATCH 14/17] Reverting changes in SystemParticipant --- .../edu/ie3/simona/model/participant/SystemParticipant.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala index a3fa10be44..3331b2280d 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala @@ -154,10 +154,10 @@ abstract class SystemParticipant[CD <: CalcRelevantData]( KILOVOLTAMPERE ) - if (apparentPower.isGreaterThan(sMax.multiply(1.05))) { + if (apparentPower.isGreaterThan(sMax)) { logger.warn( s"The var characteristics \'$qControl\' of model \'$id\' ($uuid) imposes an apparent " + - s"power (= $apparentPower) that exceeds 105 % of " + + s"power (= $apparentPower) that exceeds " + s"rated apparent power specifications (= $sMax). " + s"Therefore, setting reactive power output to the to the upper limit " + s"in correspondence to the existing active power $activePower." From ffb1a321a045b3d86ac1099aab80950d75f467ef Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 1 Jul 2022 10:08:47 +0200 Subject: [PATCH 15/17] Updating commentary on angle computation Co-authored-by: Daniel Feismann <98817556+danielfeismann@users.noreply.github.com> --- .../scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala index 24977c38e4..3043fb4fca 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala @@ -565,10 +565,7 @@ private[grid] trait GridResultsSupport { imag ) case Complex(real, imag) => - /* Both real and imaginary part are != 0. This means atan can be used to - compute an angle between -90 and 90 degrees. To compute the actual angle - (between -180 and 180 degrees), 180 degrees have to be added in case that - the real part is negative. */ +/* Both real and imaginary parts are != 0. This means that the angle related to the positive real axis is to be determined. To do this, atan can be used to calculate an angle between -90 and 90 degrees. To calculate the real angle (between -180 and 180 degrees) with respect to the real axis, 180 degrees must be added if the real part is negative. */ val baseAngle = atan(imag / real).toDegrees angleOffsetCorrection( Quantities.getQuantity(baseAngle, PowerSystemUnits.DEGREE_GEOM), From f013f1707a9d3b5ba32638c149243d5d4cc85d3e Mon Sep 17 00:00:00 2001 From: Sebastian Peter Date: Fri, 1 Jul 2022 10:17:36 +0200 Subject: [PATCH 16/17] fmt comment --- .../edu/ie3/simona/agent/grid/GridResultsSupport.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala index 3043fb4fca..7a0440bfea 100644 --- a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala +++ b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala @@ -565,7 +565,12 @@ private[grid] trait GridResultsSupport { imag ) case Complex(real, imag) => -/* Both real and imaginary parts are != 0. This means that the angle related to the positive real axis is to be determined. To do this, atan can be used to calculate an angle between -90 and 90 degrees. To calculate the real angle (between -180 and 180 degrees) with respect to the real axis, 180 degrees must be added if the real part is negative. */ + /* Both real and imaginary parts are != 0. This means that the angle + * related to the positive real axis is to be determined. To do this, + * atan can be used to calculate an angle between -90 and 90 degrees. + * To calculate the real angle (between -180 and 180 degrees) with + * respect to the real axis, 180 degrees must be added if the real + * part is negative. */ val baseAngle = atan(imag / real).toDegrees angleOffsetCorrection( Quantities.getQuantity(baseAngle, PowerSystemUnits.DEGREE_GEOM), From d2254c1daabc1d708ed15cbfab5726d310f64da3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 13:22:48 +0000 Subject: [PATCH 17/17] Bump scopt_2.13 from 4.0.1 to 4.1.0 (#277) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c493238f58..a0d7c869c9 100644 --- a/build.gradle +++ b/build.gradle @@ -132,7 +132,7 @@ dependencies { /* config */ implementation 'com.typesafe:config:1.4.2' implementation "com.github.carueda:tscfg_2.13:$tscfgVersion" - implementation "com.github.scopt:scopt_${scalaVersion}:4.0.1" // cmd args parser + implementation "com.github.scopt:scopt_${scalaVersion}:4.1.0" // cmd args parser // JTS implementation ("org.locationtech.jts:jts-core:${jtsVersion}"){