Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
34ba3f8
Bump com.github.ie3-institute:PowerSystemDataModel from 7.0.0 to 8.1.0
dependabot[bot] Aug 4, 2025
96f3e80
refactor method to derive nodes of a given voltage level from a seque…
danielfeismann Aug 5, 2025
a90624b
adapt GridSupport to new subGridNumber concept
danielfeismann Aug 5, 2025
1980577
adapt mvNodes of MvCoordinator
danielfeismann Aug 5, 2025
1023bb5
adapting SubGridHandling
danielfeismann Aug 5, 2025
f512d6a
Merge branch 'dev' into dependabot/gradle/dev/com.github.ie3-institut…
danielfeismann Oct 13, 2025
aaf9f7d
use java 21
danielfeismann Oct 13, 2025
d2f97a9
fix check for right exception
danielfeismann Oct 13, 2025
3479740
fmt
danielfeismann Oct 13, 2025
7a56c13
update gradle
danielfeismann Oct 13, 2025
286c919
fix RunGuardianSpec
danielfeismann Oct 13, 2025
2030d28
fix ci
danielfeismann Oct 13, 2025
9f83fd5
fmt
danielfeismann Oct 13, 2025
02f6638
do not update gradle and java
danielfeismann Oct 13, 2025
767f8c6
Merge branch 'dev' into dependabot/gradle/dev/com.github.ie3-institut…
danielfeismann Oct 13, 2025
761e2b1
completely remove mvNodeChanges
danielfeismann Oct 14, 2025
ae945f9
adapt node naming
danielfeismann Oct 14, 2025
af19383
roll back some changes
danielfeismann Oct 16, 2025
123c9ab
use proper subnet numbers in GridSupport
danielfeismann Oct 16, 2025
720f4b2
Merge branch 'dev' into dependabot/gradle/dev/com.github.ie3-institut…
danielfeismann Oct 16, 2025
146d2a2
Merge branch 'dev' into dependabot/gradle/dev/com.github.ie3-institut…
danielfeismann Oct 21, 2025
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ dependencies {
}

// ie³ power system data model
implementation('com.github.ie3-institute:PowerSystemDataModel:7.0.0') {
implementation('com.github.ie3-institute:PowerSystemDataModel:8.1.0') {
exclude group: 'org.slf4j'
exclude group: 'org.apache.logging.log4j'
exclude group: 'com.github.ie3-institute'
Expand Down
54 changes: 11 additions & 43 deletions src/main/scala/edu/ie3/osmogrid/guardian/run/SubGridHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
package edu.ie3.osmogrid.guardian.run

import edu.ie3.datamodel.models.input.NodeInput
import edu.ie3.datamodel.models.input.connector._
import edu.ie3.datamodel.models.input.connector.*
import edu.ie3.datamodel.models.input.connector.`type`.{
Transformer2WTypeInput,
Transformer3WTypeInput,
}
import edu.ie3.datamodel.models.input.container._
import edu.ie3.datamodel.models.input.container.*
import edu.ie3.datamodel.utils.ContainerNodeUpdateUtil
import edu.ie3.datamodel.utils.validation.ValidationUtils
import edu.ie3.osmogrid.cfg.OsmoGridConfig
import edu.ie3.osmogrid.exception.GridException
import edu.ie3.osmogrid.guardian.run.SubGridHandling._
import edu.ie3.osmogrid.guardian.run.SubGridHandling.*
import edu.ie3.osmogrid.io.input.AssetInformation
import edu.ie3.osmogrid.io.output.{GridResult, OutputRequest}
import org.apache.pekko.actor.typed.ActorRef
Expand All @@ -26,7 +26,8 @@ import tech.units.indriya.ComparableQuantity

import java.util.UUID
import javax.measure.quantity.ElectricPotential
import scala.jdk.CollectionConverters._
import javax.naming.directory.InvalidAttributesException
import scala.jdk.CollectionConverters.*
import scala.util.{Failure, Success, Try}

trait SubGridHandling {
Expand Down Expand Up @@ -129,7 +130,7 @@ trait SubGridHandling {
): Seq[GridContainer] = {
// assigning unique subgrid numbers to all given grids
val nodeUpdateMap =
updateNodeSubNetNumbers(lvData, mvData, mvNodeChanges, hvData)
updateNodeSubNetNumbers(lvData, mvData, hvData)

val lvGrids = Option.when(cfg.lv)(lvData).flatten
val mvGrids = Option.when(cfg.mv)(mvData).flatten
Expand Down Expand Up @@ -159,7 +160,6 @@ object SubGridHandling {
private def updateNodeSubNetNumbers(
lvGrids: Option[Seq[GridContainer]],
mvGrids: Option[Seq[GridContainer]],
mvNodeChanges: Option[Map[UUID, NodeInput]],
hvGrids: Option[Seq[GridContainer]],
): Map[NodeInput, NodeInput] = {
val lv = lvGrids.map(grids => assignSubNetNumbers(grids, 1))
Expand All @@ -168,48 +168,14 @@ object SubGridHandling {
val mv = mvGrids.map(grids => assignSubNetNumbers(grids, mvOffset))
val hvOffset = mv.map(_._2).getOrElse(100)

val mvCombined: Map[UUID, NodeInput] = (mv, mvNodeChanges) match {
case (Some(gridNodeChanges), Some(nodeChanges)) =>
// we need to combine multiple mv node changes
val gridNodeMap = gridNodeChanges._1

val commonKeys = nodeChanges.keySet.intersect(gridNodeMap.keySet)
val allKeys = gridNodeChanges._1.keySet ++ nodeChanges.keySet

allKeys.map { key =>
if (commonKeys.contains(key)) {
// update subnet information
key -> nodeChanges(key)
.copy()
.subnet(
gridNodeMap(key).getSubnet
)
.build()
} else key -> gridNodeMap.getOrElse(key, nodeChanges(key))
}.toMap

case (Some(gridNodeChanges), None) =>
// we only got mv grid containers
gridNodeChanges._1
case (None, Some(nodeChanges)) =>
// we only got mv node changes
nodeChanges

case _ =>
// if no mv nodes are present
Map.empty
}

val hv = hvGrids.map { grids =>
if (grids.size == 1 && grids(0).getGridName == "dummyHvGrid") {

val hvNode: NodeInput = grids(0).getRawGrid.getNodes.asScala.toList
.sortBy(
_.getVoltLvl.getNominalVoltage.getValue.doubleValue()
)
.lastOption
.getOrElse(throw GridException("No hv node found."))

(
Map(hvNode.getUuid -> hvNode.copy().subnet(hvOffset).build()),
hvOffset + 2,
Expand All @@ -220,9 +186,9 @@ object SubGridHandling {
}

// maps with updated nodes
val updateMap = lv.map(_._1).getOrElse(Map.empty) ++ mvCombined ++ hv
val updateMap = lv.map(_._1).getOrElse(Map.empty) ++ mv
.map(_._1)
.getOrElse(Map.empty)
.getOrElse(Map.empty) ++ hv.map(_._1).getOrElse(Map.empty)

val allNodes: Seq[NodeInput] = List(lvGrids, mvGrids, hvGrids).flatten
.flatMap(_.flatMap(_.getRawGrid.getNodes.asScala))
Expand Down Expand Up @@ -260,7 +226,9 @@ object SubGridHandling {
val updatedNode = if (nodeVoltage.isEquivalentTo(voltage)) {
node.copy().subnet(i + offset).build()
} else if (nodeVoltage.isGreaterThan(voltage)) {
node.copy().subnet(higherNodesOffset).build()
throw new InvalidAttributesException(
s"When assigning SubNetNumbers, a voltage of ${nodeVoltage} which is higher as the expected voltage of ${voltage} was found for node: ${node} "
)
} else node

node.getUuid -> updatedNode
Expand Down
58 changes: 39 additions & 19 deletions src/main/scala/edu/ie3/osmogrid/mv/MvCoordinator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ package edu.ie3.osmogrid.mv

import edu.ie3.datamodel.models.input.NodeInput
import edu.ie3.datamodel.models.input.container.JointGridContainer
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.osmogrid.ActorStopSupportStateless
import edu.ie3.osmogrid.cfg.OsmoGridConfig
import edu.ie3.osmogrid.guardian.run.RunGuardian
import edu.ie3.osmogrid.io.input.{InputDataEvent, ReqAssetTypes}
import edu.ie3.osmogrid.mv.MvMessageAdapters.WrappedInputResponse
import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
Expand Down Expand Up @@ -214,37 +214,57 @@ object MvCoordinator extends ActorStopSupportStateless {
) =>
ctx.log.debug(s"Starting medium voltage graph generation.")

// collect all mv node from lv sub grid containers
val mvToLv = GridContainerUtils.filterLv(lvGrids)
val mvNodesFromLvTransformers =
GridContainerUtils.filterForVoltageLvl(
lvGrids,
GermanVoltageLevelUtils.MV_10KV,
)

// collect all mv nodes from hv sub grid container or spawn a new mv node
val hvOption = hvGrids.map(GridContainerUtils.filterHv)
val mvNodesFromHvTransformers =
hvGrids.map(grid =>
GridContainerUtils
.filterForVoltageLvl(grid, GermanVoltageLevelUtils.MV_10KV)
)

// if no hv subgrid container were provided or no mv nodes could be extracted
// check if spawning a dummy node is activated
val hvToMv: Option[(JointGridContainer, NodeInput)] =
Option.when(hvOption.isEmpty && cfg.spawnMissingHvNodes)(
spawnDummyHvNode(mvToLv, assetInformation)
Option.when(
mvNodesFromHvTransformers.isEmpty && cfg.spawnMissingHvNodes
)(
spawnDummyHvNode(mvNodesFromLvTransformers, assetInformation)
)

// get the mv transition nodes
val (transitionNodes, uuidOption) = (hvOption, hvToMv) match {
case (Some(nodeList), _) =>
// found mv nodes from hv containers
(nodeList, None)
case (None, Some(value)) =>
// using a mv node with a spawned dummy hv node
(List(value._2), None)
case (None, None) =>
// using a mv node with no dummy hv node
val mvSlackNode = mvToLv(0).copy().slack(true).build()
(hvOption.getOrElse(List(mvSlackNode)), Some(mvSlackNode.getUuid))
}
val (transitionNodes, uuidOption) =
(mvNodesFromHvTransformers, hvToMv) match {
case (Some(nodeList), _) =>
// found mv nodes from hv containers
(nodeList, None)
case (None, Some(value)) =>
// using a mv node with a spawned dummy hv node
(List(value._2), None)
case (None, None) =>
// using a mv node with no dummy hv node
val mvSlackNode = mvNodesFromLvTransformers.headOption match {
case Some(node) =>
node.copy().slack(true).build()
case None =>
throw new NoSuchElementException(
"Expected to have at least one MV Node available"
)
}
(
mvNodesFromHvTransformers.getOrElse(List(mvSlackNode)),
Some(mvSlackNode.getUuid),
)
}

val (polygons, notAssignedNodes) =
VoronoiPolygonSupport.createVoronoiPolygons(
transitionNodes.toList,
mvToLv.toList,
mvNodesFromLvTransformers.toList,
ctx,
)

Expand Down
67 changes: 41 additions & 26 deletions src/main/scala/utils/GridContainerUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,62 @@

package utils

import edu.ie3.datamodel.models.input.container.*
import edu.ie3.datamodel.models.input.{AssetInput, NodeInput}
import edu.ie3.datamodel.models.input.container._
import edu.ie3.datamodel.models.voltagelevels.VoltageLevel
import edu.ie3.datamodel.models.voltagelevels.CommonVoltageLevel
import edu.ie3.osmogrid.cfg.OsmoGridConfig.Voltage
import edu.ie3.osmogrid.guardian.run.RunGuardian
import tech.units.indriya.ComparableQuantity

import javax.measure.quantity.ElectricPotential
import scala.jdk.CollectionConverters._
import scala.jdk.CollectionConverters.*

object GridContainerUtils {
private val cfg: Voltage = RunGuardian.getVoltageConfig

/** Method for retrieving all mv nodes from a sequence of lv
* [[SubGridContainer]].
* @param lvGrids
* given sub grids
/** Method for retrieving all nodes of a given voltage level from a sequence
* of [[SubGridContainer]].
*
* @param grids
* given sub or inferior grids
* @return
* all found nodes
*/
def filterLv(
lvGrids: Seq[SubGridContainer]
def filterForVoltageLvl(
grids: Seq[SubGridContainer],
voltageLvL: CommonVoltageLevel,
): Seq[NodeInput] = {
val mvVoltLvl = VoltageUtils.parse(cfg.mv)
/* gets all mv-lv nodes */
getNodes(mvVoltLvl, lvGrids)
}
grids.flatMap { grid =>
val preDominantVoltageLvl =
grid.getPredominantVoltageLevel.getNominalVoltage

/** Method for retrieving all mv nodes from a sequence of hv
* [[SubGridContainer]].
* @param hvGrids
* given sub grids
* @return
* all found nodes
*/
def filterHv(
hvGrids: Seq[SubGridContainer]
): Seq[NodeInput] = {
val mvVoltLvl = VoltageUtils.parse(cfg.mv)
/* gets all hv-mv nodes */
getNodes(mvVoltLvl, hvGrids)
// if Voltage is higher then preDominantVoltage than we must take the nodes from transformer
if (voltageLvL.getNominalVoltage.isGreaterThan(preDominantVoltageLvl)) {
// Collect nodes from 2W transformers
val nodes2WTransformers = grid.getRawGrid.getTransformer2Ws.asScala
.flatMap(transformer => transformer.allNodes().asScala)
.filter(node => voltageLvL.equals(node.getVoltLvl))

// Collect nodes from 3W transformers
val nodes3WTransformers = grid.getRawGrid.getTransformer3Ws.asScala
.flatMap(transformer => transformer.allNodes().asScala)
.filter(node => voltageLvL.equals(node.getVoltLvl))

// Combine the results from both transformers
nodes2WTransformers ++ nodes3WTransformers

} else if (
voltageLvL.getNominalVoltage.isLessThanOrEqualTo(preDominantVoltageLvl)
) {
// Collect all nodes directly from the raw grid
grid.getRawGrid.getNodes.asScala.filter(node =>
voltageLvL.equals(node.getVoltLvl)
)

} else {
Seq.empty[NodeInput] // Return an empty sequence if no conditions match.
}
}
}

/** Method to return all [[NodeInput]]'s of all given [[SubGridContainer]]
Expand Down
22 changes: 19 additions & 3 deletions src/test/scala/edu/ie3/osmogrid/guardian/run/RunGuardianSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,26 @@ class RunGuardianSpec extends ScalaTestWithActorTestKit with UnitSpec {
)
}

"handle all received grid results" in {
runningTestKit.run(HandleGridResults)
"handle all received grid results" in new GridSupport {
val lvGrids: Seq[SubGridContainer] = Seq(mockSubGrid(1))
val mvGrids: Seq[SubGridContainer] = Seq(mockSubGrid(3))
val streetGraph: OsmGraph = new OsmGraph()

/* Result is forwarded to listener */
// LV first
runningTestKit.run(
MessageAdapters.WrappedLvCoordinatorResponse(
RepLvGrids(lvGrids, streetGraph)
)
)

// MV
runningTestKit.run(
MessageAdapters.WrappedMvCoordinatorResponse(
RepMvGrids(mvGrids, None, Map.empty, assetInformation)
)
)

runningTestKit.run(HandleGridResults)
resultListener.expectMessageType[GridResult]
}

Expand Down
Loading