Skip to content

Fixed bugs in congestion management. #1394

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added congestion detection [#1186](https://github.com/ie3-institute/simona/issues/1186)
- Added `CODEOWNERS` file [#1387](https://github.com/ie3-institute/simona/issues/1387)

## Changed
### Changed
- Upgraded `scala2` to `scala3` [#53](https://github.com/ie3-institute/simona/issues/53)
- Refactoring `GridAgentBuilder` to an object [#1372](https://github.com/ie3-institute/simona/issues/1372)
- Converted `ExtSimAdapter` to typed [#1094](https://github.com/ie3-institute/simona/issues/1094)
Expand All @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixes in Documentation, ScalaDocs, Code Style and more [#1397](https://github.com/ie3-institute/simona/issues/1397)
- Fixed variable shadowing [#1371](https://github.com/ie3-institute/simona/issues/1371)
- Updated dev's guide [#1409](https://github.com/ie3-institute/simona/issues/1409)
- Fixed bug in returned next tick after congestion management [#1393](https://github.com/ie3-institute/simona/issues/1393)
- Fixed bug in `CongestionResult` values [#1395](https://github.com/ie3-institute/simona/issues/1395)

## [4.0.0] - 2025-05-09

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ trait DCMAlgorithm extends CongestionDetection {
currentTick: Long,
results: Option[PowerFlowResultEvent],
ctx: ActorContext[Request],
)(implicit
)(using
constantData: GridAgentConstantData,
buffer: StashBuffer[Request],
): Behavior[Request] = {
Expand Down Expand Up @@ -84,7 +84,7 @@ trait DCMAlgorithm extends CongestionDetection {
private[grid] def finishCongestionManagement(
stateData: CongestionManagementData,
ctx: ActorContext[Request],
)(implicit
)(using
constantData: GridAgentConstantData,
buffer: StashBuffer[Request],
): Behavior[Request] = {
Expand All @@ -94,7 +94,7 @@ trait DCMAlgorithm extends CongestionDetection {
// return to idle
GridAgent.gotoIdle(
stateData.gridAgentBaseData,
stateData.currentTick,
stateData.currentTick + constantData.resolution,
Some(powerFlowResults),
ctx,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import edu.ie3.simona.agent.grid.congestion.{CongestedComponents, Congestions}
import edu.ie3.simona.event.ResultEvent.PowerFlowResultEvent
import edu.ie3.util.quantities.QuantityUtils.asPercent
import org.apache.pekko.actor.typed.ActorRef
import squants.Each
import tech.units.indriya.unit.Units

import java.time.ZonedDateTime
import java.util.UUID
Expand Down Expand Up @@ -61,14 +63,14 @@ final case class CongestionManagementData(
nodeRes.getInputModel,
InputModelType.NODE,
subgridNo,
nodeRes.getvMag().multiply(100),
voltageLimits.vMin.multiply(100),
voltageLimits.vMax.multiply(100),
nodeRes.getvMag().to(Units.PERCENT),
voltageLimits.vMin.to(Units.PERCENT),
voltageLimits.vMax.to(Units.PERCENT),
)
}

val lines = congestedComponents.lines.map { case (lineModel, current) =>
val utilisation = (current / lineModel.iNom).asPercent
val utilisation = Each(current / lineModel.iNom).toPercent.asPercent

new CongestionResult(
startTime.plusSeconds(currentTick),
Expand All @@ -83,7 +85,8 @@ final case class CongestionManagementData(

val transformer2W = congestedComponents.transformer2Ws.map {
case (transformerModel, power) =>
val utilisation = (power / transformerModel.sRated).asPercent
val utilisation =
Each(power / transformerModel.sRated).toPercent.asPercent

new CongestionResult(
startTime.plusSeconds(currentTick),
Expand All @@ -98,7 +101,8 @@ final case class CongestionManagementData(

val transformer3W = congestedComponents.transformer3Ws.map {
case (transformerModel, power) =>
val utilisation = (power / transformerModel.sRated).asPercent
val utilisation =
Each(power / transformerModel.sRated).toPercent.asPercent

new CongestionResult(
startTime.plusSeconds(currentTick),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Research group Distribution grid planning and operation
*/

package edu.ie3.simona.agent.grid.congestion.detection
package edu.ie3.simona.agent.grid.congestion

import edu.ie3.simona.agent.grid.congestion.CongestedComponents
import edu.ie3.simona.event.ResultEvent.PowerFlowResultEvent
Expand Down Expand Up @@ -40,7 +40,7 @@ class CongestedComponentsSpec
val congested = CongestedComponents(
results,
gridModel.gridComponents,
voltageLimits,
defaultVoltageLimits,
Kilovolts(110),
1,
)
Expand All @@ -63,7 +63,7 @@ class CongestedComponentsSpec
val congested = CongestedComponents(
results,
gridModel.gridComponents,
voltageLimits,
defaultVoltageLimits,
Kilovolts(110),
1,
)
Expand All @@ -90,7 +90,7 @@ class CongestedComponentsSpec
val congested = CongestedComponents(
results,
gridModel.gridComponents,
voltageLimits,
defaultVoltageLimits,
Kilovolts(110),
1,
)
Expand Down Expand Up @@ -121,7 +121,7 @@ class CongestedComponentsSpec
val congested = CongestedComponents(
results,
gridModel.gridComponents,
voltageLimits,
defaultVoltageLimits,
Kilovolts(110),
1,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,41 @@ package edu.ie3.simona.agent.grid.congestion

import com.typesafe.config.ConfigFactory
import edu.ie3.simona.agent.EnvironmentRefs
import edu.ie3.simona.agent.grid.GridAgent
import edu.ie3.simona.agent.grid.GridAgentData.{
GridAgentBaseData,
GridAgentConstantData,
}
import edu.ie3.simona.agent.grid.{GridAgent, GridEnvironment}
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
import edu.ie3.simona.model.grid.RefSystem
import edu.ie3.simona.ontology.messages.services.{
LoadProfileMessage,
ServiceMessage,
WeatherMessage,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.test.common.model.grid.DbfsTestGrid
import edu.ie3.simona.test.common.result.CongestedComponentsTestData
import edu.ie3.simona.test.common.{ConfigTestData, TestSpawnerTyped}
import edu.ie3.util.TimeUtil
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ActorTestKitBase,
TestProbe,
}
import org.apache.pekko.actor.typed.scaladsl.{Behaviors, StashBuffer}
import org.apache.pekko.actor.typed.scaladsl.{
ActorContext,
Behaviors,
StashBuffer,
}
import org.apache.pekko.actor.typed.{ActorRef, Behavior}
import org.mockito.Mockito.when
import squants.electro.Kilovolts
import squants.energy.Megawatts

import java.time.ZonedDateTime
import scala.concurrent.duration.DurationInt

trait CongestionTestBaseData
extends ConfigTestData
with DbfsTestGrid
with CongestedComponentsTestData
with TestSpawnerTyped {
this: ActorTestKitBase =>

Expand All @@ -50,10 +55,10 @@ trait CongestionTestBaseData
.resolve()
)

val startTime: ZonedDateTime = TimeUtil.withDefaults.toZonedDateTime(
config.simona.time.startDateTime
protected val refSystem: RefSystem = RefSystem(
Megawatts(600d),
Kilovolts(110d),
)
val endTime: ZonedDateTime = startTime.plusWeeks(1)

protected val scheduler: TestProbe[SchedulerMessage] = TestProbe("scheduler")
protected val runtimeEvents: TestProbe[RuntimeEvent] = TestProbe(
Expand Down Expand Up @@ -82,6 +87,10 @@ trait CongestionTestBaseData
"resultListener"
)

protected val gridAgentActivation: TestProbe[Activation] = TestProbe(
"gridAgentActivation"
)

protected implicit val constantData: GridAgentConstantData =
GridAgentConstantData(
environmentRefs,
Expand All @@ -90,9 +99,22 @@ trait CongestionTestBaseData
3600,
startTime,
endTime,
mock[ActorRef[Activation]],
gridAgentActivation.ref,
)

def behaviorWithContextAndBuffer(
factory: (
ctx: ActorContext[GridAgent.Request],
buffer: StashBuffer[GridAgent.Request],
) => Behavior[GridAgent.Request]
)(using
capacity: Int = 10
): Behavior[GridAgent.Request] = Behaviors.withStash(capacity) { buffer =>
Behaviors.setup { ctx =>
factory(ctx, buffer)
}
}

def spawnWithBuffer(
factory: StashBuffer[GridAgent.Request] => Behavior[GridAgent.Request],
capacity: Int = 10,
Expand All @@ -118,6 +140,14 @@ trait CongestionTestBaseData
when(data.isSuperior).thenReturn(isSuperior)
when(data.congestionManagementParams).thenReturn(cmParams)
when(data.inferiorGridRefs).thenReturn(map)
when(data.superiorGridNodeUuids).thenReturn(Vector.empty)

val gridEnv = mock[GridEnvironment]
when(data.gridEnv).thenReturn(gridEnv)

when(gridEnv.gridModel).thenReturn(gridModel)
when(gridEnv.subgridGateToActorRef).thenReturn(Map.empty)
when(gridEnv.nodeToAssetAgents).thenReturn(Map.empty)

data
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* © 2025. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/

package edu.ie3.simona.agent.grid.congestion

import edu.ie3.simona.agent.grid.GridAgent
import edu.ie3.simona.agent.grid.congestion.data.CongestionManagementData
import edu.ie3.simona.agent.grid.congestion.detection.DetectionMessages.CongestionCheckRequest
import edu.ie3.simona.event.ResultEvent.PowerFlowResultEvent
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.test.common.UnitSpec
import org.apache.pekko.actor.testkit.typed.scaladsl.{
BehaviorTestKit,
ScalaTestWithActorTestKit,
TestProbe,
}

import scala.concurrent.duration.DurationInt

class DCMAlgorithmSpec
extends ScalaTestWithActorTestKit
with UnitSpec
with CongestionTestBaseData {

"The DCMAlgorithm" should {

"start the congestion management correctly" in {
val inferiorGA = TestProbe[GridAgent.Request]("inferiorGridAgent")

val baseData = gridAgentBaseData(inferiorRefs = Set(inferiorGA.ref))

// behavior, that will start the congestion management by creating the state data and checking the result for congestions
// we need to spawn the behavior here, since we send a start message internally
testKit.spawn(
behaviorWithContextAndBuffer { (ctx, buffer) =>
GridAgent.startCongestionManagement(
baseData,
3600L,
Some(
PowerFlowResultEvent(
Iterable(nodeResult1),
Iterable.empty,
Iterable(lineResult23),
Iterable.empty,
Iterable.empty,
)
),
ctx,
)(using constantData, buffer)
}
)

// we should receive a request to check for congestions
inferiorGA.expectMessageType[CongestionCheckRequest](10.seconds)
}

"finish the congestion management correctly" in {
val stateData = CongestionManagementData(
gridAgentBaseData(),
3600,
100,
PowerFlowResultEvent(
Iterable(nodeResult1),
Iterable.empty,
Iterable(lineResult23),
Iterable.empty,
Iterable.empty,
),
Congestions(
voltageCongestions = true,
lineCongestions = false,
transformerCongestions = false,
),
CongestedComponents.empty,
)

// this will return the idle behavior of the grid agent
BehaviorTestKit(
behaviorWithContextAndBuffer { (ctx, buffer) =>
GridAgent.finishCongestionManagement(
stateData,
ctx,
)(using constantData, buffer)
}
)

// we should receive an empty result event
resultListener.expectMessageType[PowerFlowResultEvent] match {
case PowerFlowResultEvent(
nodeResults,
_,
lineResults,
_,
_,
congestionResults,
) =>
nodeResults shouldBe Iterable(nodeResult1)
lineResults shouldBe Iterable(lineResult23)
}

// we should receive a next tick of 7200
val completionMsg = scheduler.expectMessageType[Completion]
completionMsg.actor shouldBe gridAgentActivation.ref
completionMsg.newTick shouldBe Some(7200)
}

}
}
Loading