Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect for slack voltage angle in distributed power flow calculation #71

Merged
merged 11 commits into from
Jul 31, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed some unreachable code [#167](https://github.com/ie3-institute/simona/issues/167)
- Fix treatment of non-InitializeTrigger triggers in initialization within SimScheduler [#237](https://github.com/ie3-institute/simona/issues/237)
- Fix breaking SIMONA caused by introducing temperature dependant load profiles in PSDM [#255](https://github.com/ie3-institute/simona/issues/255)
- Respect for voltage angle in DBFS slack voltage exchange protocol [#69](https://github.com/ie3-institute/simona/issues/69)

### Removed
- Remove workaround for tscfg tmp directory [#178](https://github.com/ie3-institute/simona/issues/178)
Expand Down
196 changes: 105 additions & 91 deletions src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -439,18 +439,23 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
)

val gridModel = gridAgentBaseData.gridEnv.gridModel
val powerFlowResult = newtonRaphsonPF(
gridModel,
gridAgentBaseData.powerFlowParams.maxIterations,
composeOperatingPoint(
gridModel.gridComponents.nodes,
gridModel.gridComponents.transformers,
gridModel.gridComponents.transformers3w,
gridModel.nodeUuidToIndexMap,
gridAgentBaseData.receivedValueStore,
gridModel.mainRefSystem
)
)(gridAgentBaseData.powerFlowParams.epsilon)

val powerFlowResult = composeOperatingPoint(
gridModel.gridComponents.nodes,
gridModel.gridComponents.transformers,
gridModel.gridComponents.transformers3w,
gridModel.nodeUuidToIndexMap,
gridAgentBaseData.receivedValueStore,
gridModel.mainRefSystem
) match {
case (operatingPoint, slackNodeVoltages) =>
newtonRaphsonPF(
gridModel,
gridAgentBaseData.powerFlowParams.maxIterations,
operatingPoint,
slackNodeVoltages
)(gridAgentBaseData.powerFlowParams.epsilon)
}

// if res is valid, ask our assets (if any) for updated power values based on the newly determined nodal voltages
powerFlowResult match {
Expand Down Expand Up @@ -589,92 +594,100 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
s"$actorName Unable to get results from previous sweep ${gridAgentBaseData.currentSweepNo - 1}!"
)
)
newtonRaphsonPF(
gridModel,
gridAgentBaseData.powerFlowParams.maxIterations,
composeOperatingPointWithUpdatedSlackVoltages(
receivedSlackValues,
previousSweepData.sweepData,
gridModel.gridComponents.transformers,
gridModel.gridComponents.transformers3w,
gridModel.mainRefSystem
)
)(gridAgentBaseData.powerFlowParams.epsilon) match {
case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
log.debug(
"{}",
composeValidNewtonRaphsonPFResultVoltagesDebugString(
validPowerFlowResult,
gridModel
)
)

// update the data
val sweepValueStore = SweepValueStore(
validPowerFlowResult,
gridModel.gridComponents.nodes,
gridModel.nodeUuidToIndexMap
)
val updatedSweepValueStore =
gridAgentBaseData.sweepValueStores + (gridAgentBaseData.currentSweepNo -> sweepValueStore)

// send request to child grids and assets for updated p/q values
// we start the grid simulation by requesting the p/q values of all the nodes we are responsible for
// as well as the slack voltage power from our superior grid
// 1. assets p/q values
val askForAssetPowersOpt =
askForAssetPowers(
currentTick,
Some(sweepValueStore),
gridAgentBaseData.gridEnv.nodeToAssetAgents,
gridModel.mainRefSystem,
gridAgentBaseData.powerFlowParams.sweepTimeout
)

// 2. inferior grids p/q values
val askForInferiorGridPowersOpt =
askInferiorGridsForPowers(
gridAgentBaseData.currentSweepNo,
gridAgentBaseData.gridEnv.subnetGateToActorRef,
gridAgentBaseData.inferiorGridGates,
gridAgentBaseData.powerFlowParams.sweepTimeout
)

// when we don't have inferior grids and no assets both methods return None and we can skip doing another power
// flow calculation otherwise we go back to simulate grid and wait for the answers
(askForAssetPowersOpt, askForInferiorGridPowersOpt) match {
case (None, None) =>
composeOperatingPointWithUpdatedSlackVoltages(
receivedSlackValues,
previousSweepData.sweepData,
gridModel.gridComponents.transformers,
gridModel.gridComponents.transformers3w,
gridModel.mainRefSystem
) match {
case (operatingPoint, slackNodeVoltages) =>
newtonRaphsonPF(
gridModel,
gridAgentBaseData.powerFlowParams.maxIterations,
operatingPoint,
slackNodeVoltages
)(gridAgentBaseData.powerFlowParams.epsilon) match {
case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
log.debug(
"I don't have assets or child grids. " +
"Going back to SimulateGrid and provide the power flow result if there is any request left."
"{}",
composeValidNewtonRaphsonPFResultVoltagesDebugString(
validPowerFlowResult,
gridModel
)
)

val powerFlowDoneData =
PowerFlowDoneData(gridAgentBaseData, validPowerFlowResult)
// update the data
val sweepValueStore = SweepValueStore(
validPowerFlowResult,
gridModel.gridComponents.nodes,
gridModel.nodeUuidToIndexMap
)
val updatedSweepValueStore =
gridAgentBaseData.sweepValueStores + (gridAgentBaseData.currentSweepNo -> sweepValueStore)

// send request to child grids and assets for updated p/q values
// we start the grid simulation by requesting the p/q values of all the nodes we are responsible for
// as well as the slack voltage power from our superior grid
// 1. assets p/q values
val askForAssetPowersOpt =
askForAssetPowers(
currentTick,
Some(sweepValueStore),
gridAgentBaseData.gridEnv.nodeToAssetAgents,
gridModel.mainRefSystem,
gridAgentBaseData.powerFlowParams.sweepTimeout
)

unstashAll() // we can answer the stashed grid power requests now
goto(SimulateGrid) using powerFlowDoneData
// 2. inferior grids p/q values
val askForInferiorGridPowersOpt =
askInferiorGridsForPowers(
gridAgentBaseData.currentSweepNo,
gridAgentBaseData.gridEnv.subnetGateToActorRef,
gridAgentBaseData.inferiorGridGates,
gridAgentBaseData.powerFlowParams.sweepTimeout
)

case _ =>
log.debug(
"Going back to SimulateGrid and wait for my assets or inferior grids to return."
)
// when we don't have inferior grids and no assets both methods return None and we can skip doing another power
// flow calculation otherwise we go back to simulate grid and wait for the answers
(askForAssetPowersOpt, askForInferiorGridPowersOpt) match {
case (None, None) =>
log.debug(
"I don't have assets or child grids. " +
"Going back to SimulateGrid and provide the power flow result if there is any request left."
)

// go back to simulate grid
goto(SimulateGrid) using gridAgentBaseData
.updateWithReceivedSlackVoltages(receivedSlackValues)
.copy(sweepValueStores = updatedSweepValueStore)
}
val powerFlowDoneData =
PowerFlowDoneData(gridAgentBaseData, validPowerFlowResult)

case failedNewtonRaphsonPFResult: FailedNewtonRaphsonPFResult =>
val powerFlowDoneData =
PowerFlowDoneData(gridAgentBaseData, failedNewtonRaphsonPFResult)
log.warning(
"Power flow with updated slack voltage did finally not converge!"
)
unstashAll() // we can answer the stashed grid power requests now and report a failed power flow back
goto(SimulateGrid) using powerFlowDoneData
unstashAll() // we can answer the stashed grid power requests now
goto(SimulateGrid) using powerFlowDoneData

case _ =>
log.debug(
"Going back to SimulateGrid and wait for my assets or inferior grids to return."
)

// go back to simulate grid
goto(SimulateGrid) using gridAgentBaseData
.updateWithReceivedSlackVoltages(receivedSlackValues)
.copy(sweepValueStores = updatedSweepValueStore)
}

case failedNewtonRaphsonPFResult: FailedNewtonRaphsonPFResult =>
val powerFlowDoneData =
PowerFlowDoneData(
gridAgentBaseData,
failedNewtonRaphsonPFResult
)
log.warning(
"Power flow with updated slack voltage did finally not converge!"
)
unstashAll() // we can answer the stashed grid power requests now and report a failed power flow back
goto(SimulateGrid) using powerFlowDoneData

}
}

// happens only when we received slack data and power values before we received a request to provide grid data
Expand Down Expand Up @@ -715,7 +728,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {

/* This is the highest grid agent, therefore no data is received for the slack node. Suppress, that it is looked
* up in the empty store. */
val operationPoint = composeOperatingPoint(
val (operationPoint, slackNodeVoltages) = composeOperatingPoint(
gridModel.gridComponents.nodes,
gridModel.gridComponents.transformers,
gridModel.gridComponents.transformers3w,
Expand All @@ -739,7 +752,8 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
newtonRaphsonPF(
gridModel,
gridAgentBaseData.powerFlowParams.maxIterations,
operationPoint
operationPoint,
slackNodeVoltages
)(gridAgentBaseData.powerFlowParams.epsilon) match {
case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
log.debug(
Expand Down
Loading