diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f51a4c..e72b85b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved `SubGridHandling` [#397](https://github.com/ie3-institute/OSMoGrid/issues/397) - Switched from `osm4scala` to `openstreetmap.osmosis` [#409](https://github.com/ie3-institute/OSMoGrid/issues/409) - Changed transformer input parameter to PSDM requirements [#417](https://github.com/ie3-institute/OSMoGrid/issues/417) +- Adapted run initialization [#404](https://github.com/ie3-institute/OSMoGrid/issues/404) ### Fixed - Fixed bug in `LvGridGeneratorSupport` [#388](https://github.com/ie3-institute/OSMoGrid/issues/388) diff --git a/input/fuerweiler/fuerweiler.conf b/input/fuerweiler/fuerweiler.conf index 3bcb046d..da188d96 100644 --- a/input/fuerweiler/fuerweiler.conf +++ b/input/fuerweiler/fuerweiler.conf @@ -14,6 +14,10 @@ output.csv.directory = "output/fuerweiler" output.csv.separator = "," output.csv.hierarchic = false +grids.output.lv = true +grids.output.mv = true +grids.output.hv = true + ################################################################## # Voltage parameters ################################################################## diff --git a/src/main/resources/config/config-template.conf b/src/main/resources/config/config-template.conf index 9c95946a..90347d24 100644 --- a/src/main/resources/config/config-template.conf +++ b/src/main/resources/config/config-template.conf @@ -22,6 +22,12 @@ output: { } } +grids.output: { + lv: Boolean | true + mv: Boolean | true + hv: Boolean | true +} + voltage: { lv: { id: String | "lv" diff --git a/src/main/scala/edu/ie3/osmogrid/cfg/ConfigFailFast.scala b/src/main/scala/edu/ie3/osmogrid/cfg/ConfigFailFast.scala index 6117e389..35561f6f 100644 --- a/src/main/scala/edu/ie3/osmogrid/cfg/ConfigFailFast.scala +++ b/src/main/scala/edu/ie3/osmogrid/cfg/ConfigFailFast.scala @@ -10,7 +10,13 @@ import org.apache.pekko.actor.typed.ActorRef import com.typesafe.scalalogging.LazyLogging import edu.ie3.osmogrid.cfg.OsmoGridConfig.Generation.{Lv, Mv} import edu.ie3.osmogrid.cfg.OsmoGridConfig.Input.{Asset, Osm} -import edu.ie3.osmogrid.cfg.OsmoGridConfig.{Generation, Input, Output, Voltage} +import edu.ie3.osmogrid.cfg.OsmoGridConfig.{ + Generation, + Grids, + Input, + Output, + Voltage +} import edu.ie3.osmogrid.exception.IllegalConfigException import edu.ie3.osmogrid.io.input.BoundaryAdminLevel import edu.ie3.osmogrid.io.output.OutputRequest @@ -23,10 +29,11 @@ object ConfigFailFast extends LazyLogging { additionalListener: Seq[ActorRef[OutputRequest]] = Seq.empty ): Try[OsmoGridConfig] = Try { cfg match { - case OsmoGridConfig(generation, input, output, voltage) => + case OsmoGridConfig(generation, grids, input, output, voltage) => checkInputConfig(input) checkOutputConfig(output, additionalListener) checkGenerationConfig(generation) + checkGridsConfig(grids) checkVoltageConfig(voltage) } cfg @@ -155,6 +162,19 @@ object ConfigFailFast extends LazyLogging { "Output directory and separator must be set when using .csv file sink!" ) + private def checkGridsConfig(grids: Grids): Unit = { + grids.output match { + case Grids.Output(false, false, false) => + // at least one output must be set + throw IllegalConfigException(s"No grid output defined.") + + case Grids.Output(true, false, false) => + logger.warn(s"Only hv output is currently not supported!") + case _ => + + } + } + // TODO: Check if necessary private def checkVoltageConfig(voltage: Voltage): Unit = {} } diff --git a/src/main/scala/edu/ie3/osmogrid/cfg/OsmoGridConfig.scala b/src/main/scala/edu/ie3/osmogrid/cfg/OsmoGridConfig.scala index 02ef78c0..1bdb7576 100644 --- a/src/main/scala/edu/ie3/osmogrid/cfg/OsmoGridConfig.scala +++ b/src/main/scala/edu/ie3/osmogrid/cfg/OsmoGridConfig.scala @@ -1,5 +1,5 @@ /* - * © 2023. TU Dortmund University, + * © 2024. TU Dortmund University, * Institute of Energy Systems, Energy Efficiency and Energy Economics, * Research group Distribution grid planning and operation */ @@ -8,6 +8,7 @@ package edu.ie3.osmogrid.cfg final case class OsmoGridConfig( generation: OsmoGridConfig.Generation, + grids: OsmoGridConfig.Grids, input: OsmoGridConfig.Input, output: OsmoGridConfig.Output, voltage: OsmoGridConfig.Voltage @@ -184,6 +185,45 @@ object OsmoGridConfig { } } + final case class Grids( + output: OsmoGridConfig.Grids.Output + ) + object Grids { + final case class Output( + hv: scala.Boolean, + lv: scala.Boolean, + mv: scala.Boolean + ) + object Output { + def apply( + c: com.typesafe.config.Config, + parentPath: java.lang.String, + $tsCfgValidator: $TsCfgValidator + ): OsmoGridConfig.Grids.Output = { + OsmoGridConfig.Grids.Output( + hv = !c.hasPathOrNull("hv") || c.getBoolean("hv"), + lv = !c.hasPathOrNull("lv") || c.getBoolean("lv"), + mv = !c.hasPathOrNull("mv") || c.getBoolean("mv") + ) + } + } + + def apply( + c: com.typesafe.config.Config, + parentPath: java.lang.String, + $tsCfgValidator: $TsCfgValidator + ): OsmoGridConfig.Grids = { + OsmoGridConfig.Grids( + output = OsmoGridConfig.Grids.Output( + if (c.hasPathOrNull("output")) c.getConfig("output") + else com.typesafe.config.ConfigFactory.parseString("output{}"), + parentPath + "output.", + $tsCfgValidator + ) + ) + } + } + final case class Input( asset: OsmoGridConfig.Input.Asset, osm: OsmoGridConfig.Input.Osm @@ -344,7 +384,7 @@ object OsmoGridConfig { hierarchic = c.hasPathOrNull("hierarchic") && c.getBoolean("hierarchic"), separator = - if (c.hasPathOrNull("separator")) c.getString("separator") else ";" + if (c.hasPathOrNull("separator")) c.getString("separator") else "," ) } private def $_reqStr( @@ -518,6 +558,12 @@ object OsmoGridConfig { parentPath + "generation.", $tsCfgValidator ), + grids = OsmoGridConfig.Grids( + if (c.hasPathOrNull("grids")) c.getConfig("grids") + else com.typesafe.config.ConfigFactory.parseString("grids{}"), + parentPath + "grids.", + $tsCfgValidator + ), input = OsmoGridConfig.Input( if (c.hasPathOrNull("input")) c.getConfig("input") else com.typesafe.config.ConfigFactory.parseString("input{}"), diff --git a/src/main/scala/edu/ie3/osmogrid/guardian/run/RunGuardian.scala b/src/main/scala/edu/ie3/osmogrid/guardian/run/RunGuardian.scala index a55b80e8..18b542d5 100644 --- a/src/main/scala/edu/ie3/osmogrid/guardian/run/RunGuardian.scala +++ b/src/main/scala/edu/ie3/osmogrid/guardian/run/RunGuardian.scala @@ -74,14 +74,19 @@ object RunGuardian ctx ) match { case Success(childReferences) => - running( - runGuardianData, - childReferences, - FinishedGridData.empty( - childReferences.lvCoordinator.isDefined, - childReferences.mvCoordinator.isDefined + if (childReferences.canRun) { + running( + runGuardianData, + childReferences, + FinishedGridData.empty ) - ) + } else { + // no coordinator alive, stop generation + ctx.log.warn( + s"No coordinators were spawned. Generation can't run!" + ) + Behaviors.stopped + } case Failure(exception) => ctx.log.error( s"Unable to start run ${runGuardianData.runId}.", @@ -131,15 +136,10 @@ object RunGuardian ) } - // store sub grids if they should be put out - val option = if (finishedGridData.lvExpected) { - Some(subGridContainer) - } else None - - val updated = finishedGridData.copy(lvData = option) + val updated = finishedGridData.copy(lvData = Some(subGridContainer)) // check if all possible data was received - if (updated.receivedAllData) { + if (!updatedChildReferences.stillRunning) { // if all data was received, ctx.self ! HandleGridResults @@ -167,22 +167,15 @@ object RunGuardian childReferences.mvCoordinator.foreach(ctx.unwatch) val updatedChildReferences = childReferences.copy(mvCoordinator = None) - // store sub grids if they should be put out - val option = if (finishedGridData.mvExpected) { - Some(subGridContainer) - } else None - - val nodeUpdates = Option.when(nodeChanges.nonEmpty)(nodeChanges) - val updated = finishedGridData.copy( - mvData = option, + mvData = Some(subGridContainer), hvData = dummyHvGrid.map(Seq(_)), // converting to sequence - mvNodeChanges = nodeUpdates, + mvNodeChanges = Option.when(nodeChanges.nonEmpty)(nodeChanges), assetInformation = Some(assetInformation) ) // check if all possible data was received - if (updated.receivedAllData) { + if (!updatedChildReferences.stillRunning) { // if all data was received, ctx.self ! HandleGridResults @@ -197,6 +190,7 @@ object RunGuardian ctx.log.info(s"Starting to handle grid results.") handleResults( + runGuardianData.cfg.grids.output, finishedGridData.lvData, finishedGridData.mvData, finishedGridData.hvData, diff --git a/src/main/scala/edu/ie3/osmogrid/guardian/run/RunSupport.scala b/src/main/scala/edu/ie3/osmogrid/guardian/run/RunSupport.scala index b524e2a0..2c5350f0 100644 --- a/src/main/scala/edu/ie3/osmogrid/guardian/run/RunSupport.scala +++ b/src/main/scala/edu/ie3/osmogrid/guardian/run/RunSupport.scala @@ -6,21 +6,34 @@ package edu.ie3.osmogrid.guardian.run -import org.apache.pekko.actor.typed.ActorRef -import org.apache.pekko.actor.typed.scaladsl.ActorContext -import edu.ie3.osmogrid.cfg.OsmoGridConfig.{Generation, Output} +import edu.ie3.osmogrid.cfg.OsmoGridConfig.Generation.Lv.{ + BoundaryAdminLevel, + Osm +} +import edu.ie3.osmogrid.cfg.OsmoGridConfig.Generation.{Lv, Mv} +import edu.ie3.osmogrid.cfg.OsmoGridConfig.Output import edu.ie3.osmogrid.cfg.{ConfigFailFast, OsmoGridConfig} -import edu.ie3.osmogrid.exception.UnsupportedRequestException import edu.ie3.osmogrid.io.input.{InputDataEvent, InputDataProvider} import edu.ie3.osmogrid.io.output.{ResultListener, ResultListenerProtocol} import edu.ie3.osmogrid.lv.{LvCoordinator, LvRequest, LvResponse, ReqLvGrids} import edu.ie3.osmogrid.mv.{MvCoordinator, MvRequest, MvResponse, ReqMvGrids} +import org.apache.pekko.actor.typed.ActorRef +import org.apache.pekko.actor.typed.scaladsl.ActorContext import java.util.UUID -import scala.util.{Failure, Success, Try} +import scala.util.{Success, Try} trait RunSupport { + private val lvFallback: Lv = Lv( + averagePowerDensity = 12.5, // W/m^2 + boundaryAdminLevel = BoundaryAdminLevel(lowest = 8, starting = 2), + considerHouseConnectionPoints = false, + loadSimultaneousFactor = 0.2, + minDistance = 10, + osm = Osm(None) + ) + /** Initiate a generation run and return the updated run meta data * * @param runGuardianData @@ -45,60 +58,72 @@ trait RunSupport { s"Initializing grid generation for run with id '${runGuardianData.runId}'!" ) - /* Check, which voltage level configs are given. Start with lv level, if this is desired for. */ - // TODO: Re-do this part! - validConfig.generation match { - case Generation(Some(lvConfig), Some(mvConfig)) => - ctx.log.info("Starting low voltage grid coordinator ...") - val inputProvider = - spawnInputDataProvider( - runGuardianData.runId, - validConfig.input, - ctx - ) - - // spin up listeners, watch them and wait until they terminate in this state - val resultListener = spawnResultListener( - runGuardianData.runId, - runGuardianData.cfg.output, - ctx - ) + val gridOutput = validConfig.grids.output + val generation = validConfig.generation - val lvCoordinator = startLvGridGeneration( - runGuardianData.runId, - inputProvider, - lvConfig, - runGuardianData.msgAdapters.lvCoordinator, - ctx - ) + val mvFallback = if (gridOutput.hv) { + Mv(true) + } else Mv(false) - val mvCoordinator = startMvGridGeneration( - runGuardianData.runId, - inputProvider, - mvConfig, - runGuardianData.msgAdapters.mvCoordinator, - ctx + val lv = if (gridOutput.lv || gridOutput.mv) { + generation.lv.orElse { + ctx.log.info( + s"No lv generation config found! Using fallback lv config." ) + Some(lvFallback) + } + } else None - Success( - ChildReferences( - inputProvider, - resultListener, - runGuardianData.additionalListener, - Some(lvCoordinator), - Some(mvCoordinator) - ) - ) - case unsupported => - ctx.log.error( - s"Received unsupported grid generation config '$unsupported'. Stopping run with id '${runGuardianData.runId}'!" - ) - Failure( - UnsupportedRequestException( - s"Unable to issue a generation run with the given parameters: '${validConfig.generation}'" - ) + val mv = if (gridOutput.mv) { + generation.mv.orElse { + ctx.log.info( + s"No mv generation config found! Using fallback mv config." ) + Some(mvFallback) + } + } else None + + val id: UUID = runGuardianData.runId + val msgAdapters = runGuardianData.msgAdapters + + ctx.log.info("Starting input provider ...") + implicit val inputProvider: ActorRef[InputDataEvent] = + spawnInputDataProvider(id, validConfig.input, ctx) + + // spin up listeners, watch them and wait until they terminate in this state + val resultListener = + spawnResultListener(id, runGuardianData.cfg.output, ctx) + + /* Check, which voltage level configs are given. Start with lv level, if this is desired for. */ + // spin up lv coordinator if a config is given + val lvCoordinator = lv.map { cfg => + ctx.log.info("Starting low voltage grid coordinator ...") + startLvGridGeneration(cfg, msgAdapters.lvCoordinator, id, ctx) + } + + // spin up mv coordinator if a config is given + val mvCoordinator = mv.map { cfg => + ctx.log.info("Starting medium voltage grid coordinator ...") + + // updating the mv generation config + val updatedConfig = validConfig.copy(generation.copy(mv = Some(cfg))) + startMvGridGeneration( + updatedConfig, + msgAdapters.mvCoordinator, + id, + ctx + ) } + + Success( + ChildReferences( + inputProvider, + resultListener, + runGuardianData.additionalListener, + lvCoordinator, + mvCoordinator + ) + ) } } @@ -165,24 +190,25 @@ trait RunSupport { /** Spawn a [[LvCoordinator]] for the targeted run and ask it to start * conversion + * @param lvConfig + * Configuration for low voltage grid generation * + * @param lvCoordinatorAdapter + * Message adapter to understand responses from [[LvCoordinator]] * @param runId * Identifier for the targeted run * @param inputDataProvider * Reference to the [[InputDataProvider]] for this run - * @param lvConfig - * Configuration for low voltage grid generation - * @param lvCoordinatorAdapter - * Message adapter to understand responses from [[LvCoordinator]] * @param ctx * Current actor context */ private def startLvGridGeneration( - runId: UUID, - inputDataProvider: ActorRef[InputDataEvent], lvConfig: OsmoGridConfig.Generation.Lv, lvCoordinatorAdapter: ActorRef[LvResponse], + runId: UUID, ctx: ActorContext[RunRequest] + )(implicit + inputDataProvider: ActorRef[InputDataEvent] ): ActorRef[LvRequest] = { val lvCoordinator = ctx.spawn( @@ -198,23 +224,25 @@ trait RunSupport { } /** Spawn a [[MvCoordinator]] for the targeted run and ask it to start. - * @param runId - * identifier for the targeted run + * * @param mvConfig * configuration for medium voltage grid generation * @param mvCoordinatorAdapter * message adapter to understand responses from [[MvCoordinator]] + * @param runId + * identifier for the targeted run * @param ctx * current actor context * @return * an [[ActorRef]] for a [[MvRequest]] */ private def startMvGridGeneration( - runId: UUID, - inputDataProvider: ActorRef[InputDataEvent], - mvConfig: OsmoGridConfig.Generation.Mv, + mvConfig: OsmoGridConfig, mvCoordinatorAdapter: ActorRef[MvResponse], + runId: UUID, ctx: ActorContext[RunRequest] + )(implicit + inputDataProvider: ActorRef[InputDataEvent] ): ActorRef[MvRequest] = { val mvCoordinator = ctx.spawn( diff --git a/src/main/scala/edu/ie3/osmogrid/guardian/run/SubGridHandling.scala b/src/main/scala/edu/ie3/osmogrid/guardian/run/SubGridHandling.scala index b6f7a17f..ee530dc3 100644 --- a/src/main/scala/edu/ie3/osmogrid/guardian/run/SubGridHandling.scala +++ b/src/main/scala/edu/ie3/osmogrid/guardian/run/SubGridHandling.scala @@ -15,6 +15,7 @@ import edu.ie3.datamodel.models.input.connector.`type`.{ 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.io.input.AssetInformation @@ -49,6 +50,7 @@ trait SubGridHandling { * logger */ protected def handleResults( + cfg: OsmoGridConfig.Grids.Output, lvData: Option[Seq[SubGridContainer]], mvData: Option[Seq[SubGridContainer]], hvData: Option[Seq[GridContainer]], @@ -60,7 +62,14 @@ trait SubGridHandling { log.info("All requested grids successfully generated.") val allGrids: Seq[GridContainer] = - processResults(lvData, mvData, hvData, mvNodeChanges, assetInformation) + processResults( + cfg, + lvData, + mvData, + hvData, + mvNodeChanges, + assetInformation + ) val jointGrid = if (allGrids.isEmpty) { throw GridException( @@ -111,6 +120,7 @@ trait SubGridHandling { * a sequence of [[SubGridContainer]] */ protected def processResults( + cfg: OsmoGridConfig.Grids.Output, lvData: Option[Seq[SubGridContainer]], mvData: Option[Seq[SubGridContainer]], hvData: Option[Seq[GridContainer]], @@ -121,8 +131,12 @@ trait SubGridHandling { val nodeUpdateMap = updateNodeSubNetNumbers(lvData, mvData, mvNodeChanges, hvData) + val lvGrids = Option.when(cfg.lv)(lvData).flatten + val mvGrids = Option.when(cfg.mv)(mvData).flatten + val hvGrids = Option.when(cfg.hv)(hvData).flatten + // updating all grids with the updated nodes - Seq(lvData, mvData, hvData).flatten.flatten.map(grid => + Seq(lvGrids, mvGrids, hvGrids).flatten.flatten.map(grid => ContainerNodeUpdateUtil.updateGridWithNodes(grid, nodeUpdateMap.asJava) ) } diff --git a/src/main/scala/edu/ie3/osmogrid/guardian/run/run.scala b/src/main/scala/edu/ie3/osmogrid/guardian/run/run.scala index e4898b90..3f74af0b 100644 --- a/src/main/scala/edu/ie3/osmogrid/guardian/run/run.scala +++ b/src/main/scala/edu/ie3/osmogrid/guardian/run/run.scala @@ -86,6 +86,14 @@ private[run] final case class ChildReferences( resultListener .map(Seq(_)) .getOrElse(Seq.empty) ++ additionalResultListeners + + /** Returns true if at least one coordinator exists. + */ + def canRun: Boolean = lvCoordinator.isDefined || mvCoordinator.isDefined + + /** Returns true if at least one coordinator is still alive and running. + */ + def stillRunning: Boolean = lvCoordinator.isDefined || mvCoordinator.isDefined } sealed trait StateData @@ -124,25 +132,16 @@ private[run] final case class StoppingData( } final case class FinishedGridData( - lvExpected: Boolean, - mvExpected: Boolean, lvData: Option[Seq[SubGridContainer]], mvData: Option[Seq[SubGridContainer]], hvData: Option[Seq[GridContainer]], mvNodeChanges: Option[Map[UUID, NodeInput]], assetInformation: Option[AssetInformation] -) extends StateData { - def receivedAllData: Boolean = { - val lv = lvExpected == lvData.isDefined - val mv = mvExpected == mvData.isDefined - - lv && mv - } -} +) extends StateData object FinishedGridData { - def empty(lvExpected: Boolean, mvExpected: Boolean): FinishedGridData = - FinishedGridData(lvExpected, mvExpected, None, None, None, None, None) + def empty: FinishedGridData = + FinishedGridData(None, None, None, None, None) } /** Message to tell the [[RunGuardian]] to start handling the received results. diff --git a/src/main/scala/edu/ie3/osmogrid/mv/MvCoordinator.scala b/src/main/scala/edu/ie3/osmogrid/mv/MvCoordinator.scala index 59b5c1b0..537e299e 100644 --- a/src/main/scala/edu/ie3/osmogrid/mv/MvCoordinator.scala +++ b/src/main/scala/edu/ie3/osmogrid/mv/MvCoordinator.scala @@ -16,7 +16,7 @@ import edu.ie3.osmogrid.mv.MvMessageAdapters.WrappedInputResponse import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors} import org.apache.pekko.actor.typed.{ActorRef, Behavior, PostStop} import utils.OsmoGridUtils.spawnDummyHvNode -import utils.{GridContainerUtils, VoronoiUtils} +import utils.GridContainerUtils import scala.util.{Failure, Success} @@ -36,7 +36,7 @@ object MvCoordinator extends ActorStopSupportStateless { * the idle state */ def apply( - cfg: OsmoGridConfig.Generation.Mv, + cfg: OsmoGridConfig, inputDataProvider: ActorRef[InputDataEvent], runGuardian: ActorRef[MvResponse] ): Behavior[MvRequest] = Behaviors.setup[MvRequest] { context => @@ -56,7 +56,7 @@ object MvCoordinator extends ActorStopSupportStateless { * a new behaviour */ private def idle( - cfg: OsmoGridConfig.Generation.Mv, + cfg: OsmoGridConfig, inputDataProvider: ActorRef[InputDataEvent], messageAdapters: MvMessageAdapters, runGuardian: ActorRef[MvResponse] @@ -164,7 +164,9 @@ object MvCoordinator extends ActorStopSupportStateless { Some(hv) case (Some(hv), false) => Some(hv) case (None, false) => - throw new RuntimeException("Hv grids are needed and missing!") + if (awaitingInputData.hvGridsRequired) { + throw new RuntimeException("Hv grids are needed and missing!") + } else None case (None, true) => ctx.log.debug( "No hv grids are provided! A new hv node will be spawned." @@ -240,7 +242,7 @@ object MvCoordinator extends ActorStopSupportStateless { } val (polygons, notAssignedNodes) = - VoronoiUtils.createVoronoiPolygons( + VoronoiPolygonSupport.createVoronoiPolygons( transitionNodes.toList, mvToLv.toList, ctx diff --git a/src/main/scala/utils/MvUtils.scala b/src/main/scala/edu/ie3/osmogrid/mv/MvGraphGeneratorSupport.scala similarity index 86% rename from src/main/scala/utils/MvUtils.scala rename to src/main/scala/edu/ie3/osmogrid/mv/MvGraphGeneratorSupport.scala index f3e8a74a..08904295 100644 --- a/src/main/scala/utils/MvUtils.scala +++ b/src/main/scala/edu/ie3/osmogrid/mv/MvGraphGeneratorSupport.scala @@ -4,26 +4,24 @@ * Research group Distribution grid planning and operation */ -package utils +package edu.ie3.osmogrid.mv import edu.ie3.datamodel.models.input.NodeInput import edu.ie3.osmogrid.graph.OsmGraph +import edu.ie3.osmogrid.mv.VoronoiPolygonSupport.VoronoiPolygon import edu.ie3.util.osm.model.OsmEntity.Node import org.slf4j.{Logger, LoggerFactory} -import utils.Connections.{ - buildUndirectedConnections, - buildUndirectedShortestPathConnections -} +import utils.Connections.buildUndirectedConnections import utils.GridConversion.NodeConversion import utils.OsmoGridUtils.getAllUniqueCombinations -import utils.VoronoiUtils.VoronoiPolygon +import utils.{Connections, Solver} import scala.jdk.CollectionConverters.CollectionHasAsScala /** Utility object for mv generation. */ -object MvUtils { - val log: Logger = LoggerFactory.getLogger(MvUtils.getClass) +object MvGraphGeneratorSupport { + val log: Logger = LoggerFactory.getLogger(MvGraphGeneratorSupport.getClass) /** Utility method for creating [[NodeConversion]] and [[Connections]] * utilities.

NOTICE: for correct operation -> number of vertexes >= @@ -80,7 +78,10 @@ object MvUtils { // creating necessary utility objects val (nodeConversion, connections) = - MvUtils.createDefinitions(voronoiPolygon.allNodes, reducedStreetGraph) + MvGraphGeneratorSupport.createDefinitions( + voronoiPolygon.allNodes, + reducedStreetGraph + ) val transitionNode: Node = nodeConversion.getOsmNode( voronoiPolygon.transitionPointToHigherVoltLvl diff --git a/src/main/scala/edu/ie3/osmogrid/mv/VoronoiCoordinator.scala b/src/main/scala/edu/ie3/osmogrid/mv/VoronoiCoordinator.scala index ba147b36..c062b803 100644 --- a/src/main/scala/edu/ie3/osmogrid/mv/VoronoiCoordinator.scala +++ b/src/main/scala/edu/ie3/osmogrid/mv/VoronoiCoordinator.scala @@ -13,8 +13,8 @@ import edu.ie3.datamodel.models.input.container.SubGridContainer import edu.ie3.osmogrid.ActorStopSupportStateless import edu.ie3.osmogrid.mv.MvGridGeneratorSupport.buildGrid import utils.GridConversion -import utils.MvUtils.generateMvGraph -import utils.VoronoiUtils.VoronoiPolygon +import MvGraphGeneratorSupport.generateMvGraph +import VoronoiPolygonSupport.VoronoiPolygon /** Coordinator for [[VoronoiPolygon]]s. This actor will generate a mv graph * structure and convert the structure into a [[SubGridContainer]] and a diff --git a/src/main/scala/utils/VoronoiUtils.scala b/src/main/scala/edu/ie3/osmogrid/mv/VoronoiPolygonSupport.scala similarity index 99% rename from src/main/scala/utils/VoronoiUtils.scala rename to src/main/scala/edu/ie3/osmogrid/mv/VoronoiPolygonSupport.scala index 88254fa0..e03586db 100644 --- a/src/main/scala/utils/VoronoiUtils.scala +++ b/src/main/scala/edu/ie3/osmogrid/mv/VoronoiPolygonSupport.scala @@ -4,12 +4,12 @@ * Research group Distribution grid planning and operation */ -package utils +package edu.ie3.osmogrid.mv -import org.apache.pekko.actor.typed.scaladsl.ActorContext import edu.ie3.datamodel.models.input.NodeInput import edu.ie3.util.exceptions.GeoException import edu.ie3.util.geo.GeoUtils +import org.apache.pekko.actor.typed.scaladsl.ActorContext import org.locationtech.jts.geom.{Coordinate, Polygon} import org.locationtech.jts.triangulate.VoronoiDiagramBuilder import org.slf4j.Logger @@ -18,7 +18,7 @@ import scala.collection.parallel.CollectionConverters.ImmutableIterableIsParalle import scala.jdk.CollectionConverters._ // TODO: Parts of this or maybe all of this could be moved to GeoUtils (PowerSystemUtils). -object VoronoiUtils { +object VoronoiPolygonSupport { /** A voronoi polygons. * diff --git a/src/main/scala/edu/ie3/osmogrid/mv/mv.scala b/src/main/scala/edu/ie3/osmogrid/mv/mv.scala index efa562db..645c82b2 100644 --- a/src/main/scala/edu/ie3/osmogrid/mv/mv.scala +++ b/src/main/scala/edu/ie3/osmogrid/mv/mv.scala @@ -13,6 +13,7 @@ import edu.ie3.datamodel.models.input.container.{ } import edu.ie3.osmogrid.cfg.OsmoGridConfig import edu.ie3.osmogrid.exception.{ + IllegalConfigException, IllegalStateException, RequestFailedException } @@ -26,7 +27,7 @@ import edu.ie3.osmogrid.mv.MvMessageAdapters.WrappedInputResponse import org.apache.pekko.actor.typed.ActorRef import org.slf4j.Logger import utils.GridConversion.NodeConversion -import utils.VoronoiUtils.VoronoiPolygon +import VoronoiPolygonSupport.VoronoiPolygon import java.util.UUID import scala.util.{Failure, Success, Try} @@ -184,6 +185,8 @@ private[mv] object MvMessageAdapters { * * @param cfg * config for mv generation + * @param hvGridsRequired + * true if we either need a dummy hv node or actual hv grids * @param runGuardian * superior actor * @param lvGrids @@ -197,6 +200,7 @@ private[mv] object MvMessageAdapters { */ private[mv] final case class AwaitingInputData( cfg: OsmoGridConfig.Generation.Mv, + hvGridsRequired: Boolean, runGuardian: ActorRef[MvResponse], lvGrids: Option[Seq[SubGridContainer]], hvGrids: Option[Seq[SubGridContainer]], @@ -238,10 +242,14 @@ private[mv] final case class AwaitingInputData( val needed = lvGrids.isDefined && streetGraph.isDefined && assetInformation.isDefined - if (cfg.spawnMissingHvNodes) { - needed + if (hvGridsRequired) { + if (cfg.spawnMissingHvNodes) { + needed + } else { + needed && hvGrids.isDefined + } } else { - needed && hvGrids.isDefined + needed } } } @@ -258,10 +266,20 @@ private[mv] object AwaitingInputData { * an empty [[AwaitingInputData]] */ def empty( - cfg: OsmoGridConfig.Generation.Mv, + cfg: OsmoGridConfig, runGuardian: ActorRef[MvResponse] ): AwaitingInputData = - AwaitingInputData(cfg, runGuardian, None, None, None, None) + AwaitingInputData( + cfg.generation.mv.getOrElse( + throw IllegalConfigException(s"No medium voltage config found in $cfg!") + ), + cfg.grids.output.hv, + runGuardian, + None, + None, + None, + None + ) } /** Class for medium voltage result data. diff --git a/src/test/resources/test_config.conf b/src/test/resources/test_config.conf index 9a15673c..680c304f 100644 --- a/src/test/resources/test_config.conf +++ b/src/test/resources/test_config.conf @@ -6,6 +6,11 @@ input.asset.file { } output.csv.directory = "output_file_path" output.gridName = "test_grid" + +grids.output.lv = true +grids.output.mv = true +grids.output.hv = true + generation.lv { averagePowerDensity = 12.5 ratedVoltage = 0.4 diff --git a/src/test/scala/edu/ie3/osmogrid/cfg/ConfigFailFastSpec.scala b/src/test/scala/edu/ie3/osmogrid/cfg/ConfigFailFastSpec.scala index 549afef9..6b6ec53d 100644 --- a/src/test/scala/edu/ie3/osmogrid/cfg/ConfigFailFastSpec.scala +++ b/src/test/scala/edu/ie3/osmogrid/cfg/ConfigFailFastSpec.scala @@ -166,7 +166,8 @@ class ConfigFailFastSpec extends UnitSpec { OsmoGridConfigFactory.parseWithoutFallback { viableConfigurationString.stripMargin } match { - case Success(cfg) => ConfigFailFast.check(cfg) shouldBe Success(cfg) + case Success(cfg) => + ConfigFailFast.check(cfg) shouldBe Success(cfg) case Failure(exception) => fail(s"Config generation failed with an exception: '$exception'") } @@ -183,6 +184,9 @@ object ConfigFailFastSpec { |input.asset.file.hierarchic = false |output.csv.directory = "output_file_path" |output.gridName = "test_grid" + |grids.output.lv = true + |grids.output.mv = true + |grids.output.hv = true |voltage.lv.id = "lv" |voltage.lv.default = 0.4 |voltage.mv.id = "mv" diff --git a/src/test/scala/edu/ie3/osmogrid/guardian/run/RunGuardianSpec.scala b/src/test/scala/edu/ie3/osmogrid/guardian/run/RunGuardianSpec.scala index 7cdea035..4da910d1 100644 --- a/src/test/scala/edu/ie3/osmogrid/guardian/run/RunGuardianSpec.scala +++ b/src/test/scala/edu/ie3/osmogrid/guardian/run/RunGuardianSpec.scala @@ -112,16 +112,27 @@ class RunGuardianSpec extends ScalaTestWithActorTestKit with UnitSpec { } idleTestKit .expectEffectType[WatchedWith[ResultListenerProtocol, RunWatch]] + idleTestKit.expectEffectPF { case Spawned(_: Behavior[_], name, _) => name shouldBe s"LvCoordinator_$runId" } idleTestKit.expectEffectType[WatchedWith[LvRequest, RunWatch]] + idleTestKit.expectEffectPF { case Spawned(_: Behavior[_], name, _) => + name shouldBe s"MvCoordinator_$runId" + } + idleTestKit.expectEffectType[WatchedWith[MvRequest, RunWatch]] + /* Check for child messages */ idleTestKit .childInbox[LvRequest](s"LvCoordinator_$runId") .receiveAll() .contains(ReqLvGrids) shouldBe true + + idleTestKit + .childInbox[MvRequest](s"MvCoordinator_$runId") + .receiveAll() + .contains(ReqMvGrids) shouldBe true } } @@ -151,8 +162,7 @@ class RunGuardianSpec extends ScalaTestWithActorTestKit with UnitSpec { Some(lvCoordinator.ref), Some(mvCoordinator.ref) ) - val finishedGridData = - FinishedGridData.empty(lvExpected = true, mvExpected = true) + val finishedGridData = FinishedGridData.empty val runningTestKit = BehaviorTestKit( RunGuardian invokePrivate running( @@ -313,8 +323,7 @@ class RunGuardianSpec extends ScalaTestWithActorTestKit with UnitSpec { Some(lvCoordinator.ref), Some(mvCoordinator.ref) ) - val finishedGridData = - FinishedGridData.empty(lvExpected = true, mvExpected = true) + val finishedGridData = FinishedGridData.empty val runningTestKit = BehaviorTestKit( RunGuardian invokePrivate running( diff --git a/src/test/scala/edu/ie3/osmogrid/guardian/run/SubGridHandlingSpec.scala b/src/test/scala/edu/ie3/osmogrid/guardian/run/SubGridHandlingSpec.scala index 15d90163..f91a665b 100644 --- a/src/test/scala/edu/ie3/osmogrid/guardian/run/SubGridHandlingSpec.scala +++ b/src/test/scala/edu/ie3/osmogrid/guardian/run/SubGridHandlingSpec.scala @@ -6,42 +6,36 @@ package edu.ie3.osmogrid.guardian.run -import org.apache.pekko.actor.testkit.typed.scaladsl.ActorTestKit -import edu.ie3.datamodel.models.input.{AssetInput, NodeInput} -import edu.ie3.datamodel.models.input.connector.`type`.Transformer3WTypeInput +import edu.ie3.datamodel.models.StandardUnits import edu.ie3.datamodel.models.input.connector.{ Transformer2WInput, Transformer3WInput } import edu.ie3.datamodel.models.input.container.{ GraphicElements, - GridContainer, RawGridElements, SubGridContainer, SystemParticipants } import edu.ie3.datamodel.models.input.graphics.GraphicInput import edu.ie3.datamodel.models.input.system.SystemParticipantInput -import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils +import edu.ie3.datamodel.models.input.{AssetInput, NodeInput} import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils._ -import edu.ie3.datamodel.models.{StandardUnits, UniqueEntity} -import edu.ie3.osmogrid.cfg.OsmoGridConfigFactory -import edu.ie3.osmogrid.io.output.OutputRequest -import edu.ie3.osmogrid.lv.LvResponse +import edu.ie3.osmogrid.cfg.{OsmoGridConfig, OsmoGridConfigFactory} import edu.ie3.osmogrid.exception.GridException -import edu.ie3.osmogrid.io.output.ResultListenerProtocol +import edu.ie3.osmogrid.io.output.{OutputRequest, ResultListenerProtocol} +import edu.ie3.osmogrid.lv.LvResponse import edu.ie3.osmogrid.mv.MvResponse import edu.ie3.test.common.{GridSupport, UnitSpec} +import org.apache.pekko.actor.testkit.typed.scaladsl.ActorTestKit import org.locationtech.jts.geom.Point import org.scalatest.BeforeAndAfterAll import org.scalatestplus.mockito.MockitoSugar.mock import org.slf4j.{Logger, LoggerFactory} import tech.units.indriya.quantity.Quantities -import utils.GridContainerUtils import java.util.UUID import scala.jdk.CollectionConverters._ -import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} class SubGridHandlingSpec @@ -62,10 +56,12 @@ class SubGridHandlingSpec mvCoordinator.ref ) val log = testKit.system.log + val cfg = OsmoGridConfig.Grids.Output(hv = true, lv = true, mv = true) "handle empty results correctly" in { val empty = Try { handleResults( + cfg, None, None, None, @@ -90,6 +86,7 @@ class SubGridHandlingSpec val lv = mockSubGrid(1, MV_10KV, LV) val processed = processResults( + cfg, Some(Seq(lv)), None, None, @@ -134,6 +131,7 @@ class SubGridHandlingSpec val expectedUpdatedNode = commonNode.copy().subnet(3).build() val processed = processResults( + cfg, Some(Seq(lv)), Some(Seq(mv)), None, diff --git a/src/test/scala/edu/ie3/osmogrid/io/output/ResultListenerIT.scala b/src/test/scala/edu/ie3/osmogrid/io/output/ResultListenerIT.scala index 7790671e..cb0c83ad 100644 --- a/src/test/scala/edu/ie3/osmogrid/io/output/ResultListenerIT.scala +++ b/src/test/scala/edu/ie3/osmogrid/io/output/ResultListenerIT.scala @@ -115,7 +115,7 @@ class ResultListenerIT val gridData = CsvJointGridContainerSource.read( jointGrid.getGridName, - ";", + ",", tmpDir, false ) diff --git a/src/test/scala/edu/ie3/osmogrid/mv/MvCoordinatorSpec.scala b/src/test/scala/edu/ie3/osmogrid/mv/MvCoordinatorSpec.scala index f9fb4e69..d9739414 100644 --- a/src/test/scala/edu/ie3/osmogrid/mv/MvCoordinatorSpec.scala +++ b/src/test/scala/edu/ie3/osmogrid/mv/MvCoordinatorSpec.scala @@ -36,9 +36,7 @@ class MvCoordinatorSpec private val runGuardian = asynchronousTestKit.createTestProbe[MvResponse]("RunGuardian") private val cfg = - OsmoGridConfigFactory.defaultTestConfig.generation.mv.getOrElse( - fail("Test config does not contain config for mv grid generation.") - ) + OsmoGridConfigFactory.defaultTestConfig "MvCoordinator" should { "be applied correctly" in { diff --git a/src/test/scala/edu/ie3/osmogrid/utils/MvUtilsSpec.scala b/src/test/scala/edu/ie3/osmogrid/mv/MvGraphGeneratorSupportSpec.scala similarity index 89% rename from src/test/scala/edu/ie3/osmogrid/utils/MvUtilsSpec.scala rename to src/test/scala/edu/ie3/osmogrid/mv/MvGraphGeneratorSupportSpec.scala index 20aa14ec..041b9be9 100644 --- a/src/test/scala/edu/ie3/osmogrid/utils/MvUtilsSpec.scala +++ b/src/test/scala/edu/ie3/osmogrid/mv/MvGraphGeneratorSupportSpec.scala @@ -4,19 +4,19 @@ * Research group Distribution grid planning and operation */ -package edu.ie3.osmogrid.utils +package edu.ie3.osmogrid.mv import edu.ie3.osmogrid.graph.OsmGraph +import edu.ie3.osmogrid.mv.MvGraphGeneratorSupport.generateMvGraph +import edu.ie3.osmogrid.mv.VoronoiPolygonSupport.VoronoiPolygon import edu.ie3.test.common.{MvTestData, UnitSpec} import edu.ie3.util.osm.model.OsmEntity.Node +import utils.Connections import utils.GridConversion.NodeConversion -import utils.MvUtils.generateMvGraph -import utils.VoronoiUtils.VoronoiPolygon -import utils.{Connections, MvUtils} import scala.jdk.CollectionConverters._ -class MvUtilsSpec extends UnitSpec with MvTestData { +class MvGraphGeneratorSupportSpec extends UnitSpec with MvTestData { "The MvUtils" should { def streetGraph: OsmGraph = { val streetGraph = new OsmGraph() @@ -40,7 +40,10 @@ class MvUtilsSpec extends UnitSpec with MvTestData { val nodes = List(nodeToHv, nodeInMv1, nodeInMv2) val (conversion, conn) = - MvUtils invokePrivate createDefinitions(nodes, streetGraph) + MvGraphGeneratorSupport invokePrivate createDefinitions( + nodes, + streetGraph + ) conversion.conversionToPSDM shouldBe Map( transitionPoint -> nodeToHv, diff --git a/src/test/scala/edu/ie3/osmogrid/utils/VoronoiUtilsSpec.scala b/src/test/scala/edu/ie3/osmogrid/mv/VoronoiPolygonSupportSpec.scala similarity index 84% rename from src/test/scala/edu/ie3/osmogrid/utils/VoronoiUtilsSpec.scala rename to src/test/scala/edu/ie3/osmogrid/mv/VoronoiPolygonSupportSpec.scala index 9649b220..fd052d79 100644 --- a/src/test/scala/edu/ie3/osmogrid/utils/VoronoiUtilsSpec.scala +++ b/src/test/scala/edu/ie3/osmogrid/mv/VoronoiPolygonSupportSpec.scala @@ -4,21 +4,20 @@ * Research group Distribution grid planning and operation */ -package edu.ie3.osmogrid.utils +package edu.ie3.osmogrid.mv import edu.ie3.datamodel.models.input.NodeInput +import edu.ie3.osmogrid.mv.VoronoiPolygonSupport.VoronoiPolygon import edu.ie3.test.common.{NodeInputSupport, UnitSpec} import org.locationtech.jts.geom.Polygon import org.slf4j.{Logger, LoggerFactory} -import utils.VoronoiUtils -import utils.VoronoiUtils.VoronoiPolygon -class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { +class VoronoiPolygonSupportSpec extends UnitSpec with NodeInputSupport { "The VoronoiUtils" should { val createPolygons = PrivateMethod[List[VoronoiPolygon]](Symbol("createPolygons")) val polygon: VoronoiPolygon = - (VoronoiUtils invokePrivate createPolygons(List(nodeToHv1)))(0) + (VoronoiPolygonSupport invokePrivate createPolygons(List(nodeToHv1)))(0) "generate polygons correctly" in { val useBuilder = PrivateMethod[List[Polygon]](Symbol("useBuilder")) @@ -34,20 +33,23 @@ class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { ) forAll(cases) { (nodes, expectedNumber) => - val polygons: List[Polygon] = VoronoiUtils invokePrivate useBuilder( - nodes.map(n => n.getGeoPosition.getCoordinate).toList - ) + val polygons: List[Polygon] = + VoronoiPolygonSupport invokePrivate useBuilder( + nodes.map(n => n.getGeoPosition.getCoordinate).toList + ) polygons.size shouldBe expectedNumber } } "create no voronoi polygons when an empty list is given" in { - val polygons = VoronoiUtils invokePrivate createPolygons(List.empty) + val polygons = + VoronoiPolygonSupport invokePrivate createPolygons(List.empty) polygons.size shouldBe 0 } "create a single voronoi polygon correctly" in { - val polygons = VoronoiUtils invokePrivate createPolygons(List(nodeToHv1)) + val polygons = + VoronoiPolygonSupport invokePrivate createPolygons(List(nodeToHv1)) polygons.size shouldBe 1 val polygon: VoronoiPolygon = polygons(0) @@ -68,7 +70,7 @@ class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { ) forAll(cases) { (nodes, expectedSize) => - val polygons = VoronoiUtils invokePrivate createPolygons(nodes) + val polygons = VoronoiPolygonSupport invokePrivate createPolygons(nodes) polygons.size shouldBe expectedSize } } @@ -77,7 +79,7 @@ class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { val hvToMvNodes = List(nodeToHv1, nodeToHv2, nodeToHv3, nodeToHv4) val polygons: List[VoronoiPolygon] = - VoronoiUtils invokePrivate createPolygons(hvToMvNodes) + VoronoiPolygonSupport invokePrivate createPolygons(hvToMvNodes) polygons.size shouldBe 4 @@ -128,12 +130,12 @@ class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { PrivateMethod[(List[VoronoiPolygon], List[NodeInput])]( Symbol("updatePolygons") ) - val log: Logger = LoggerFactory.getLogger(VoronoiUtils.getClass) + val log: Logger = LoggerFactory.getLogger(VoronoiPolygonSupport.getClass) val polygons: List[VoronoiPolygon] = List(polygon) val nodes = List(nodeInMv1, nodeInMv2, nodeInMv3) val (updatedPolygon, notAssigned) = - VoronoiUtils invokePrivate updatePolygons(polygons, nodes, log) + VoronoiPolygonSupport invokePrivate updatePolygons(polygons, nodes, log) notAssigned shouldBe List() updatedPolygon(0).transitionPointsToLowerVoltLvl shouldBe List( @@ -148,10 +150,10 @@ class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { PrivateMethod[(List[VoronoiPolygon], List[NodeInput])]( Symbol("updatePolygons") ) - val log: Logger = LoggerFactory.getLogger(VoronoiUtils.getClass) + val log: Logger = LoggerFactory.getLogger(VoronoiPolygonSupport.getClass) val polygons: List[VoronoiPolygon] = - VoronoiUtils invokePrivate createPolygons( + VoronoiPolygonSupport invokePrivate createPolygons( List(nodeToHv1, nodeToHv2, nodeToHv3, nodeToHv4) ) @@ -194,7 +196,11 @@ class VoronoiUtilsSpec extends UnitSpec with NodeInputSupport { forAll(cases) { (nodes, expectedNotAssigned, l1, l2, l3, l4) => val (updatedPolygon, notAssigned) = - VoronoiUtils invokePrivate updatePolygons(polygons, nodes, log) + VoronoiPolygonSupport invokePrivate updatePolygons( + polygons, + nodes, + log + ) notAssigned shouldBe expectedNotAssigned updatedPolygon(0).transitionPointsToLowerVoltLvl shouldBe l1 diff --git a/src/test/scala/edu/ie3/test/common/MvTestData.scala b/src/test/scala/edu/ie3/test/common/MvTestData.scala index d0c83bd8..899713d7 100644 --- a/src/test/scala/edu/ie3/test/common/MvTestData.scala +++ b/src/test/scala/edu/ie3/test/common/MvTestData.scala @@ -27,7 +27,7 @@ import utils.Connections.Connection import utils.GridConversion.NodeConversion import utils.OsmoGridUtils.getAllUniqueCombinations import utils.Solver.StepResultOption -import utils.VoronoiUtils.VoronoiPolygon +import edu.ie3.osmogrid.mv.VoronoiPolygonSupport.VoronoiPolygon import java.util.UUID