diff --git a/.scalafmt.conf b/.scalafmt.conf
index e69de29bb2..b9ff87f158 100644
--- a/.scalafmt.conf
+++ b/.scalafmt.conf
@@ -0,0 +1,4 @@
+version = 3.7.3
+runner.dialect = scala213
+
+rewrite.trailingCommas.style = multiple
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65f07e90da..b54f552bfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Instantiation of Heat Pump Agents [#253](https://github.com/ie3-institute/simona/issues/253)
- Output of accompanying thermal result models
- Added JDK installation, Scala Plugin + SDK in usersguide [#324](https://github.com/ie3-institute/simona/issues/324)
+- Squants scalatest matchers [#715](https://github.com/ie3-institute/simona/issues/715)
+- Energy Management capabilities:
+ - Added capability of SystemParticipants to handle flexibility [#308](https://github.com/ie3-institute/simona/issues/308)
+ - Added smart charging logic [#31](https://github.com/ie3-institute/simona/issues/31) and flex calculation in `EvcsAgent` [#332](https://github.com/ie3-institute/simona/issues/332)
+- Enhance output quotes of `RunSimona` [#743](https://github.com/ie3-institute/simona/issues/743)
+- Printing logs of failed tests [#747](https://github.com/ie3-institute/simona/issues/747)
+- Models for measurements within the grid structure [#89](https://github.com/ie3-institute/simona/issues/89)
+- Config possibility for transformer control groups [#90](https://github.com/ie3-institute/simona/issues/90)
### Changed
- Adapted to changed data source in PSDM [#435](https://github.com/ie3-institute/simona/issues/435)
@@ -26,13 +34,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adapting to simonaAPI 0.3.0 (adapted message protocol)
- Schedule lock [#651](https://github.com/ie3-institute/simona/issues/651)
- New scheduling protocol [#650](https://github.com/ie3-institute/simona/issues/650)
+ - Small improvements to the code [#696](https://github.com/ie3-institute/simona/issues/696)
- Replaced akka with pekko [#641](https://github.com/ie3-institute/simona/issues/641)
- Use `ThermalGrid` to calculate thermal environment of a heat pump [#315](https://github.com/ie3-institute/simona/issues/315)
+- Enable windows path as config parameters [#549](https://github.com/ie3-institute/simona/issues/549)
+- Unified consideration of scaling factor when simulating system participants [#81](https://github.com/ie3-institute/simona/issues/81)
+- Small improvements in `ResultEventListener` [#738](https://github.com/ie3-institute/simona/issues/738)
+- Converting `SimonaSim` to pekko typed/terminating SimonSim when initialization fails [#210](https://github.com/ie3-institute/simona/issues/210)
+- Converting the `GridAgent` and the `DBFSAlgorithm` to `pekko typed` [#666](https://github.com/ie3-institute/simona/issues/666)
### Fixed
- Removed a repeated line in the documentation of vn_simona config [#658](https://github.com/ie3-institute/simona/issues/658)
- Removed version number "2.0" from the logo printed to console [#642](https://github.com/ie3-institute/simona/issues/642)
- Fixed PV Model documentation [#684](https://github.com/ie3-institute/simona/issues/684), [#686](https://github.com/ie3-institute/simona/issues/686)
+- Removed `CsvDataSourceAdapter` workaround [#702](https://github.com/ie3-institute/simona/issues/702)
+- Logging wrong duration in the first simulation hour [#705](https://github.com/ie3-institute/simona/issues/705)
+- Fixed some compiler warnings [#657](https://github.com/ie3-institute/simona/issues/657)
+- Fixing false negative in ref system voltage validation [#706](https://github.com/ie3-institute/simona/issues/706)
+- Fixing randomly failing test in `RuntimeEventListenerSpec` etc. [#709](https://github.com/ie3-institute/simona/issues/709)
## [3.0.0] - 2023-08-07
diff --git a/build.gradle b/build.gradle
index 016036315b..b4a0226708 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,9 +7,9 @@ plugins {
id 'signing'
id 'maven-publish' // publish to a maven repo (local or mvn central, has to be defined)
id 'pmd' // code check, working on source code
- id 'com.diffplug.spotless' version '6.23.3'// code format
- id "com.github.ben-manes.versions" version '0.50.0'
- id "de.undercouch.download" version "5.5.0" // downloads plugin
+ id 'com.diffplug.spotless' version '6.25.0'// code format
+ id "com.github.ben-manes.versions" version '0.51.0'
+ id "de.undercouch.download" version "5.6.0" // downloads plugin
id "kr.motd.sphinx" version "2.10.1" // documentation generation
id "com.github.johnrengelman.shadow" version "8.1.1" // fat jar
id "org.sonarqube" version "4.4.1.3373" // sonarqube
@@ -24,14 +24,14 @@ ext {
javaVersion = JavaVersion.VERSION_17
scalaVersion = '2.13'
- scalaBinaryVersion = '2.13.12'
- pekkoVersion = '1.0.1'
+ scalaBinaryVersion = '2.13.13'
+ pekkoVersion = '1.0.2'
jtsVersion = '1.19.0'
confluentKafkaVersion = '7.4.0'
- tscfgVersion = '1.0.2'
- scapegoatVersion = '2.1.3'
+ tscfgVersion = '1.0.0'
+ scapegoatVersion = '2.1.5'
- testContainerVersion = '0.41.0'
+ testContainerVersion = '0.41.3'
scriptsLocation = 'gradle' + File.separator + 'scripts' + File.separator // location of script plugins
}
@@ -67,11 +67,6 @@ repositories {
}
dependencies {
- constraints {
- implementation( 'com.fasterxml.jackson.core:jackson-databind:2.16.0+' ){
- because "[CVE-2020-25649] CWE-611: Improper Restriction of XML External Entity Reference ('XXE')"
- }
- }
// ie³ internal repository
implementation('com.github.ie3-institute:PowerSystemUtils:2.0') {
@@ -102,13 +97,13 @@ dependencies {
/* logging */
implementation "com.typesafe.scala-logging:scala-logging_${scalaVersion}:3.9.5" // pekko scala logging
- implementation "ch.qos.logback:logback-classic:1.4.14"
+ implementation "ch.qos.logback:logback-classic:1.5.3"
/* testing */
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
testImplementation 'org.scalatestplus:mockito-3-4_2.13:3.2.10.0'
- testImplementation 'org.mockito:mockito-core:5.8.0' // mocking framework
- testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.17"
+ testImplementation 'org.mockito:mockito-core:5.11.0' // mocking framework
+ testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.18"
testRuntimeOnly 'com.vladsch.flexmark:flexmark-all:0.64.8' //scalatest html output
testImplementation group: 'org.pegdown', name: 'pegdown', version: '1.6.0'
testImplementation "org.apache.pekko:pekko-testkit_${scalaVersion}:${pekkoVersion}" // pekko testkit
@@ -147,7 +142,7 @@ dependencies {
/* Kafka */
implementation "org.apache.kafka:kafka-clients:${confluentKafkaVersion}-ccs"
implementation "io.confluent:kafka-streams-avro-serde:${confluentKafkaVersion}"
- implementation "com.sksamuel.avro4s:avro4s-core_${scalaVersion}:4.1.1"
+ implementation "com.sksamuel.avro4s:avro4s-core_${scalaVersion}:4.1.2"
implementation 'org.apache.commons:commons-math3:3.6.1' // apache commons math3
implementation 'org.apache.poi:poi-ooxml:5.2.5' // used for FilenameUtils
diff --git a/docs/readthedocs/config.md b/docs/readthedocs/config.md
index 91a6d952a5..64a9dfc1b7 100644
--- a/docs/readthedocs/config.md
+++ b/docs/readthedocs/config.md
@@ -236,3 +236,42 @@ Secondary convergence criterion for the power flow calculation is the number of
Resolution of the power flow calculation:
`simona.powerflow.resolution = "3600s"`
+
+## Transformer Control Group configuration
+
+It's possible to add a voltage control function to a transformer or group of transformers. This requires measurements within the network to be under voltage control and at least one corresponding transformer.
+The voltage control will attempt to adjust the voltage by changing the tap position of the corresponding transformer. If changing the tap position would cause a voltage limit to be exceeded, the initial voltage deviation cannot be reduced by the voltage control system.
+
+Transformer control groups must contain at least one transformer and one measurement. And can be configured as shown in this example for two transformer control groups:
+```
+simona.control.transformer = [
+{
+transformers = ["31a2b9bf-e785-4475-aa44-1c34646e8c79"],
+measurements = ["923f2d69-3093-4198-86e4-13d2d1c220f8"],
+vMin = 0.98,
+vMax = 1.02
+}
+, {
+transformers = ["1132dbf4-e8a1-44ae-8415-f42d4497aa1d"],
+measurements = ["7686b818-a0ba-465c-8e4e-f7d3c4e171fc"],
+vMin = 0.98,
+vMax = 1.02
+}
+]
+```
+
+UUID of transformer in control group:
+
+`transformers = ["31a2b9bf-e785-4475-aa44-1c34646e8c79"]`
+
+UUID of measurement in control group:
+
+`measurements = ["923f2d69-3093-4198-86e4-13d2d1c220f8"]`
+
+Minimum Voltage Limit in p.u.:
+
+`vMin = 0.98`
+
+Maximum Voltage Limit in p.u.:
+
+`vMax = 1.02`
diff --git a/docs/readthedocs/models.md b/docs/readthedocs/models.md
index f4709ebab9..e32ee67098 100644
--- a/docs/readthedocs/models.md
+++ b/docs/readthedocs/models.md
@@ -33,3 +33,11 @@ models/load_model
models/pv_model
models/wec_model
```
+
+## Measurement and Control
+```{toctree}
+---
+maxdepth: 1
+---
+models/measurement_control
+```
diff --git a/docs/readthedocs/models/cts_model.md b/docs/readthedocs/models/cts_model.md
index eea6fcfe0c..0ca51a7395 100644
--- a/docs/readthedocs/models/cts_model.md
+++ b/docs/readthedocs/models/cts_model.md
@@ -7,7 +7,7 @@ This storage model operates on volumes, although the functions it provides for o
## Attributes, Units and Remarks
-Please refer to {doc}`PowerSystemDataModel - CTS Model ` for Attributes and Units used in this Model.
+Please refer to {doc}`PowerSystemDataModel - CTS Model ` for Attributes and Units used in this Model.
## Calculations
### Maximal storage capacity
diff --git a/docs/readthedocs/models/measurement_control.md b/docs/readthedocs/models/measurement_control.md
new file mode 100644
index 0000000000..e57238d6a6
--- /dev/null
+++ b/docs/readthedocs/models/measurement_control.md
@@ -0,0 +1,5 @@
+(measurement_control)=
+
+# Transformer Control Groups
+
+Transformer control group can be used to implement control functionalities like long-range control for active voltage stability. For this purpose, network areas and transformers can be logically linked to a control group via measuring points. If a deviation from the target voltage magnitude is detected at one of the measuring points, the transformer is switched to the appropriate tap position to solve the deviation, provided that no limit values are violated at other measuring points. This requires that only measuring points are included in control groups that can also be influenced by the associated transformer.
diff --git a/docs/readthedocs/models/thermal_grid_model.md b/docs/readthedocs/models/thermal_grid_model.md
index e7aa2c214e..fc16159552 100644
--- a/docs/readthedocs/models/thermal_grid_model.md
+++ b/docs/readthedocs/models/thermal_grid_model.md
@@ -5,4 +5,4 @@ The Thermal Grid Model introduces a coupling point to thermal system, equivalent
## Attributes, Units and Remarks
-Please refer to {doc}`PowerSystemDataModel - Thermal Bus ` for Attributes and Units used in this Model.
+Please refer to {doc}`PowerSystemDataModel - Thermal Bus ` for Attributes and Units used in this Model.
diff --git a/docs/readthedocs/requirements.txt b/docs/readthedocs/requirements.txt
index bd2e90f529..e34adcdafc 100644
--- a/docs/readthedocs/requirements.txt
+++ b/docs/readthedocs/requirements.txt
@@ -1,7 +1,7 @@
Sphinx==7.2.6
sphinx-rtd-theme==2.0.0
-sphinxcontrib-plantuml==0.27
+sphinxcontrib-plantuml==0.28
myst-parser==2.0.0
markdown-it-py==3.0.0
sphinx-hoverxref==1.3.0
-sphinxcontrib-bibtex==2.6.1
+sphinxcontrib-bibtex==2.6.2
diff --git a/gradle/scripts/scoverage.gradle b/gradle/scripts/scoverage.gradle
index 0a2e0157e8..e8ac99cf1f 100644
--- a/gradle/scripts/scoverage.gradle
+++ b/gradle/scripts/scoverage.gradle
@@ -3,7 +3,7 @@
// https://github.com/scoverage/gradle-scoverage/issues/109 for details
scoverage {
- scoverageVersion = "2.0.11"
+ scoverageVersion = "2.1.0"
scoverageScalaVersion = scalaBinaryVersion
coverageOutputHTML = false
coverageOutputXML = true
diff --git a/gradle/scripts/spotless.gradle b/gradle/scripts/spotless.gradle
index 525b407e68..104ff70c86 100644
--- a/gradle/scripts/spotless.gradle
+++ b/gradle/scripts/spotless.gradle
@@ -34,7 +34,7 @@ spotless {
//sets a license header, removes unused imports and formats conforming to the scala fmt formatter
scala {
- scalafmt()
+ scalafmt().configFile(".scalafmt.conf")
licenseHeader ie3LicHead, "package.*\\n"
}
diff --git a/input/samples/vn_simona/vn_simona.conf b/input/samples/vn_simona/vn_simona.conf
index fe2e05d439..59185bb5fb 100644
--- a/input/samples/vn_simona/vn_simona.conf
+++ b/input/samples/vn_simona/vn_simona.conf
@@ -182,3 +182,17 @@ simona.powerflow.newtonraphson.epsilon = [1E-12]
simona.powerflow.newtonraphson.iterations = 50
simona.powerflow.resolution = "3600s"
simona.powerflow.stopOnFailure = true
+
+simona.control.transformer = [
+ {
+ transformers = ["31a2b9bf-e785-4475-aa44-1c34646e8c79"],
+ measurements = ["923f2d69-3093-4198-86e4-13d2d1c220f8"],
+ vMin = 0.98,
+ vMax = 1.02
+ }, {
+ transformers = ["1132dbf4-e8a1-44ae-8415-f42d4497aa1d"],
+ measurements = ["7686b818-a0ba-465c-8e4e-f7d3c4e171fc"],
+ vMin = 0.98,
+ vMax = 1.02
+ }
+]
diff --git a/src/main/resources/config/config-template.conf b/src/main/resources/config/config-template.conf
index e5448bcb6a..b006cafd6b 100644
--- a/src/main/resources/config/config-template.conf
+++ b/src/main/resources/config/config-template.conf
@@ -50,6 +50,8 @@ WecRuntimeConfig {
EvcsRuntimeConfig {
baseRuntimeConfig: BaseRuntimeConfig # this entry is ignored by the config generator,
# but cannot removed bc otherwise EvcsRuntimeConfig is handled as String
+ chargingStrategy: String | "maxPower" # can be one of maxPower, constantPower, gridOrientedScheduling or marketOrientedScheduling
+ lowestEvSoc: Double | 0.2 # Defines the lowest possible state of charge (SoC) that an EV is allowed to uncharge in vehicle to grid (V2G) mode
}
#@define extends BaseRuntimeConfig
@@ -111,6 +113,7 @@ SimpleOutputConfig {
ParticipantBaseOutputConfig {
base: BaseOutputConfig
powerRequestReply: boolean # Inform listeners about power request replies
+ flexResult: boolean | false
}
#@define
@@ -123,6 +126,14 @@ GridOutputConfig {
transformers3w: boolean | false
}
+#@define
+TransformerControlGroup {
+ measurements: [string]
+ transformers: [string]
+ vMax: Double
+ vMin: Double
+}
+
##################################################################
# Agentsim
##################################################################
@@ -257,6 +268,7 @@ simona.output.thermal = {
defaultConfig = SimpleOutputConfig
individualConfigs = [SimpleOutputConfig]
}
+simona.output.flex = Boolean | false
##################################################################
# Runtime Configuration // todo refactor as this naming is misleading
@@ -329,3 +341,11 @@ simona.event.listener = [
eventsToProcess = [string]
}
]
+
+##################################################################
+# Configuration of Control Schemes
+##################################################################
+#@optional
+simona.control = {
+ transformer = [TransformerControlGroup]
+}
diff --git a/src/main/scala/edu/ie3/simona/actor/ActorUtil.scala b/src/main/scala/edu/ie3/simona/actor/ActorUtil.scala
index 6f59446915..bfe44d97ad 100644
--- a/src/main/scala/edu/ie3/simona/actor/ActorUtil.scala
+++ b/src/main/scala/edu/ie3/simona/actor/ActorUtil.scala
@@ -12,7 +12,7 @@ import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
object ActorUtil {
def stopOnError[M](
ctx: ActorContext[M],
- msg: String
+ msg: String,
): Behavior[M] = {
ctx.log.error(s"$msg. Stopping.")
Behaviors.stopped
diff --git a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala
index bc5b548f2f..6aeeaf093c 100644
--- a/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala
+++ b/src/main/scala/edu/ie3/simona/actor/SimonaActorNaming.scala
@@ -6,7 +6,9 @@
package edu.ie3.simona.actor
-import org.apache.pekko.actor.{ActorRef, ActorRefFactory, Props}
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
+import org.apache.pekko.actor.{ActorRefFactory, Props, ActorRef => ClassicRef}
import java.util.UUID
@@ -15,10 +17,10 @@ object SimonaActorNaming {
implicit class RichActorRefFactory(private val refFactory: ActorRefFactory)
extends AnyVal {
- def simonaActorOf(props: Props, actorId: String): ActorRef =
+ def simonaActorOf(props: Props, actorId: String): ClassicRef =
refFactory.actorOf(props, actorName(props, actorId))
- def simonaActorOf(props: Props): ActorRef =
+ def simonaActorOf(props: Props): ClassicRef =
refFactory.actorOf(props, actorName(props, simonaActorUuid))
}
@@ -62,13 +64,13 @@ object SimonaActorNaming {
def actorName(typeName: String, actorId: String): String =
s"${typeName}_${cleanActorIdForPekko(actorId)}"
- /** Extracts the actor name from given [[ActorRef]]. Cluster singletons are
+ /** Extracts the actor name from given [[ClassicRef]]. Cluster singletons are
* taken care of separately.
*
* @return
* the actor name extract from the ActorRef
*/
- def actorName(actorRef: ActorRef): String =
+ def actorName(actorRef: ClassicRef): String =
actorRef.path.name match {
case "singleton" =>
// singletons end in /${actorName}/singleton
@@ -77,6 +79,14 @@ object SimonaActorNaming {
other
}
+ /** Extracts the actor name from given [[ActorRef]]. Cluster singletons are
+ * taken care of separately.
+ *
+ * @return
+ * the actor name extract from the ActorRef
+ */
+ def actorName(actorRef: ActorRef[_]): String = actorName(actorRef.toClassic)
+
/** Constructs the type name from given props.
*
* @return
diff --git a/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala b/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala
index af7b168e41..4b5c6c920d 100644
--- a/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala
+++ b/src/main/scala/edu/ie3/simona/agent/EnvironmentRefs.scala
@@ -6,7 +6,10 @@
package edu.ie3.simona.agent
-import org.apache.pekko.actor.ActorRef
+import edu.ie3.simona.event.RuntimeEvent
+import edu.ie3.simona.ontology.messages.SchedulerMessage
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
/** Container class, that gather together reference to relevant entities, that
* represent the environment in the simulation
@@ -23,9 +26,9 @@ import org.apache.pekko.actor.ActorRef
* Reference to the EV data service, if existing
*/
final case class EnvironmentRefs(
- scheduler: ActorRef,
- runtimeEventListener: ActorRef,
- primaryServiceProxy: ActorRef,
- weather: ActorRef,
- evDataService: Option[ActorRef]
+ scheduler: ActorRef[SchedulerMessage],
+ runtimeEventListener: ActorRef[RuntimeEvent],
+ primaryServiceProxy: ClassicRef,
+ weather: ClassicRef,
+ evDataService: Option[ClassicRef],
)
diff --git a/src/main/scala/edu/ie3/simona/agent/SimonaAgent.scala b/src/main/scala/edu/ie3/simona/agent/SimonaAgent.scala
index cfdd3372c3..ff916942fd 100644
--- a/src/main/scala/edu/ie3/simona/agent/SimonaAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/SimonaAgent.scala
@@ -37,7 +37,7 @@ trait SimonaAgent[D]
case Event(Status.Failure(ex), _) =>
log.error(
ex,
- "Received a failure status message with following exception."
+ "Received a failure status message with following exception.",
)
self ! PoisonPill
stay()
diff --git a/src/main/scala/edu/ie3/simona/agent/ValueStore.scala b/src/main/scala/edu/ie3/simona/agent/ValueStore.scala
index eea250e176..a21f7d0f58 100644
--- a/src/main/scala/edu/ie3/simona/agent/ValueStore.scala
+++ b/src/main/scala/edu/ie3/simona/agent/ValueStore.scala
@@ -9,6 +9,8 @@ package edu.ie3.simona.agent
import edu.ie3.simona.util.SimonaConstants
import squants.Dimensionless
+import scala.collection.SortedMap
+
/** Represents a value store to hold data of former ticks
*
* @param maxTickSpan
@@ -20,7 +22,7 @@ import squants.Dimensionless
*/
final case class ValueStore[+D](
maxTickSpan: Long,
- private val store: Map[Long, D] = Map.empty[Long, D]
+ private val store: SortedMap[Long, D] = SortedMap.empty[Long, D],
) {
/** Determine the lastly known data tick, if available. Includes the given
@@ -42,9 +44,7 @@ final case class ValueStore[+D](
* An Option to the last entry
*/
def last(requestedTick: Long): Option[(Long, D)] =
- store
- .filter(_._1 <= requestedTick)
- .maxByOption(_._1)
+ store.rangeTo(requestedTick).lastOption
/** Get the last known entry (with the highest tick)
*
@@ -52,7 +52,27 @@ final case class ValueStore[+D](
* An Option to the last entry
*/
def last(): Option[(Long, D)] =
- store.maxByOption(_._1)
+ store.lastOption
+
+ /** Optionally returns the entry for given tick
+ * @param tick
+ * The tick
+ * @return
+ * The data for the tick if it exists, otherwise [[None]]
+ */
+ def get(tick: Long): Option[D] =
+ store.get(tick)
+
+ /** Returns the data associated with a tick, or a default value if no data
+ * exists for the tick.
+ * @param tick
+ * The tick
+ * @return
+ * the data associated with `tick` if it exists, otherwise the result of
+ * the `default` function.
+ */
+ def getOrElse[D2 >: D](tick: Long, default: => D2): D2 =
+ store.getOrElse(tick, default)
/** Acquires the stored information within the specified tick window
*
@@ -65,20 +85,13 @@ final case class ValueStore[+D](
* in the value store
*/
def get(requestStart: Long, requestEnd: Long): Map[Long, D] =
- store.filter(entry => entry._1 >= requestStart && entry._1 <= requestEnd)
+ store.rangeFrom(requestStart).rangeTo(requestEnd).toMap
- /** Checks, if all needed ticks are available in the given value store
- *
- * @param neededTicks
- * An Array of needed ticks
- * @return
- * true, if everything is there
- */
- def allTicksAvailable(neededTicks: Array[Long]): Boolean =
- store.keySet.toSeq.sorted.containsSlice(neededTicks.toSeq.sorted)
+ def asMap: Map[Long, D] =
+ store.toMap
}
-case object ValueStore {
+object ValueStore {
/** Create a default "empty" voltage value store which requires an initial
* voltage value to be set for tick 0
@@ -92,11 +105,11 @@ case object ValueStore {
*/
def forVoltage(
maxTickSpan: Long,
- initialPerUnit: Dimensionless
+ initialPerUnit: Dimensionless,
): ValueStore[Dimensionless] =
new ValueStore(
maxTickSpan,
- Map(SimonaConstants.FIRST_TICK_IN_SIMULATION -> initialPerUnit)
+ SortedMap(SimonaConstants.FIRST_TICK_IN_SIMULATION -> initialPerUnit),
)
/** Create a value store for result values. A result value store requires a
@@ -131,9 +144,21 @@ case object ValueStore {
def updateValueStore[D](
valueStore: ValueStore[D],
tick: Long,
- newEntry: D
- ): ValueStore[D] = valueStore.copy(
- store = (valueStore.store + (tick -> newEntry))
- .filter(pair => pair._1 > tick - valueStore.maxTickSpan)
- )
+ newEntry: D,
+ ): ValueStore[D] = {
+ val updatedStore = valueStore.store ++ SortedMap(tick -> newEntry)
+
+ // always keep at least 3 entries
+ val minKeep = 3
+
+ valueStore.copy(
+ store = if (updatedStore.size > minKeep) {
+ val (rest, keep) = updatedStore.splitAt(updatedStore.size - minKeep)
+ val restPruned = rest.rangeFrom(tick - valueStore.maxTickSpan + 1L)
+
+ restPruned ++ keep
+ } else
+ updatedStore
+ )
+ }
}
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 309fb1b7ef..f47f3ef8b8 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/DBFSAlgorithm.scala
@@ -6,10 +6,6 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.actor.{ActorRef, FSM, PoisonPill}
-import org.apache.pekko.pattern.{ask, pipe}
-import org.apache.pekko.util.{Timeout => PekkoTimeout}
import breeze.linalg.{DenseMatrix, DenseVector}
import breeze.math.Complex
import edu.ie3.datamodel.graph.SubGridGate
@@ -19,18 +15,22 @@ import edu.ie3.powerflow.model.PowerFlowResult
import edu.ie3.powerflow.model.PowerFlowResult.FailedPowerFlowResult.FailedNewtonRaphsonPFResult
import edu.ie3.powerflow.model.PowerFlowResult.SuccessFullPowerFlowResult.ValidNewtonRaphsonPFResult
import edu.ie3.powerflow.model.enums.NodeType
-import edu.ie3.simona.agent.grid.GridAgent._
+import edu.ie3.simona.agent.grid.GridAgent.idle
import edu.ie3.simona.agent.grid.GridAgentData.{
GridAgentBaseData,
- PowerFlowDoneData
+ GridAgentConstantData,
+ PowerFlowDoneData,
}
+import edu.ie3.simona.agent.grid.GridAgentMessage._
import edu.ie3.simona.agent.grid.ReceivedValues._
-import edu.ie3.simona.agent.state.AgentState
-import edu.ie3.simona.agent.state.AgentState.Idle
-import edu.ie3.simona.agent.state.GridAgentState.{
- CheckPowerDifferences,
- HandlePowerFlowCalculations,
- SimulateGrid
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.agent.grid.VoltageMessage.{
+ ProvideSlackVoltageMessage,
+ RequestSlackVoltageMessage,
+}
+import edu.ie3.simona.agent.participant.ParticipantAgent.{
+ FinishParticipantSimulation,
+ ParticipantMessage,
}
import edu.ie3.simona.event.RuntimeEvent.PowerFlowFailed
import edu.ie3.simona.exceptions.agent.DBFSAlgorithmException
@@ -38,714 +38,810 @@ import edu.ie3.simona.model.grid.{NodeModel, RefSystem}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage._
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
-import edu.ie3.simona.ontology.messages.VoltageMessage.{
- ProvideSlackVoltageMessage,
- RequestSlackVoltageMessage
-}
import edu.ie3.simona.util.TickUtil.TickLong
import edu.ie3.util.scala.quantities.Megavars
import edu.ie3.util.scala.quantities.SquantsUtils.RichElectricPotential
+import org.apache.pekko.actor.typed.scaladsl.AskPattern._
+import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
+import org.apache.pekko.actor.typed.scaladsl.{
+ ActorContext,
+ Behaviors,
+ StashBuffer,
+}
+import org.apache.pekko.actor.typed.{ActorRef, Behavior, Scheduler}
+import org.apache.pekko.pattern.ask
+import org.apache.pekko.util.{Timeout => PekkoTimeout}
+import org.slf4j.Logger
import squants.Each
import squants.energy.Megawatts
import java.time.{Duration, ZonedDateTime}
import java.util.UUID
import scala.concurrent.{ExecutionContext, Future}
+import scala.util.{Failure, Success}
/** Trait that is normally mixed into every [[GridAgent]] to enable distributed
* forward backward sweep (DBFS) algorithm execution. It is considered to be
* the standard behaviour of a [[GridAgent]].
*/
trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
- this: GridAgent =>
- // implicit ExecutionContext should be in scope
- // see https://pekko.apache.org/docs/pekko/current/futures.html
- implicit val ec: ExecutionContext = context.dispatcher
-
- when(SimulateGrid) {
-
- // first part of the grid simulation, same for all gridAgents on all levels
- // we start with a forward-sweep by requesting the data from our child assets and grids (if any)
- case Event(
- Activation(currentTick),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- log.debug("Start sweep number: {}", gridAgentBaseData.currentSweepNo)
- // hold tick and trigger for the whole time the dbfs is executed or
- // at least until the the first full sweep is done (for superior grid agent only)
- holdTick(currentTick)
-
- // 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
- askForAssetPowers(
- currentTick,
- gridAgentBaseData.sweepValueStores
- .get(gridAgentBaseData.currentSweepNo),
- gridAgentBaseData.gridEnv.nodeToAssetAgents,
- gridAgentBaseData.gridEnv.gridModel.mainRefSystem,
- gridAgentBaseData.powerFlowParams.sweepTimeout
- )
- // 2. inferior grids p/q values
- askInferiorGridsForPowers(
- gridAgentBaseData.currentSweepNo,
- gridAgentBaseData.gridEnv.subgridGateToActorRef,
- gridAgentBaseData.inferiorGridGates,
- gridAgentBaseData.powerFlowParams.sweepTimeout
- )
+ /** Method that defines the [[Behavior]] for simulating the grid.
+ * @param gridAgentData
+ * state data of the actor
+ * @param currentTick
+ * current simulation tick
+ * @return
+ * a [[Behavior]]
+ */
+ private[grid] def simulateGrid(
+ gridAgentData: GridAgentData,
+ currentTick: Long,
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = Behaviors.receivePartial {
+ case (ctx, message) =>
+ (message, gridAgentData) match {
+ // first part of the grid simulation, same for all gridAgents on all levels
+ // we start with a forward-sweep by requesting the data from our child assets and grids (if any)
+ case (
+ WrappedActivation(activation: Activation),
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ ctx.log.debug(
+ "Start sweep number: {}",
+ gridAgentBaseData.currentSweepNo,
+ )
- // 3. superior grids slack voltage
- askSuperiorGridsForSlackVoltages(
- gridAgentBaseData.currentSweepNo,
- gridAgentBaseData.gridEnv.subgridGateToActorRef,
- gridAgentBaseData.superiorGridGates,
- gridAgentBaseData.powerFlowParams.sweepTimeout
- )
+ // 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
+ askForAssetPowers(
+ currentTick,
+ gridAgentBaseData.sweepValueStores
+ .get(gridAgentBaseData.currentSweepNo),
+ gridAgentBaseData.gridEnv.nodeToAssetAgents,
+ gridAgentBaseData.gridEnv.gridModel.mainRefSystem,
+ gridAgentBaseData.powerFlowParams.sweepTimeout,
+ )(ctx)
+
+ // 2. inferior grids p/q values
+ askInferiorGridsForPowers(
+ gridAgentBaseData.currentSweepNo,
+ gridAgentBaseData.gridEnv.subgridGateToActorRef,
+ gridAgentBaseData.inferiorGridGates,
+ gridAgentBaseData.powerFlowParams.sweepTimeout,
+ )(ctx)
+
+ // 3. superior grids slack voltage
+ askSuperiorGridsForSlackVoltages(
+ gridAgentBaseData.currentSweepNo,
+ gridAgentBaseData.gridEnv.subgridGateToActorRef,
+ gridAgentBaseData.superiorGridGates,
+ gridAgentBaseData.powerFlowParams.sweepTimeout,
+ )(ctx)
+
+ simulateGrid(gridAgentBaseData, activation.tick)
+
+ // if we receive power values as response on our request, we process them here
+ case (
+ receivedValues: ReceivedValues,
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ // 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: ReceivedSlackVoltageValues =>
+ gridAgentBaseData.updateWithReceivedSlackVoltages(
+ receivedSlacks
+ )
+ case unknownReceivedValues =>
+ throw new DBFSAlgorithmException(
+ s"Received unknown values: $unknownReceivedValues"
+ )
+ }
+
+ // check if we have enough data for a power flow calculation or a
+ // power differences check (if the grid agent is a superior agent)
+ // if yes, check if we have failing power flow in at least one of the inferior grids
+ // if there are failing ones, escalate the failure to the superior grid (if any),
+ // if not go to power flow or power differences check
+ // if we haven't received everything yet, stay and wait
+ val allValuesReceived =
+ updatedGridAgentBaseData.allRequestedDataReceived
- stay()
-
- // if we receive power values as response on our request, we process them here
- case Event(
- receivedValues: ReceivedValues,
- gridAgentBaseData: GridAgentBaseData
- ) =>
- // 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: ReceivedSlackVoltageValues =>
- gridAgentBaseData.updateWithReceivedSlackVoltages(receivedSlacks)
- case unknownReceivedValues =>
- throw new DBFSAlgorithmException(
- s"Received unknown values: $unknownReceivedValues"
+ ctx.log.debug(
+ "{}",
+ if (allValuesReceived)
+ "Got answers for all my requests for Slack Voltages and Power Values."
+ else
+ "Still waiting for answers my requests for Slack Voltages and Power Values.",
)
- }
- // check if we have enough data for a power flow calculation or a
- // power differences check (if the grid agent is a superior agent)
- // if yes, check if we have failing power flow in at least one of the inferior grids
- // if there are failing ones, escalate the failure to the superior grid (if any),
- // if not go to power flow or power differences check
- // if we haven't received everything yet, stay and wait
- val allValuesReceived = updatedGridAgentBaseData.allRequestedDataReceived
-
- log.debug(
- "{}",
- if (allValuesReceived)
- "Got answers for all my requests for Slack Voltages and Power Values."
- else
- "Still waiting for answers my requests for Slack Voltages and Power Values."
- )
+ if (gridAgentBaseData.isSuperior) {
+ goToCheckPowerDifferencesOrStay(
+ allValuesReceived,
+ updatedGridAgentBaseData,
+ currentTick,
+ simulateGrid,
+ )(ctx, constantData, buffer)
+ } else {
+ goToPowerFlowCalculationOrStay(
+ allValuesReceived,
+ updatedGridAgentBaseData,
+ currentTick,
+ simulateGrid,
+ )(ctx, constantData, buffer)
+ }
- if (gridAgentBaseData.isSuperior) {
- goToCheckPowerDifferencesOrStay(
- allValuesReceived,
- updatedGridAgentBaseData
- )
- } else {
- goToPowerFlowCalculationOrStay(
- allValuesReceived,
- updatedGridAgentBaseData
- )
- }
+ // if we receive a request for slack voltages from our inferior grids we want to answer it
+ case (
+ RequestSlackVoltageMessage(
+ currentSweepNo,
+ nodeUuids,
+ sender,
+ ),
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ ctx.log.debug(
+ s"Received Slack Voltages request from {} for nodes {} and sweepNo: {}",
+ sender,
+ nodeUuids,
+ gridAgentBaseData.currentSweepNo,
+ )
- // if we receive a request for slack voltages from our inferior grids we want to answer it
- case Event(
- RequestSlackVoltageMessage(currentSweepNo, nodeUuids),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- log.debug(
- s"Received Slack Voltages request from {} for nodes {} and sweepNo: {}",
- sender(),
- nodeUuids,
- gridAgentBaseData.currentSweepNo
- )
+ nodeUuids.map { nodeUuid =>
+ // 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
+ (gridAgentBaseData.sweepValueStores.get(currentSweepNo) match {
+ case Some(result) =>
+ Some(result, currentSweepNo)
+ case None =>
+ // this happens if this agent is either a) the superior grid agent, because it will always get a request for
+ // the next sweep, as it triggers calculations for the next sweep or b) at all other
+ // (non last downstream grid agents) in sweep 0
+ ctx.log.debug(
+ "Unable to find slack voltage for nodes '{}' in sweep '{}'. Try to get voltage of previous sweep.",
+ nodeUuids,
+ currentSweepNo,
+ )
+ gridAgentBaseData.sweepValueStores
+ .get(currentSweepNo - 1)
+ .map((_, currentSweepNo - 1))
+ }).map { case (result, sweepNo) =>
+ // get nodeUUID
+ result.sweepData.find(_.nodeUuid == nodeUuid) match {
+ case Some(sweepValueStoreData) =>
+ val slackVoltageInPu = sweepValueStoreData.stateData.voltage
+ val mainRefSystem =
+ gridAgentBaseData.gridEnv.gridModel.mainRefSystem
+ (
+ mainRefSystem.vInSi(slackVoltageInPu.real),
+ mainRefSystem.vInSi(slackVoltageInPu.imag),
+ )
+ case None =>
+ throw new DBFSAlgorithmException(
+ s"Requested nodeUuid $nodeUuid " +
+ s"not found in sweep value store data for sweepNo: $sweepNo. This indicates" +
+ s"either a wrong processing of a previous sweep result or inconsistencies in grid model data!"
+ )
+ }
+ }.getOrElse {
+ ctx.log.debug(
+ "Unable to get slack voltage for node '{}' in sweeps '{}' and '{}'. Returning target voltage.",
+ nodeUuid,
+ currentSweepNo,
+ currentSweepNo - 1,
+ )
- nodeUuids.map { nodeUuid =>
- // 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
- (gridAgentBaseData.sweepValueStores.get(currentSweepNo) match {
- case Some(result) =>
- Some(result, currentSweepNo)
- case None =>
- // this happens if this agent is either a) the superior grid agent, because it will always get a request for
- // the next sweep, as it triggers calculations for the next sweep or b) at all other
- // (non last downstream grid agents) in sweep 0
- log.debug(
- "Unable to find slack voltage for nodes '{}' in sweep '{}'. Try to get voltage of previous sweep.",
- nodeUuids,
- currentSweepNo
- )
- gridAgentBaseData.sweepValueStores
- .get(currentSweepNo - 1)
- .map((_, currentSweepNo - 1))
- }).map { case (result, sweepNo) =>
- // get nodeUUID
- result.sweepData.find(_.nodeUuid == nodeUuid) match {
- case Some(sweepValueStoreData) =>
- val slackVoltageInPu = sweepValueStoreData.stateData.voltage
- val mainRefSystem =
+ val refSystem =
gridAgentBaseData.gridEnv.gridModel.mainRefSystem
+
+ /* Determine the slack node voltage under consideration of the target voltage set point */
+ val vTarget =
+ gridAgentBaseData.gridEnv.gridModel.gridComponents.nodes
+ .find { case NodeModel(uuid, _, _, isSlack, _, _) =>
+ uuid == nodeUuid && isSlack
+ }
+ .map(_.vTarget)
+ .getOrElse(Each(1d))
+ val vSlack =
+ refSystem.nominalVoltage.multiplyWithDimensionles(vTarget)
+
(
- mainRefSystem.vInSi(slackVoltageInPu.real),
- mainRefSystem.vInSi(slackVoltageInPu.imag)
+ vSlack,
+ refSystem.vInSi(0d),
)
- case None =>
- throw new DBFSAlgorithmException(
- s"Requested nodeUuid $nodeUuid " +
- s"not found in sweep value store data for sweepNo: $sweepNo. This indicates" +
- s"either a wrong processing of a previous sweep result or inconsistencies in grid model data!"
+ } match {
+ case (slackE, slackF) =>
+ ctx.log.debug(
+ s"Provide {} to {} for node {} and sweepNo: {}",
+ s"$slackE, $slackF",
+ sender,
+ nodeUuid,
+ gridAgentBaseData.currentSweepNo,
+ )
+
+ ExchangeVoltage(nodeUuid, slackE, slackF)
+ }
+ } match {
+ case exchangeVoltages =>
+ sender ! ProvideSlackVoltageMessage(
+ currentSweepNo,
+ exchangeVoltages,
)
+ Behaviors.same
}
- }.getOrElse {
- log.debug(
- "Unable to get slack voltage for node '{}' in sweeps '{}' and '{}'. Returning target voltage.",
- nodeUuid,
- currentSweepNo,
- currentSweepNo - 1
- )
- val refSystem =
- gridAgentBaseData.gridEnv.gridModel.mainRefSystem
-
- /* Determine the slack node voltage under consideration of the target voltage set point */
- val vTarget =
- gridAgentBaseData.gridEnv.gridModel.gridComponents.nodes
- .find { case NodeModel(uuid, _, _, isSlack, _, _) =>
- uuid == nodeUuid && isSlack
- }
- .map(_.vTarget)
- .getOrElse(Each(1d))
- val vSlack =
- refSystem.nominalVoltage.multiplyWithDimensionles(vTarget)
-
- (
- vSlack,
- refSystem.vInSi(0d)
- )
- } match {
- case (slackE, slackF) =>
- log.debug(
- s"Provide {} to {} for node {} and sweepNo: {}",
- s"$slackE, $slackF",
- sender(),
- nodeUuid,
- gridAgentBaseData.currentSweepNo
+ // receive grid power values message request from superior grids
+ // before power flow calc for this sweep we either have to stash() the message to answer it later (in current sweep)
+ // or trigger a new run for the next sweepNo
+ case (
+ msg @ WrappedPowerMessage(
+ RequestGridPowerMessage(
+ requestSweepNo,
+ _,
+ _,
+ )
+ ),
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ if (gridAgentBaseData.currentSweepNo == requestSweepNo) {
+ ctx.log.debug(
+ s"Received request for grid power values for sweepNo {} before my first power flow calc. Stashing away.",
+ requestSweepNo,
)
- ExchangeVoltage(nodeUuid, slackE, slackF)
- }
- } match {
- case exchangeVoltages =>
- stay() replying ProvideSlackVoltageMessage(
- currentSweepNo,
- exchangeVoltages
- )
- }
+ buffer.stash(msg)
- // receive grid power values message request from superior grids
- // / before power flow calc for this sweep we either have to stash() the message to answer it later (in current sweep)
- // / or trigger a new run for the next sweepNo
- case Event(
- RequestGridPowerMessage(requestSweepNo, _),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- if (gridAgentBaseData.currentSweepNo == requestSweepNo) {
- log.debug(
- s"Received request for grid power values for sweepNo {} before my first power flow calc. Stashing away.",
- requestSweepNo
- )
- stash()
- stay()
- } else {
- log.debug(
- s"Received request for grid power values for a NEW sweep (request: {}, my: {})",
- requestSweepNo,
- gridAgentBaseData.currentSweepNo
- )
- self ! PrepareNextSweepTrigger(currentTick)
+ Behaviors.same
+ } else {
+ ctx.log.debug(
+ s"Received request for grid power values for a NEW sweep (request: {}, my: {})",
+ requestSweepNo,
+ gridAgentBaseData.currentSweepNo,
+ )
+ ctx.self ! PrepareNextSweepTrigger(currentTick)
- stash()
- stay() using gridAgentBaseData.copy(currentSweepNo = requestSweepNo)
- }
+ buffer.stash(msg)
- // / after power flow calc for this sweepNo
- case Event(
- RequestGridPowerMessage(_, requestedNodeUuids),
- powerFlowDoneData @ PowerFlowDoneData(
- gridAgentBaseData,
- powerFlowResult,
- pendingRequestAnswers
+ simulateGrid(
+ gridAgentBaseData.copy(currentSweepNo = requestSweepNo),
+ currentTick,
+ )
+ }
+
+ // after power flow calc for this sweepNo
+ case (
+ WrappedPowerMessage(
+ RequestGridPowerMessage(_, requestedNodeUuids, sender)
+ ),
+ powerFlowDoneData @ PowerFlowDoneData(
+ gridAgentBaseData,
+ powerFlowResult,
+ pendingRequestAnswers,
+ ),
+ ) =>
+ /* Determine the subgrid number of the grid agent, that has sent the request */
+ val firstRequestedNodeUuid = requestedNodeUuids.headOption.getOrElse(
+ throw new DBFSAlgorithmException(
+ "Did receive a grid power request but without specified nodes"
+ )
)
- ) =>
- /* Determine the subgrid number of the grid agent, that has sent the request */
- val firstRequestedNodeUuid = requestedNodeUuids.headOption.getOrElse(
- throw new DBFSAlgorithmException(
- "Did receive a grid power request but without specified nodes"
- )
- )
- gridAgentBaseData.gridEnv.subgridGateToActorRef
- .map { case (subGridGate, _) => subGridGate.superiorNode }
- .find(_.getUuid == firstRequestedNodeUuid)
- .map(_.getSubnet) match {
- case Some(requestingSubgridNumber) =>
- powerFlowResult match {
- case validNewtonRaphsonPFResult: ValidNewtonRaphsonPFResult =>
- val exchangePowers = requestedNodeUuids
- .map { nodeUuid =>
- /* Figure out the node index for each requested node */
- nodeUuid -> gridAgentBaseData.gridEnv.gridModel.nodeUuidToIndexMap
- .get(nodeUuid)
- .flatMap { nodeIndex =>
- /* Find matching node result */
- validNewtonRaphsonPFResult.nodeData.find(stateData =>
- stateData.index == nodeIndex
- )
+
+ gridAgentBaseData.gridEnv.subgridGateToActorRef
+ .map { case (subGridGate, _) => subGridGate.superiorNode }
+ .find(_.getUuid == firstRequestedNodeUuid)
+ .map(_.getSubnet) match {
+ case Some(requestingSubgridNumber) =>
+ powerFlowResult match {
+ case validNewtonRaphsonPFResult: ValidNewtonRaphsonPFResult =>
+ val exchangePowers = requestedNodeUuids
+ .map { nodeUuid =>
+ /* Figure out the node index for each requested node */
+ nodeUuid -> gridAgentBaseData.gridEnv.gridModel.nodeUuidToIndexMap
+ .get(nodeUuid)
+ .flatMap { nodeIndex =>
+ /* Find matching node result */
+ validNewtonRaphsonPFResult.nodeData.find(stateData =>
+ stateData.index == nodeIndex
+ )
+ }
+ .map {
+ case StateData(_, nodeType, _, power)
+ if nodeType == NodeType.SL =>
+ val refSystem =
+ gridAgentBaseData.gridEnv.gridModel.mainRefSystem
+ val (pInPu, qInPu) =
+ (power.real, power.imag)
+ // The power flow result data provides the nodal residual power at the slack node.
+ // A feed-in case from the inferior grid TO the superior grid leads to positive residual power at the
+ // inferior grid's *slack node* (superior grid seems to be a load to the inferior grid).
+ // To model the exchanged power from the superior grid's point of view, -1 has to be multiplied.
+ // (Inferior grid is a feed in facility to superior grid, which is negative then). Analogously for load case.
+ (
+ refSystem.pInSi(pInPu) * (-1),
+ refSystem.qInSi(qInPu) * (-1),
+ )
+ case _ =>
+ /* TODO: As long as there are no multiple slack nodes, provide "real" power only for the slack node */
+ (
+ Megawatts(0d),
+ Megavars(0d),
+ )
+ }
+ .getOrElse {
+ throw new DBFSAlgorithmException(
+ s"Got a request for power @ node with uuid $requestedNodeUuids but cannot find it in my result data!"
+ )
+ }
}
- .map {
- case StateData(_, nodeType, _, power)
- if nodeType == NodeType.SL =>
- val refSystem =
- gridAgentBaseData.gridEnv.gridModel.mainRefSystem
- val (pInPu, qInPu) =
- (power.real, power.imag)
- // The power flow result data provides the nodal residual power at the slack node.
- // A feed-in case from the inferior grid TO the superior grid leads to positive residual power at the
- // inferior grid's *slack node* (superior grid seems to be a load to the inferior grid).
- // To model the exchanged power from the superior grid's point of view, -1 has to be multiplied.
- // (Inferior grid is a feed in facility to superior grid, which is negative then). Analogously for load case.
- (
- refSystem.pInSi(pInPu) * (-1),
- refSystem.qInSi(qInPu) * (-1)
- )
- case _ =>
- /* TODO: As long as there are no multiple slack nodes, provide "real" power only for the slack node */
- (
- Megawatts(0d),
- Megavars(0d)
- )
+ .map { case (nodeUuid, (p, q)) =>
+ ProvideGridPowerMessage.ExchangePower(
+ nodeUuid,
+ p,
+ q,
+ )
}
- .getOrElse {
- throw new DBFSAlgorithmException(
- s"Got a request for power @ node with uuid $requestedNodeUuids but cannot find it in my result data!"
+
+ /* Determine the remaining replies */
+ val stillPendingRequestAnswers =
+ pendingRequestAnswers.filterNot(
+ _ == requestingSubgridNumber
+ )
+
+ // update the sweep value store and clear all received maps
+ // note: normally it is expected that this has to be done after power flow calculations but for the sake
+ // of having it only once in the code we put this here. Otherwise it would have to been put before EVERY
+ // return with a valid power flow result (currently happens already in two situations)
+ val updatedGridAgentBaseData =
+ if (stillPendingRequestAnswers.isEmpty) {
+ gridAgentBaseData.storeSweepDataAndClearReceiveMaps(
+ validNewtonRaphsonPFResult,
+ gridAgentBaseData.superiorGridNodeUuids,
+ gridAgentBaseData.inferiorGridGates,
+ )
+ } else {
+ powerFlowDoneData.copy(pendingRequestAnswers =
+ stillPendingRequestAnswers
)
}
- }
- .map { case (nodeUuid, (p, q)) =>
- ProvideGridPowerMessage.ExchangePower(
- nodeUuid,
- p,
- q
- )
- }
- /* Determine the remaining replies */
- val stillPendingRequestAnswers =
- pendingRequestAnswers.filterNot(_ == requestingSubgridNumber)
-
- // update the sweep value store and clear all received maps
- // note: normally it is expected that this has to be done after power flow calculations but for the sake
- // of having it only once in the code we put this here. Otherwise it would have to been put before EVERY
- // return with a valid power flow result (currently happens already in two situations)
- val updatedGridAgentBaseData =
- if (stillPendingRequestAnswers.isEmpty) {
- gridAgentBaseData.storeSweepDataAndClearReceiveMaps(
- validNewtonRaphsonPFResult,
- gridAgentBaseData.superiorGridNodeUuids,
- gridAgentBaseData.inferiorGridGates
+ sender ! WrappedPowerMessage(
+ ProvideGridPowerMessage(exchangePowers)
)
- } else {
- powerFlowDoneData.copy(pendingRequestAnswers =
- stillPendingRequestAnswers
- )
- }
+ simulateGrid(updatedGridAgentBaseData, currentTick)
- stay() replying
- ProvideGridPowerMessage(
- exchangePowers
- ) using updatedGridAgentBaseData
+ case _: FailedNewtonRaphsonPFResult =>
+ sender ! WrappedPowerMessage(FailedPowerFlow)
+ simulateGrid(gridAgentBaseData, currentTick)
+ }
+ case None =>
+ /* It is not possible to determine, who has asked */
+ ctx.log.error(
+ "I got a grid power request from a subgrid I don't know. Can't answer it properly."
+ )
- case _: FailedNewtonRaphsonPFResult =>
- stay() replying FailedPowerFlow using gridAgentBaseData
+ sender ! WrappedPowerMessage(FailedPowerFlow)
+ Behaviors.same
}
- case None =>
- /* It is not possible to determine, who has asked */
- log.error(
- "I got a grid power request from a subgrid I don't know. Can't answer it properly."
- )
- stay() replying FailedPowerFlow using gridAgentBaseData
- }
-
- // called when a grid power values request from a superior grid is received
- // which is similar to a new sweep and causes a) a power flow with updated slack voltage values and
- // b) afterwards a request for updated power values from inferior grids and assets with updated voltage values
- // based on the just carried out power flow
- case Event(
- PrepareNextSweepTrigger(_),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- // request the updated slack voltages from the superior grid
- askSuperiorGridsForSlackVoltages(
- gridAgentBaseData.currentSweepNo,
- gridAgentBaseData.gridEnv.subgridGateToActorRef,
- gridAgentBaseData.superiorGridGates,
- gridAgentBaseData.powerFlowParams.sweepTimeout
- )
- log.debug(s"Going to {}", HandlePowerFlowCalculations)
-
- goto(HandlePowerFlowCalculations) using gridAgentBaseData
-
- // last step which should includes a) information on inferior grids about finish and
- // b) cleanup of receiveMaps and sweepStore
- case Event(
- FinishGridSimulationTrigger(currentTick),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- // inform my child grids about the end of this grid simulation
- gridAgentBaseData.inferiorGridGates
- .map {
- gridAgentBaseData.gridEnv.subgridGateToActorRef(_)
- }
- .distinct
- .foreach(_ ! FinishGridSimulationTrigger(currentTick))
-
- // inform every system participant about the end of this grid simulation
- gridAgentBaseData.gridEnv.nodeToAssetAgents.foreach { case (_, actors) =>
- actors.foreach(actor => {
- actor ! FinishGridSimulationTrigger(
- currentTick
- )
- })
- }
+ // called when a grid power values request from a superior grid is received
+ // which is similar to a new sweep and causes a) a power flow with updated slack voltage values and
+ // b) afterwards a request for updated power values from inferior grids and assets with updated voltage values
+ // based on the just carried out power flow
+ case (
+ PrepareNextSweepTrigger(_),
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ // request the updated slack voltages from the superior grid
+ askSuperiorGridsForSlackVoltages(
+ gridAgentBaseData.currentSweepNo,
+ gridAgentBaseData.gridEnv.subgridGateToActorRef,
+ gridAgentBaseData.superiorGridGates,
+ gridAgentBaseData.powerFlowParams.sweepTimeout,
+ )(ctx)
+
+ ctx.log.debug(s"Going to HandlePowerFlowCalculation")
+
+ handlePowerFlowCalculations(gridAgentBaseData, currentTick)
+
+ // last step which should includes a) information on inferior grids about finish and
+ // b) cleanup of receiveMaps and sweepStore
+ case (
+ FinishGridSimulationTrigger(currentTick),
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ // inform my child grids about the end of this grid simulation
+ gridAgentBaseData.inferiorGridGates
+ .map {
+ gridAgentBaseData.gridEnv.subgridGateToActorRef(_)
+ }
+ .distinct
+ .foreach(
+ _ ! FinishGridSimulationTrigger(currentTick)
+ )
- // notify listener about the results
- log.debug("Calculate results and sending the results to the listener ...")
- createAndSendPowerFlowResults(
- gridAgentBaseData,
- currentTick.toDateTime(simStartTime)
- )
+ // inform every system participant about the end of this grid simulation
+ gridAgentBaseData.gridEnv.nodeToAssetAgents.foreach {
+ case (_, actors) =>
+ actors.foreach { actor =>
+ actor ! FinishParticipantSimulation(currentTick)
+ }
+ }
- // do my cleanup stuff
- log.debug("Doing my cleanup stuff")
+ // notify listener about the results
+ ctx.log.debug(
+ "Calculate results and sending the results to the listener ..."
+ )
+ createAndSendPowerFlowResults(
+ gridAgentBaseData,
+ currentTick.toDateTime(constantData.simStartTime),
+ )(ctx.log, constantData)
- // / clean copy of the gridAgentBaseData
- val cleanedGridAgentBaseData = GridAgentBaseData.clean(
- gridAgentBaseData,
- gridAgentBaseData.superiorGridNodeUuids,
- gridAgentBaseData.inferiorGridGates
- )
+ // do my cleanup stuff
+ ctx.log.debug("Doing my cleanup stuff")
- // / release tick for the whole simulation (StartGridSimulationTrigger)
- releaseTick()
+ // / clean copy of the gridAgentBaseData
+ val cleanedGridAgentBaseData = GridAgentBaseData.clean(
+ gridAgentBaseData,
+ gridAgentBaseData.superiorGridNodeUuids,
+ gridAgentBaseData.inferiorGridGates,
+ )
- // / inform scheduler that we are done with the whole simulation and request new trigger for next time step
- environmentRefs.scheduler ! Completion(
- self.toTyped,
- Some(currentTick + resolution)
- )
+ // / inform scheduler that we are done with the whole simulation and request new trigger for next time step
+ constantData.environmentRefs.scheduler ! Completion(
+ constantData.activationAdapter,
+ Some(currentTick + constantData.resolution),
+ )
- // return to Idle
- goto(Idle) using cleanedGridAgentBaseData
+ // return to Idle
+ idle(cleanedGridAgentBaseData)
+ case _ =>
+ // preventing "match may not be exhaustive"
+ Behaviors.unhandled
+ }
}
- /** Every power flow calculation should take place here. Generally used for
- * power flow calculations only and only if all data required are already
- * received as requested.
+ /** Method that defines the [[Behavior]] for handling the power flow
+ * calculations. Generally used for power flow calculations only and only if
+ * all data required are already received as requested.
+ *
+ * @param gridAgentData
+ * state data of the actor
+ * @param currentTick
+ * current simulation tick
+ * @return
+ * a [[Behavior]]
*/
- when(HandlePowerFlowCalculations) {
-
- // main method for power flow calculations
- case Event(
- DoPowerFlowTrigger(currentTick, _),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- log.debug(
- "Received the following power values to the corresponding nodes: {}",
- gridAgentBaseData.receivedValueStore.nodeToReceivedPower
- )
-
- val gridModel = gridAgentBaseData.gridEnv.gridModel
-
- val (operatingPoint, slackNodeVoltages) = composeOperatingPoint(
- gridModel.gridComponents.nodes,
- gridModel.gridComponents.transformers,
- gridModel.gridComponents.transformers3w,
- gridModel.nodeUuidToIndexMap,
- gridAgentBaseData.receivedValueStore,
- gridModel.mainRefSystem
- )
-
- newtonRaphsonPF(
- gridModel,
- gridAgentBaseData.powerFlowParams.maxIterations,
- operatingPoint,
- slackNodeVoltages
- )(gridAgentBaseData.powerFlowParams.epsilon) match {
- // if res is valid, ask our assets (if any) for updated power values based on the newly determined nodal voltages
- case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
- log.debug(
- "{}",
- composeValidNewtonRaphsonPFResultVoltagesDebugString(
- validPowerFlowResult,
- gridModel
- )
+ private def handlePowerFlowCalculations(
+ gridAgentData: GridAgentData,
+ currentTick: Long,
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = Behaviors.receivePartial {
+ case (ctx, message) =>
+ (message, gridAgentData) match {
+ // main method for power flow calculations
+ case (
+ DoPowerFlowTrigger(currentTick, _),
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ ctx.log.debug(
+ "Received the following power values to the corresponding nodes: {}",
+ gridAgentBaseData.receivedValueStore.nodeToReceivedPower,
)
- val powerFlowDoneData =
- PowerFlowDoneData(gridAgentBaseData, validPowerFlowResult)
+ val gridModel = gridAgentBaseData.gridEnv.gridModel
- val sweepValueStoreOpt = Some(
- SweepValueStore(
- validPowerFlowResult,
- gridModel.gridComponents.nodes,
- gridModel.nodeUuidToIndexMap
- )
- )
- askForAssetPowers(
- currentTick,
- sweepValueStoreOpt,
- gridAgentBaseData.gridEnv.nodeToAssetAgents,
+ val (operatingPoint, slackNodeVoltages) = composeOperatingPoint(
+ gridModel.gridComponents.nodes,
+ gridModel.gridComponents.transformers,
+ gridModel.gridComponents.transformers3w,
+ gridModel.nodeUuidToIndexMap,
+ gridAgentBaseData.receivedValueStore,
gridModel.mainRefSystem,
- gridAgentBaseData.powerFlowParams.sweepTimeout
- ) match {
- case None =>
- // when we don't have assets we can skip another request for different asset behaviour due to changed
- // voltage values and go back to SimulateGrid directly
- log.debug(
- s"No generation or load assets in the grid. Going back to {}.",
- SimulateGrid
- )
- unstashAll() // we can answer the stashed grid power requests now
- goto(SimulateGrid) using powerFlowDoneData
- case Some(_) =>
- // will return a future based on the `ask-pattern` which will be processed below
- stay() using powerFlowDoneData
- }
-
- case failedNewtonRaphsonPFResult: FailedNewtonRaphsonPFResult =>
- val powerFlowDoneData =
- PowerFlowDoneData(gridAgentBaseData, failedNewtonRaphsonPFResult)
- log.warning(
- "Power flow calculation before asking for updated powers 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
- }
- // handler for the future provided by `askForAssetPowers` to check if there are any changes in generation/load
- // of assets based on updated nodal voltages
- case Event(
- receivedPowerValues: ReceivedPowerValues,
- powerFlowDoneData: PowerFlowDoneData
- ) =>
- val gridAgentBaseData = powerFlowDoneData.gridAgentBaseData
-
- // check if we have changed values from our assets
- // 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 (_, _: AssetPowerChangedMessage) => true
- case _ => false
- }
-
- if (changed) {
- log.debug(
- "Assets have changed their exchanged power with the grid. Update nodal powers and prepare new power flow."
- )
- val updatedGridAgentBaseData: GridAgentBaseData =
- receivedPowerValues match {
- case receivedPowers: ReceivedPowerValues =>
- gridAgentBaseData.updateWithReceivedPowerValues(
- receivedPowers,
- replace = true
+ newtonRaphsonPF(
+ gridModel,
+ gridAgentBaseData.powerFlowParams.maxIterations,
+ operatingPoint,
+ slackNodeVoltages,
+ )(gridAgentBaseData.powerFlowParams.epsilon)(ctx.log) match {
+ // if res is valid, ask our assets (if any) for updated power values based on the newly determined nodal voltages
+ case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
+ ctx.log.debug(
+ "{}",
+ composeValidNewtonRaphsonPFResultVoltagesDebugString(
+ validPowerFlowResult,
+ gridModel,
+ ),
)
- case unknownValuesReceived =>
- throw new DBFSAlgorithmException(
- s"Received unsuitable values: $unknownValuesReceived"
+
+ val powerFlowDoneData =
+ PowerFlowDoneData(gridAgentBaseData, validPowerFlowResult)
+
+ val sweepValueStoreOpt = Some(
+ SweepValueStore(
+ validPowerFlowResult,
+ gridModel.gridComponents.nodes,
+ gridModel.nodeUuidToIndexMap,
+ )
)
- }
- // check if we have enough data for a power flow calculation
- // if yes, go to the powerflow
- // if no, stay and wait
- val readyForPowerFlow =
- updatedGridAgentBaseData.allRequestedDataReceived
- log.debug(
- "{}",
- if (readyForPowerFlow)
- "Got answers for all my requests for Slack Voltages and Power Values."
- else
- "Still waiting for answers my requests for Slack Voltages and Power Values."
- )
+ if (
+ askForAssetPowers(
+ currentTick,
+ sweepValueStoreOpt,
+ gridAgentBaseData.gridEnv.nodeToAssetAgents,
+ gridModel.mainRefSystem,
+ gridAgentBaseData.powerFlowParams.sweepTimeout,
+ )(ctx)
+ ) {
+ // will return a future based on the `ask-pattern` which will be processed below
+ handlePowerFlowCalculations(powerFlowDoneData, currentTick)
+ } else {
+ // when we don't have assets we can skip another request for different asset behaviour due to changed
+ // voltage values and go back to SimulateGrid directly
+ ctx.log.debug(
+ s"No generation or load assets in the grid. Going back to SimulateGrid."
+ )
- goToPowerFlowCalculationOrStay(
- readyForPowerFlow,
- updatedGridAgentBaseData
- )
+ // we can answer the stashed grid power requests now
+ buffer.unstashAll(
+ simulateGrid(powerFlowDoneData, currentTick)
+ )
+ }
- } else {
- // no changes from assets, we want to go back to SimulateGrid and report the LF results to our parent grids if any requests
- log.debug(
- s"Assets have not changed their exchanged power or no voltage dependent behaviour. Going back to {}.",
- SimulateGrid
- )
- unstashAll() // we can answer the stashed grid power requests now
- goto(SimulateGrid) using powerFlowDoneData
+ case failedNewtonRaphsonPFResult: FailedNewtonRaphsonPFResult =>
+ val powerFlowDoneData =
+ PowerFlowDoneData(
+ gridAgentBaseData,
+ failedNewtonRaphsonPFResult,
+ )
+ ctx.log.warn(
+ "Power flow calculation before asking for updated powers did finally not converge!"
+ )
+ // we can answer the stashed grid power requests now and report a failed power flow back
+ buffer.unstashAll(simulateGrid(powerFlowDoneData, currentTick))
+ }
- }
+ // handler for the future provided by `askForAssetPowers` to check if there are any changes in generation/load
+ // of assets based on updated nodal voltages
+ case (
+ receivedPowerValues: ReceivedPowerValues,
+ powerFlowDoneData: PowerFlowDoneData,
+ ) =>
+ val gridAgentBaseData = powerFlowDoneData.gridAgentBaseData
+
+ // check if we have changed values from our assets
+ // 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 (_, _: AssetPowerChangedMessage) => true
+ case _ => false
+ }
- // executed after request from the superior grid to execute a new sweep (forward sweep state)
- // this means we requested an update of the slack voltage values, but for now don't request (and hence don't expect)
- // updated power values for our power flow calculations
- case Event(
- receivedSlackValues: ReceivedSlackVoltageValues,
- gridAgentBaseData: GridAgentBaseData
- ) =>
- log.debug(
- "Received Slack values for new forward sweep with same power but updated voltage values"
- )
+ if (changed) {
+ ctx.log.debug(
+ "Assets have changed their exchanged power with the grid. Update nodal powers and prepare new power flow."
+ )
+ val updatedGridAgentBaseData: GridAgentBaseData =
+ receivedPowerValues match {
+ case receivedPowers: ReceivedPowerValues =>
+ gridAgentBaseData.updateWithReceivedPowerValues(
+ receivedPowers,
+ replace = true,
+ )
+ case unknownValuesReceived =>
+ throw new DBFSAlgorithmException(
+ s"Received unsuitable values: $unknownValuesReceived"
+ )
+ }
- // power flow
- val gridModel = gridAgentBaseData.gridEnv.gridModel
- val previousSweepData = gridAgentBaseData.sweepValueStores.getOrElse(
- gridAgentBaseData.currentSweepNo - 1,
- throw new DBFSAlgorithmException(
- s"$actorName Unable to get results from previous sweep ${gridAgentBaseData.currentSweepNo - 1}!"
- )
- )
+ // check if we have enough data for a power flow calculation
+ // if yes, go to the powerflow
+ // if no, stay and wait
+ val readyForPowerFlow =
+ updatedGridAgentBaseData.allRequestedDataReceived
+ ctx.log.debug(
+ "{}",
+ if (readyForPowerFlow)
+ "Got answers for all my requests for Slack Voltages and Power Values."
+ else
+ "Still waiting for answers my requests for Slack Voltages and Power Values.",
+ )
- val (operatingPoint, slackNodeVoltages) =
- composeOperatingPointWithUpdatedSlackVoltages(
- receivedSlackValues,
- previousSweepData.sweepData,
- gridModel.gridComponents.transformers,
- gridModel.gridComponents.transformers3w,
- gridModel.mainRefSystem
- )
+ goToPowerFlowCalculationOrStay(
+ readyForPowerFlow,
+ updatedGridAgentBaseData,
+ currentTick,
+ handlePowerFlowCalculations,
+ )(ctx, constantData, buffer)
- newtonRaphsonPF(
- gridModel,
- gridAgentBaseData.powerFlowParams.maxIterations,
- operatingPoint,
- slackNodeVoltages
- )(gridAgentBaseData.powerFlowParams.epsilon) match {
- case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
- log.debug(
- "{}",
- composeValidNewtonRaphsonPFResultVoltagesDebugString(
- validPowerFlowResult,
- gridModel
+ } else {
+ // no changes from assets, we want to go back to SimulateGrid and report the LF results to our parent grids if any requests
+ ctx.log.debug(
+ s"Assets have not changed their exchanged power or no voltage dependent behaviour. Going back to SimulateGrid."
)
+ // we can answer the stashed grid power requests now
+ buffer.unstashAll(simulateGrid(powerFlowDoneData, currentTick))
+ }
+
+ // executed after request from the superior grid to execute a new sweep (forward sweep state)
+ // this means we requested an update of the slack voltage values, but for now don't request (and hence don't expect)
+ // updated power values for our power flow calculations
+ case (
+ receivedSlackValues: ReceivedSlackVoltageValues,
+ gridAgentBaseData: GridAgentBaseData,
+ ) =>
+ ctx.log.debug(
+ "Received Slack values for new forward sweep with same power but updated voltage values"
)
- // update the data
- val sweepValueStore = SweepValueStore(
- validPowerFlowResult,
- gridModel.gridComponents.nodes,
- gridModel.nodeUuidToIndexMap
+ // power flow
+ val gridModel = gridAgentBaseData.gridEnv.gridModel
+ val previousSweepData = gridAgentBaseData.sweepValueStores.getOrElse(
+ gridAgentBaseData.currentSweepNo - 1,
+ throw new DBFSAlgorithmException(
+ s"${gridAgentBaseData.actorName}: Unable to get results from previous sweep ${gridAgentBaseData.currentSweepNo - 1}!"
+ ),
)
- 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,
+ val (operatingPoint, slackNodeVoltages) =
+ composeOperatingPointWithUpdatedSlackVoltages(
+ receivedSlackValues,
+ previousSweepData.sweepData,
+ gridModel.gridComponents.transformers,
+ gridModel.gridComponents.transformers3w,
gridModel.mainRefSystem,
- gridAgentBaseData.powerFlowParams.sweepTimeout
)
- // 2. inferior grids p/q values
- val askForInferiorGridPowersOpt =
- askInferiorGridsForPowers(
- gridAgentBaseData.currentSweepNo,
- gridAgentBaseData.gridEnv.subgridGateToActorRef,
- gridAgentBaseData.inferiorGridGates,
- gridAgentBaseData.powerFlowParams.sweepTimeout
- )
+ newtonRaphsonPF(
+ gridModel,
+ gridAgentBaseData.powerFlowParams.maxIterations,
+ operatingPoint,
+ slackNodeVoltages,
+ )(gridAgentBaseData.powerFlowParams.epsilon)(ctx.log) match {
+ case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
+ ctx.log.debug(
+ "{}",
+ composeValidNewtonRaphsonPFResultVoltagesDebugString(
+ validPowerFlowResult,
+ gridModel,
+ ),
+ )
- // 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."
+ // 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,
+ )(ctx)
+
+ // 2. inferior grids p/q values
+ val askForInferiorGridPowersOpt =
+ askInferiorGridsForPowers(
+ gridAgentBaseData.currentSweepNo,
+ gridAgentBaseData.gridEnv.subgridGateToActorRef,
+ gridAgentBaseData.inferiorGridGates,
+ gridAgentBaseData.powerFlowParams.sweepTimeout,
+ )(ctx)
+
+ // 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
+ if (!askForAssetPowersOpt && !askForInferiorGridPowersOpt) {
+ ctx.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."
+ )
- val powerFlowDoneData =
- PowerFlowDoneData(gridAgentBaseData, validPowerFlowResult)
+ val powerFlowDoneData =
+ PowerFlowDoneData(gridAgentBaseData, validPowerFlowResult)
- unstashAll() // we can answer the stashed grid power requests now
- goto(SimulateGrid) using powerFlowDoneData
+ // we can answer the stashed grid power requests now
+ buffer.unstashAll(
+ simulateGrid(powerFlowDoneData, currentTick)
+ )
+ } else {
+ ctx.log.debug(
+ "Going back to SimulateGrid and wait for my assets or inferior grids to return."
+ )
- case _ =>
- log.debug(
- "Going back to SimulateGrid and wait for my assets or inferior grids to return."
- )
+ // go back to simulate grid
+ simulateGrid(
+ gridAgentBaseData
+ .updateWithReceivedSlackVoltages(receivedSlackValues)
+ .copy(sweepValueStores = updatedSweepValueStore),
+ currentTick,
+ )
+ }
- // go back to simulate grid
- goto(SimulateGrid) using gridAgentBaseData
- .updateWithReceivedSlackVoltages(receivedSlackValues)
- .copy(sweepValueStores = updatedSweepValueStore)
+ case failedNewtonRaphsonPFResult: FailedNewtonRaphsonPFResult =>
+ val powerFlowDoneData =
+ PowerFlowDoneData(
+ gridAgentBaseData,
+ failedNewtonRaphsonPFResult,
+ )
+ ctx.log.warn(
+ "Power flow with updated slack voltage did finally not converge!"
+ )
+ // we can answer the stashed grid power requests now and report a failed power flow back
+ buffer.unstashAll(simulateGrid(powerFlowDoneData, currentTick))
}
- case failedNewtonRaphsonPFResult: FailedNewtonRaphsonPFResult =>
- val powerFlowDoneData =
- PowerFlowDoneData(
- gridAgentBaseData,
- failedNewtonRaphsonPFResult
- )
- log.warning(
- "Power flow with updated slack voltage did finally not converge!"
+ // happens only when we received slack data and power values before we received a request to provide grid data
+ // (only possible when first simulation triggered and this agent is faster in this state as the request
+ // by a superior grid arrives)
+ case (
+ msg: WrappedPowerMessage,
+ _: GridAgentBaseData,
+ ) =>
+ ctx.log.debug(
+ "Received Request for Grid Power too early. Stashing away"
)
- unstashAll() // we can answer the stashed grid power requests now and report a failed power flow back
- goto(SimulateGrid) using powerFlowDoneData
- }
+ buffer.stash(msg)
+ Behaviors.same
+
+ // happens only when we received slack data and power values before we received a request to provide grid
+ // (only possible when first simulation triggered and this agent is faster
+ // with its power flow calculation in this state as the request by a superior grid arrives)
+ case (
+ msg: WrappedPowerMessage,
+ _: PowerFlowDoneData,
+ ) =>
+ ctx.log.debug(
+ "Received Request for Grid Power too early. Stashing away"
+ )
- // happens only when we received slack data and power values before we received a request to provide grid data
- // (only possible when first simulation triggered and this agent is faster in this state as the request
- // by a superior grid arrives)
- case Event(
- _: RequestGridPowerMessage,
- _: GridAgentBaseData
- ) =>
- log.debug("Received Request for Grid Power too early. Stashing away")
- stash()
- stay()
-
- // happens only when we received slack data and power values before we received a request to provide gride
- // (only possible when first simulation triggered and this agent is faster
- // with its power flow calculation in this state as the request by a superior grid arrives)
- case Event(
- _: RequestGridPowerMessage,
- _: PowerFlowDoneData
- ) =>
- log.debug("Received Request for Grid Power too early. Stashing away")
- stash()
- stay()
+ buffer.stash(msg)
+ Behaviors.same
+ case _ =>
+ // preventing "match may not be exhaustive"
+ Behaviors.unhandled
+ }
}
- // should be reached by the superior (dummy) grid agent only
- when(CheckPowerDifferences) {
+ /** Method used for checking the power difference. This method should only
+ * be reached by the superior (dummy) grid agent.
+ * @param gridAgentBaseData
+ * state data of the actor
+ * @return
+ * a [[Behavior]]
+ */
+ private def checkPowerDifferences(
+ gridAgentBaseData: GridAgentBaseData
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = Behaviors.receivePartial {
- case Event(
- CheckPowerDifferencesTrigger(currentTick),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- log.debug("Starting the power differences check ...")
+ case (ctx, CheckPowerDifferencesTrigger(currentTick)) =>
+ ctx.log.debug("Starting the power differences check ...")
val currentSweepNo = gridAgentBaseData.currentSweepNo
val gridModel = gridAgentBaseData.gridEnv.gridModel
@@ -759,7 +855,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
gridModel.nodeUuidToIndexMap,
gridAgentBaseData.receivedValueStore,
gridModel.mainRefSystem,
- targetVoltageFromReceivedData = false
+ targetVoltageFromReceivedData = false,
)
/* Regarding the power flow result of this grid, there are two cases. If this is the "highest" grid in a
@@ -770,22 +866,22 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
val nodeData = operationPoint.map(StateData(_))
ValidNewtonRaphsonPFResult(-1, nodeData, DenseMatrix(0d, 0d))
} else {
- log.debug(
+ ctx.log.debug(
"This grid contains a three winding transformer. Perform power flow calculations before assessing the power deviations."
)
newtonRaphsonPF(
gridModel,
gridAgentBaseData.powerFlowParams.maxIterations,
operationPoint,
- slackNodeVoltages
- )(gridAgentBaseData.powerFlowParams.epsilon) match {
+ slackNodeVoltages,
+ )(gridAgentBaseData.powerFlowParams.epsilon)(ctx.log) match {
case validPowerFlowResult: ValidNewtonRaphsonPFResult =>
- log.debug(
+ ctx.log.debug(
"{}",
composeValidNewtonRaphsonPFResultVoltagesDebugString(
validPowerFlowResult,
- gridModel
- )
+ gridModel,
+ ),
)
validPowerFlowResult
case result: PowerFlowResult.FailedPowerFlowResult =>
@@ -798,20 +894,24 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
.storeSweepDataAndClearReceiveMaps(
validResult,
gridAgentBaseData.superiorGridNodeUuids,
- gridAgentBaseData.inferiorGridGates
+ gridAgentBaseData.inferiorGridGates,
)
.copy(currentSweepNo = currentSweepNo + 1)
// the difference is checked @ the higher nodes of our transformers => the slack nodes
// if we are either in the first backward sweep OR if the deviation is bigger as allowed, we need a second sweep
if (gridAgentBaseData.sweepValueStores.isEmpty) {
- log.debug("Sweep value store is empty. Starting a second sweep ...")
+ ctx.log.debug(
+ "Sweep value store is empty. Starting a second sweep ..."
+ )
goToSimulateGridForNextSweepWith(
updatedGridAgentBaseData,
- currentTick
+ currentTick,
)
} else {
- log.debug("Sweep value store is not empty. Check for deviation ...")
+ ctx.log.debug(
+ "Sweep value store is not empty. Check for deviation ..."
+ )
// calculate deviation vector for all nodes
val previousSweepNodePower: DenseVector[Complex] =
@@ -821,7 +921,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
currentSweepNo - 1,
throw new DBFSAlgorithmException(
s"No data for previous sweep with no ${currentSweepNo - 1} available!"
- )
+ ),
)
.sweepData
.map(_.stateData.power)
@@ -834,7 +934,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
currentSweepNo,
throw new DBFSAlgorithmException(
s"No data for current sweep with no $currentSweepNo available!"
- )
+ ),
)
.sweepData
.map(_.stateData.power)
@@ -850,65 +950,68 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
Math.abs(complex.imag) >= allowedDeviation
}) match {
case Some(deviation) => // next sweep
- log.debug(
+ ctx.log.debug(
"Deviation between the last two sweeps: {}",
- deviation
+ deviation,
)
goToSimulateGridForNextSweepWith(
updatedGridAgentBaseData,
- currentTick
+ currentTick,
)
case None => // we're done
- log.debug("We found a result! :-)")
+ ctx.log.debug("We found a result! :-)")
- log.debug(
+ ctx.log.debug(
"Final deviation: {}",
- (previousSweepNodePower - currentSweepNodePower).toScalaVector
+ (previousSweepNodePower - currentSweepNodePower).toScalaVector,
)
// go back to SimulateGrid and trigger a finish
- self ! FinishGridSimulationTrigger(currentTick)
- goto(SimulateGrid)
-
+ ctx.self ! FinishGridSimulationTrigger(currentTick)
+ simulateGrid(gridAgentBaseData, currentTick)
}
}
case failedResult: PowerFlowResult.FailedPowerFlowResult =>
- log.warning(
+ ctx.log.warn(
"Power flow for high voltage branch of three winding transformer failed after {} iterations. Cause: {}",
failedResult.iteration,
- failedResult.cause
+ failedResult.cause,
)
- self ! FinishGridSimulationTrigger(currentTick)
- handlePowerFlowFailure(gridAgentBaseData)
- goto(SimulateGrid) using gridAgentBaseData
+ ctx.self ! FinishGridSimulationTrigger(currentTick)
+ handlePowerFlowFailure(gridAgentBaseData, currentTick, ctx)
}
}
/** Checks if all data has been received and if yes checks if the there are
* any failed power flow indications from inferior grids. If both == true,
- * then no state change is triggered but the sweep value store is updated
- * with a [[FailedPowerFlow]] information as well, the now used data is set
- * to [[PowerFlowDoneData]] and this is escalated to the superior grid(s). If
- * there is no [[FailedPowerFlow]] in the [[GridAgentBaseData]] a state
- * transition to [[HandlePowerFlowCalculations]] is triggered.
+ * then no [[Behavior]] change is triggered but the sweep value store is
+ * updated with a [[FailedPowerFlow]] information as well, the now used data
+ * is set to [[PowerFlowDoneData]] and this is escalated to the superior
+ * grid(s). If there is no [[FailedPowerFlow]] in the [[GridAgentBaseData]] a
+ * behavior transition to [[handlePowerFlowCalculations]] is triggered.
*
- * If allReceived == false, no state transition is triggered
+ * If allReceived == false, no [[Behavior]] transition is triggered
*
* @param allReceived
* indicates if all requested data has been received
* @param gridAgentBaseData
* the current or updated data of the [[GridAgent]]
* @return
- * either the same state the agent is currently in or a transition to
- * [[HandlePowerFlowCalculations]]
+ * either the same behavior the agent is currently in or a transition to
+ * [[handlePowerFlowCalculations]]
*/
private def goToPowerFlowCalculationOrStay(
allReceived: Boolean,
- gridAgentBaseData: GridAgentBaseData
- ): FSM.State[AgentState, GridAgentData] = {
-
+ gridAgentBaseData: GridAgentBaseData,
+ currentTick: Long,
+ behavior: (GridAgentData, Long) => Behavior[GridAgentMessage],
+ )(implicit
+ ctx: ActorContext[GridAgentMessage],
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] =
if (allReceived) {
- log.debug(
+ ctx.log.debug(
"All power values of inferior grids, assets + voltage superior grid slack voltages received."
)
@@ -918,27 +1021,33 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
.exists(v => v.exists(k => k._2.contains(FailedPowerFlow)))
if (powerFlowFailedSomewhereInInferior) {
- log.warning("Received Failed Power Flow Result. Escalate to my parent.")
- unstashAll() // we want to answer the requests from our parent
- stay() using PowerFlowDoneData(
+ ctx.log.warn(
+ "Received Failed Power Flow Result. Escalate to my parent."
+ )
+
+ // we want to answer the requests from our parent
+ val powerFlowDoneData = PowerFlowDoneData(
gridAgentBaseData,
- FailedNewtonRaphsonPFResult(-1, CalculationFailed)
+ FailedNewtonRaphsonPFResult(-1, CalculationFailed),
)
+
+ buffer.unstashAll(behavior(powerFlowDoneData, currentTick))
} else {
- self ! DoPowerFlowTrigger(currentTick, gridAgentBaseData.currentSweepNo)
- goto(HandlePowerFlowCalculations) using gridAgentBaseData
+ ctx.self ! DoPowerFlowTrigger(
+ currentTick,
+ gridAgentBaseData.currentSweepNo,
+ )
+ handlePowerFlowCalculations(gridAgentBaseData, currentTick)
}
} else {
- log.debug(
+ ctx.log.debug(
"Still waiting for asset or grid power values or slack voltage information of inferior grids"
)
- stay() using gridAgentBaseData
+ behavior(gridAgentBaseData, currentTick)
}
- }
-
/** Normally only reached by the superior (dummy) agent!
*
* Checks if all data has been received and if yes checks if the there are
@@ -947,8 +1056,8 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* this step is skipped and the simulation goes on or this leads to a
* termination of the simulation due to a failed power flow calculation.
*
- * If there is no [[FailedPowerFlow]] in the [[GridAgentBaseData]] a state
- * transition to [[CheckPowerDifferences]] is triggered.
+ * If there is no [[FailedPowerFlow]] in the [[GridAgentBaseData]] a
+ * [[Behavior]] transition to [[checkPowerDifferences]] is triggered.
*
* If allReceived == false, no state transition is triggered
*
@@ -959,15 +1068,23 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* @param gridAgentBaseData
* the current or updated data of the [[GridAgent]]
* @return
- * either the same state the agent is currently in or a transition to
- * [[CheckPowerDifferences]]
+ * either the same behavior the agent is currently in or a transition to
+ * [[checkPowerDifferences]]
*/
private def goToCheckPowerDifferencesOrStay(
allReceived: Boolean,
- gridAgentBaseData: GridAgentBaseData
- ): FSM.State[AgentState, GridAgentData] = {
+ gridAgentBaseData: GridAgentBaseData,
+ currentTick: Long,
+ behavior: (GridAgentData, Long) => Behavior[GridAgentMessage],
+ )(implicit
+ ctx: ActorContext[GridAgentMessage],
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = {
if (allReceived) {
- log.debug("All power values of child assets + inferior grids received.")
+ ctx.log.debug(
+ "All power values of child assets + inferior grids received."
+ )
// if our superior grid received a FailedPowerFlow from the inferior grids it has to trigger a finish
// of the simulation which will either result in a skip of this time step OR a termination of the simulation run
@@ -979,60 +1096,73 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
)
)
if (powerFlowFailedSomewhere) {
- log.warning("Power flow failed! This incident will be reported!")
- self ! FinishGridSimulationTrigger(currentTick)
- handlePowerFlowFailure(gridAgentBaseData)
- goto(SimulateGrid) using gridAgentBaseData
+ ctx.log.warn("Power flow failed! This incident will be reported!")
+ ctx.self ! FinishGridSimulationTrigger(currentTick)
+
+ handlePowerFlowFailure(gridAgentBaseData, currentTick, ctx)
} else {
- self ! CheckPowerDifferencesTrigger(currentTick)
- goto(CheckPowerDifferences) using gridAgentBaseData
+ ctx.self ! CheckPowerDifferencesTrigger(currentTick)
+ checkPowerDifferences(gridAgentBaseData)
}
} else {
- log.debug(
+ ctx.log.debug(
"Still waiting for asset or grid power values or slack voltage information of inferior grids"
)
- stay() using gridAgentBaseData
+ behavior(gridAgentBaseData, currentTick)
}
}
+ /** Method for handling failed power flows.
+ * @param gridAgentBaseData
+ * state data of the actor
+ * @param currentTick
+ * of the simulation
+ */
private def handlePowerFlowFailure(
- gridAgentBaseData: GridAgentBaseData
- ): Unit = {
- environmentRefs.runtimeEventListener ! PowerFlowFailed
+ gridAgentBaseData: GridAgentBaseData,
+ currentTick: Long,
+ ctx: ActorContext[GridAgentMessage],
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = {
+ constantData.environmentRefs.runtimeEventListener ! PowerFlowFailed
if (gridAgentBaseData.powerFlowParams.stopOnFailure) {
- log.error("Stopping because of failed power flow.")
- self ! PoisonPill
+ ctx.log.error("Stopping because of failed power flow.")
+ Behaviors.stopped
}
+
+ simulateGrid(gridAgentBaseData, currentTick)
}
/** Normally only reached by the superior (dummy) agent!
*
- * Triggers a state transition to [[SimulateGrid]], informs the
+ * Triggers a [[Behavior]] transition to [[simulateGrid]], informs the
* [[edu.ie3.simona.scheduler.Scheduler]] about the finish of this sweep and
* requests a new trigger for itself for a new sweep
*
* @param gridAgentBaseData
* the [[GridAgentBaseData]] that should be used in the next sweep in
- * [[SimulateGrid]]
+ * [[simulateGrid]]
* @param currentTick
* current tick the agent is in
* @return
- * a state transition to [[SimulateGrid]]
+ * a behavior transition to [[simulateGrid]]
*/
private def goToSimulateGridForNextSweepWith(
gridAgentBaseData: GridAgentBaseData,
- currentTick: Long
- ): FSM.State[AgentState, GridAgentData] = {
-
- releaseTick()
- environmentRefs.scheduler ! Completion(
- self.toTyped,
- Some(currentTick)
+ currentTick: Long,
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = {
+ constantData.environmentRefs.scheduler ! Completion(
+ constantData.activationAdapter,
+ Some(currentTick),
)
- goto(SimulateGrid) using gridAgentBaseData
-
+ simulateGrid(gridAgentBaseData, currentTick)
}
/** Triggers an execution of the pekko `ask` pattern for all power values of
@@ -1044,7 +1174,7 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* the current sweep value store containing the current node voltage for
* the assets
* @param nodeToAssetAgents
- * a map contains a mapping between nodes and the [[ActorRef]] s located @
+ * a map contains a mapping between nodes and the [[ActorRef]] s located \@
* those nodes
* @param refSystem
* the reference system of the [[edu.ie3.simona.model.grid.GridModel]] of
@@ -1052,71 +1182,72 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* @param askTimeout
* a timeout for the request
* @return
- * Some(Future[ReceivedPowerValues]) if this grids contains assets or None
- * if no request has been send due to non-existence of assets
+ * true if this grids contains assets or false if no request has been send
+ * due to non-existence of assets
*/
private def askForAssetPowers(
currentTick: Long,
sweepValueStore: Option[SweepValueStore],
- nodeToAssetAgents: Map[UUID, Set[ActorRef]],
+ nodeToAssetAgents: Map[UUID, Set[ActorRef[ParticipantMessage]]],
refSystem: RefSystem,
- askTimeout: Duration
- ): Option[Future[ReceivedPowerValues]] = {
-
+ askTimeout: Duration,
+ )(implicit
+ ctx: ActorContext[GridAgentMessage]
+ ): Boolean = {
implicit val timeout: PekkoTimeout = PekkoTimeout.create(askTimeout)
+ implicit val ec: ExecutionContext = ctx.executionContext
- log.debug(s"asking assets for power values: {}", nodeToAssetAgents)
-
- if (nodeToAssetAgents.values.flatten.nonEmpty)
- Some(
- Future
- .sequence(
- nodeToAssetAgents.flatten { case (nodeUuid, assetActorRefs) =>
- assetActorRefs.map(assetAgent => {
-
- val (eInPu, fInPU) =
- sweepValueStore match {
- case Some(sweepValueStore) =>
- val (eInSi, fInSi) = 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(eInSi),
- refSystem.vInPu(fInSi)
- )
- case None =>
- (
- Each(1d),
- Each(0d)
- )
- }
+ ctx.log.debug(s"asking assets for power values: {}", nodeToAssetAgents)
- (assetAgent ? RequestAssetPowerMessage(
- currentTick,
- eInPu,
- fInPU
- )).map {
- case providedPowerValuesMessage: AssetPowerChangedMessage =>
- (assetAgent, providedPowerValuesMessage)
- case assetPowerUnchangedMessage: AssetPowerUnchangedMessage =>
- (assetAgent, assetPowerUnchangedMessage)
+ if (nodeToAssetAgents.values.flatten.nonEmpty) {
+ val future = Future
+ .sequence(
+ nodeToAssetAgents.flatten { case (nodeUuid, assetActorRefs) =>
+ assetActorRefs.map(assetAgent => {
+
+ val (eInPu, fInPU) =
+ sweepValueStore match {
+ case Some(sweepValueStore) =>
+ val (eInSi, fInSi) = 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(eInSi),
+ refSystem.vInPu(fInSi),
+ )
+ case None =>
+ (
+ Each(1d),
+ Each(0d),
+ )
}
- })
- }.toVector
- )
- .map(ReceivedAssetPowerValues)
- .pipeTo(self)
- )
- else
- None
+
+ (assetAgent.toClassic ? RequestAssetPowerMessage(
+ currentTick,
+ eInPu,
+ fInPU,
+ )).map {
+ case providedPowerValuesMessage: AssetPowerChangedMessage =>
+ (assetAgent, providedPowerValuesMessage)
+ case assetPowerUnchangedMessage: AssetPowerUnchangedMessage =>
+ (assetAgent, assetPowerUnchangedMessage)
+ }
+ })
+ }.toVector
+ )
+ .map(res => ReceivedAssetPowerValues(res))
+
+ pipeToSelf(future, ctx)
+ true
+ } else false
}
/** Triggers an execution of the pekko `ask` pattern for all power values @
@@ -1130,22 +1261,28 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* @param askTimeout
* a timeout for the request
* @return
- * Some(Future[ReceivedPowerValues]) if this grids has connected inferior
- * grids or None if this no inferior grids
+ * true if this grids has connected inferior grids or false if this no
+ * inferior grids
*/
private def askInferiorGridsForPowers(
currentSweepNo: Int,
- subGridGateToActorRef: Map[SubGridGate, ActorRef],
+ subGridGateToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]],
inferiorGridGates: Seq[SubGridGate],
- askTimeout: Duration
- ): Option[Future[ReceivedPowerValues]] = {
+ askTimeout: Duration,
+ )(implicit
+ ctx: ActorContext[GridAgentMessage]
+ ): Boolean = {
implicit val timeout: PekkoTimeout = PekkoTimeout.create(askTimeout)
- log.debug(
+ implicit val ec: ExecutionContext = ctx.executionContext
+ implicit val scheduler: Scheduler = ctx.system.scheduler
+
+ ctx.log.debug(
s"asking inferior grids for power values: {}",
- inferiorGridGates
+ inferiorGridGates,
)
- Option.when(inferiorGridGates.nonEmpty) {
- Future
+
+ if (inferiorGridGates.nonEmpty) {
+ val future = Future
.sequence(
inferiorGridGates
.map { inferiorGridGate =>
@@ -1161,21 +1298,31 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
inferiorGridGates
}
.map { case (inferiorGridAgentRef, inferiorGridGateNodes) =>
- (inferiorGridAgentRef ? RequestGridPowerMessage(
- currentSweepNo,
- inferiorGridGateNodes.distinct
- )).map {
- case provideGridPowerMessage: ProvideGridPowerMessage =>
- (inferiorGridAgentRef, provideGridPowerMessage)
- case FailedPowerFlow =>
- (inferiorGridAgentRef, FailedPowerFlow)
- }
+ inferiorGridAgentRef
+ .ask[GridAgentMessage](ref =>
+ WrappedPowerMessage(
+ RequestGridPowerMessage(
+ currentSweepNo,
+ inferiorGridGateNodes.distinct,
+ ref,
+ )
+ )
+ )
+ .map {
+ case WrappedPowerMessage(
+ provideGridPowerMessage: ProvideGridPowerMessage
+ ) =>
+ (inferiorGridAgentRef, provideGridPowerMessage)
+ case WrappedPowerMessage(FailedPowerFlow) =>
+ (inferiorGridAgentRef, FailedPowerFlow)
+ }
}
.toVector
)
- .map(ReceivedGridPowerValues)
- .pipeTo(self)
- }
+ .map(res => ReceivedGridPowerValues(res))
+ pipeToSelf(future, ctx)
+ true
+ } else false
}
/** Triggers an execution of the pekko `ask` pattern for all slack voltages of
@@ -1189,39 +1336,50 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* @param askTimeout
* a timeout for the request
* @return
- * Some(Future[ReceivedSlackValues]) if this grids has connected superior
- * grids or None if this no superior grids
+ * true if this grids has connected superior grids or false if this no
+ * superior grids
*/
private def askSuperiorGridsForSlackVoltages(
currentSweepNo: Int,
- subGridGateToActorRef: Map[SubGridGate, ActorRef],
+ subGridGateToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]],
superiorGridGates: Vector[SubGridGate],
- askTimeout: Duration
- ): Option[Future[ReceivedSlackVoltageValues]] = {
+ askTimeout: Duration,
+ )(implicit
+ ctx: ActorContext[GridAgentMessage]
+ ): Boolean = {
implicit val timeout: PekkoTimeout = PekkoTimeout.create(askTimeout)
- log.debug(
+ implicit val ec: ExecutionContext = ctx.executionContext
+ implicit val scheduler: Scheduler = ctx.system.scheduler
+
+ ctx.log.debug(
s"asking superior grids for slack voltage values: {}",
- superiorGridGates
+ superiorGridGates,
)
- Option.when(superiorGridGates.nonEmpty) {
- Future
+ if (superiorGridGates.nonEmpty) {
+ val future = Future
.sequence(
superiorGridGates
.groupBy(subGridGateToActorRef(_))
.map { case (superiorGridAgent, gridGates) =>
- (superiorGridAgent ? RequestSlackVoltageMessage(
- currentSweepNo,
- gridGates.map(_.superiorNode.getUuid)
- )).map { case providedSlackValues: ProvideSlackVoltageMessage =>
- (superiorGridAgent, providedSlackValues)
- }
+ superiorGridAgent
+ .ask[GridAgentMessage](ref =>
+ RequestSlackVoltageMessage(
+ currentSweepNo,
+ gridGates.map(_.superiorNode.getUuid),
+ ref,
+ )
+ )
+ .map { case providedSlackValues: ProvideSlackVoltageMessage =>
+ (superiorGridAgent, providedSlackValues)
+ }
}
.toVector
)
- .map(ReceivedSlackVoltageValues)
- .pipeTo(self)
- }
+ .map(res => ReceivedSlackVoltageValues(res))
+ pipeToSelf(future, ctx)
+ true
+ } else false
}
/** Create an instance of
@@ -1236,21 +1394,42 @@ trait DBFSAlgorithm extends PowerFlowSupport with GridResultsSupport {
* @param currentTimestamp
* the current time stamp
*/
- def createAndSendPowerFlowResults(
+ private def createAndSendPowerFlowResults(
gridAgentBaseData: GridAgentBaseData,
- currentTimestamp: ZonedDateTime
+ currentTimestamp: ZonedDateTime,
+ )(implicit
+ log: Logger,
+ constantData: GridAgentConstantData,
): Unit = {
gridAgentBaseData.sweepValueStores.lastOption.foreach {
case (_, valueStore) =>
- notifyListener(
+ constantData.notifyListeners(
this.createResultModels(
gridAgentBaseData.gridEnv.gridModel,
- valueStore
+ valueStore,
)(
- currentTimestamp
+ currentTimestamp,
+ log,
)
)
}
}
+ /** This method uses [[ActorContext.pipeToSelf()]] to send a future message to
+ * itself. If the future is a [[Success]] the message is send, else a
+ * [[ReceivedFailure]] with the thrown error is send.
+ * @param future
+ * future message that should be send to the agent after it was processed
+ * @param ctx
+ * [[ActorContext]] of the receiving actor
+ */
+ private def pipeToSelf(
+ future: Future[GridAgentMessage],
+ ctx: ActorContext[GridAgentMessage],
+ ): Unit = {
+ ctx.pipeToSelf[GridAgentMessage](future) {
+ case Success(value) => value
+ case Failure(exception) => ReceivedFailure(exception)
+ }
+ }
}
diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgent.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgent.scala
index b8677f607a..8cd2a6b1f6 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgent.scala
@@ -6,172 +6,106 @@
package edu.ie3.simona.agent.grid
-import edu.ie3.simona.agent.grid.GridAgent.Create
+import edu.ie3.simona.actor.SimonaActorNaming
+import edu.ie3.simona.agent.EnvironmentRefs
import edu.ie3.simona.agent.grid.GridAgentData.{
GridAgentBaseData,
+ GridAgentConstantData,
GridAgentInitData,
- GridAgentUninitializedData
}
-import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
-import edu.ie3.simona.agent.state.GridAgentState.{Initializing, SimulateGrid}
-import edu.ie3.simona.agent.{EnvironmentRefs, SimonaAgent}
+import edu.ie3.simona.agent.grid.GridAgentMessage._
+import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage
import edu.ie3.simona.config.SimonaConfig
+import edu.ie3.simona.event.ResultEvent
import edu.ie3.simona.exceptions.agent.GridAgentInitializationException
import edu.ie3.simona.model.grid.GridModel
-import edu.ie3.simona.ontology.messages.PowerMessage.RequestGridPowerMessage
+import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
-import edu.ie3.simona.ontology.messages.{Activation, StopMessage}
-import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.TimeUtil
-import org.apache.pekko.actor.{ActorRef, Props, Stash}
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.scaladsl.{Behaviors, StashBuffer}
+import org.apache.pekko.actor.typed.{ActorRef, Behavior}
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.util.UUID
import scala.language.postfixOps
-object GridAgent {
- def props(
+object GridAgent extends DBFSAlgorithm {
+
+ def apply(
environmentRefs: EnvironmentRefs,
simonaConfig: SimonaConfig,
- listener: Iterable[ActorRef]
- ): Props =
- Props(
- new GridAgent(
+ listener: Iterable[ActorRef[ResultEvent]],
+ ): Behavior[GridAgentMessage] = Behaviors.withStash(100) { buffer =>
+ Behaviors.setup[GridAgentMessage] { context =>
+ context.messageAdapter(msg => WrappedPowerMessage(msg))
+ val activationAdapter: ActorRef[Activation] =
+ context.messageAdapter[Activation](msg => WrappedActivation(msg))
+
+ // val initialization
+ val resolution: Long = simonaConfig.simona.powerflow.resolution.get(
+ ChronoUnit.SECONDS
+ ) // this determines the agents regular time bin it wants to be triggered e.g one hour
+
+ val simStartTime: ZonedDateTime = TimeUtil.withDefaults
+ .toZonedDateTime(simonaConfig.simona.time.startDateTime)
+
+ val agentValues = GridAgentConstantData(
environmentRefs,
simonaConfig,
- listener
+ listener,
+ resolution,
+ simStartTime,
+ activationAdapter,
)
- )
-
- /** GridAgent initialization data can only be constructed once all GridAgent
- * actors are created. Thus, we need an extra initialization message.
- * @param gridAgentInitData
- * The initialization data
- */
- final case class Create(
- gridAgentInitData: GridAgentInitData,
- unlockKey: ScheduleKey
- )
-
- /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
- * execute a power flow calculation
- *
- * @param tick
- * current tick
- */
- final case class DoPowerFlowTrigger(tick: Long, currentSweepNo: Int)
-
- /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
- * activate the superior grid agent to check for deviation after two sweeps
- * and see if the power flow converges
- *
- * @param tick
- * current tick
- */
- final case class CheckPowerDifferencesTrigger(tick: Long)
-
- /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
- * trigger the [[edu.ie3.simona.agent.grid.GridAgent]] s to prepare
- * themselves for a new sweep
- *
- * @param tick
- * current tick
- */
- final case class PrepareNextSweepTrigger(tick: Long)
-
- /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
- * indicate that a result has been found and each
- * [[edu.ie3.simona.agent.grid.GridAgent]] should do it's cleanup work
- *
- * @param tick
- * current tick
- */
- final case class FinishGridSimulationTrigger(tick: Long)
-}
-
-class GridAgent(
- val environmentRefs: EnvironmentRefs,
- simonaConfig: SimonaConfig,
- val listener: Iterable[ActorRef]
-) extends SimonaAgent[GridAgentData]
- with DBFSAlgorithm
- with Stash {
-
- // val initialization
- protected val resolution: Long = simonaConfig.simona.powerflow.resolution.get(
- ChronoUnit.SECONDS
- ) // this determines the agents regular time bin it wants to be triggered e.g one hour
- protected val simStartTime: ZonedDateTime = TimeUtil.withDefaults
- .toZonedDateTime(simonaConfig.simona.time.startDateTime)
-
- private val gridAgentController =
- new GridAgentController(
- context,
- environmentRefs,
- simStartTime,
- TimeUtil.withDefaults
- .toZonedDateTime(simonaConfig.simona.time.endDateTime),
- simonaConfig.simona.runtime.participant,
- simonaConfig.simona.output.participant,
- resolution,
- listener,
- log
- )
-
- override def postStop(): Unit = {
- log.debug("{} shutdown", self)
+ uninitialized(agentValues, buffer, simonaConfig)
+ }
}
- override def preStart(): Unit = {
- log.debug("{} started!", self)
- }
-
- // general agent states
- // first fsm state of the agent
- startWith(Uninitialized, GridAgentUninitializedData)
-
- when(Uninitialized) {
- case Event(
- Create(gridAgentInitData, unlockKey),
- _
- ) =>
- environmentRefs.scheduler ! ScheduleActivation(
- self.toTyped,
- INIT_SIM_TICK,
- Some(unlockKey)
- )
-
- goto(Initializing) using gridAgentInitData
- }
+ private def uninitialized(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ simonaConfig: SimonaConfig,
+ ): Behavior[GridAgentMessage] =
+ Behaviors.receiveMessagePartial {
+ case CreateGridAgent(gridAgentInitData, unlockKey) =>
+ constantData.environmentRefs.scheduler ! ScheduleActivation(
+ constantData.activationAdapter,
+ INIT_SIM_TICK,
+ Some(unlockKey),
+ )
+ initializing(gridAgentInitData, simonaConfig)
+ }
- when(Initializing) {
- case Event(
- Activation(INIT_SIM_TICK),
- gridAgentInitData: GridAgentInitData
- ) =>
+ private def initializing(
+ gridAgentInitData: GridAgentInitData,
+ simonaConfig: SimonaConfig,
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = Behaviors.receivePartial {
+ case (ctx, WrappedActivation(Activation(INIT_SIM_TICK))) =>
// fail fast sanity checks
- failFast(gridAgentInitData)
+ failFast(gridAgentInitData, SimonaActorNaming.actorName(ctx.self))
- log.debug(
+ ctx.log.debug(
s"Inferior Subnets: {}; Inferior Subnet Nodes: {}",
gridAgentInitData.inferiorGridIds,
- gridAgentInitData.inferiorGridNodeUuids
+ gridAgentInitData.inferiorGridNodeUuids,
)
- log.debug(
+ ctx.log.debug(
s"Superior Subnets: {}; Superior Subnet Nodes: {}",
gridAgentInitData.superiorGridIds,
- gridAgentInitData.superiorGridNodeUuids
+ gridAgentInitData.superiorGridNodeUuids,
)
- log.debug("Received InitializeTrigger.")
+ ctx.log.debug("Received InitializeTrigger.")
// build the assets concurrently
val subGridContainer = gridAgentInitData.subGridContainer
@@ -179,21 +113,37 @@ class GridAgent(
val thermalGridsByBusId = gridAgentInitData.thermalIslandGrids.map {
thermalGrid => thermalGrid.bus().getUuid -> thermalGrid
}.toMap
- log.debug(s"Thermal island grids: ${thermalGridsByBusId.size}")
+ ctx.log.debug(s"Thermal island grids: ${thermalGridsByBusId.size}")
// get the [[GridModel]]
val gridModel = GridModel(
subGridContainer,
refSystem,
- TimeUtil.withDefaults
- .toZonedDateTime(simonaConfig.simona.time.startDateTime),
TimeUtil.withDefaults.toZonedDateTime(
- simonaConfig.simona.time.endDateTime
- )
+ constantData.simonaConfig.simona.time.startDateTime
+ ),
+ TimeUtil.withDefaults.toZonedDateTime(
+ constantData.simonaConfig.simona.time.endDateTime
+ ),
+ simonaConfig,
)
+ val gridAgentController =
+ new GridAgentController(
+ ctx,
+ constantData.environmentRefs,
+ constantData.simStartTime,
+ TimeUtil.withDefaults
+ .toZonedDateTime(constantData.simonaConfig.simona.time.endDateTime),
+ constantData.simonaConfig.simona.runtime.participant,
+ constantData.simonaConfig.simona.output.participant,
+ constantData.resolution,
+ constantData.listener,
+ ctx.log,
+ )
+
/* Reassure, that there are also calculation models for the given uuids */
- val nodeToAssetAgentsMap: Map[UUID, Set[ActorRef]] =
+ val nodeToAssetAgentsMap: Map[UUID, Set[ActorRef[ParticipantMessage]]] =
gridAgentController
.buildSystemParticipants(subGridContainer, thermalGridsByBusId)
.map { case (uuid: UUID, actorSet) =>
@@ -216,61 +166,60 @@ class GridAgent(
gridAgentInitData.superiorGridNodeUuids,
gridAgentInitData.inferiorGridGates,
PowerFlowParams(
- simonaConfig.simona.powerflow.maxSweepPowerDeviation,
- simonaConfig.simona.powerflow.newtonraphson.epsilon.toVector.sorted,
- simonaConfig.simona.powerflow.newtonraphson.iterations,
- simonaConfig.simona.powerflow.sweepTimeout,
- simonaConfig.simona.powerflow.stopOnFailure
+ constantData.simonaConfig.simona.powerflow.maxSweepPowerDeviation,
+ constantData.simonaConfig.simona.powerflow.newtonraphson.epsilon.toVector.sorted,
+ constantData.simonaConfig.simona.powerflow.newtonraphson.iterations,
+ constantData.simonaConfig.simona.powerflow.sweepTimeout,
+ constantData.simonaConfig.simona.powerflow.stopOnFailure,
),
- log,
- actorName
+ SimonaActorNaming.actorName(ctx.self),
)
- log.debug("Je suis initialized")
+ ctx.log.debug("Je suis initialized")
- environmentRefs.scheduler ! Completion(
- self.toTyped,
- Some(resolution)
+ constantData.environmentRefs.scheduler ! Completion(
+ constantData.activationAdapter,
+ Some(constantData.resolution),
)
- goto(Idle) using gridAgentBaseData
+ idle(gridAgentBaseData)
}
- when(Idle) {
-
- // needs to be set here to handle if the messages arrive too early
- // before a transition to GridAgentBehaviour took place
- case Event(RequestGridPowerMessage(_, _), _: GridAgentBaseData) =>
- stash()
- stay()
-
- case Event(
- Activation(tick),
- gridAgentBaseData: GridAgentBaseData
- ) =>
- unstashAll()
-
- environmentRefs.scheduler ! Completion(
- self.toTyped,
- Some(tick)
+ /** Method that defines the idle [[Behavior]] of the agent.
+ *
+ * @param gridAgentBaseData
+ * state data of the actor
+ * @param constantData
+ * immutable [[GridAgent]] values
+ * @param buffer
+ * for [[GridAgentMessage]]s
+ * @return
+ * a [[Behavior]]
+ */
+ private[grid] def idle(
+ gridAgentBaseData: GridAgentBaseData
+ )(implicit
+ constantData: GridAgentConstantData,
+ buffer: StashBuffer[GridAgentMessage],
+ ): Behavior[GridAgentMessage] = Behaviors.receivePartial {
+ case (_, msg: WrappedPowerMessage) =>
+ // needs to be set here to handle if the messages arrive too early
+ // before a transition to GridAgentBehaviour took place
+ buffer.stash(msg)
+ Behaviors.same
+
+ case (_, WrappedActivation(activation: Activation)) =>
+ constantData.environmentRefs.scheduler ! Completion(
+ constantData.activationAdapter,
+ Some(activation.tick),
)
-
- goto(SimulateGrid) using gridAgentBaseData
-
- case Event(StopMessage(_), data: GridAgentBaseData) =>
- // shutdown children
- data.gridEnv.nodeToAssetAgents.foreach { case (_, actors) =>
- actors.foreach(context.stop)
- }
-
- // we are done
- stop()
+ buffer.unstashAll(simulateGrid(gridAgentBaseData, activation.tick))
}
- // everything else
- whenUnhandled(myUnhandled())
-
- private def failFast(gridAgentInitData: GridAgentInitData): Unit = {
+ private def failFast(
+ gridAgentInitData: GridAgentInitData,
+ actorName: String,
+ ): Unit = {
if (
gridAgentInitData.superiorGridGates.isEmpty && gridAgentInitData.inferiorGridGates.isEmpty
)
diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala
index 9ec4aca397..a8904c9ef9 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentController.scala
@@ -6,17 +6,15 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.actor.{ActorContext, ActorRef}
-import org.apache.pekko.event.LoggingAdapter
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.models.input.container.{SubGridContainer, ThermalGrid}
import edu.ie3.datamodel.models.input.system._
import edu.ie3.simona.actor.SimonaActorNaming._
import edu.ie3.simona.agent.EnvironmentRefs
+import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.{
ActorEvMovementsService,
- ActorWeatherService
+ ActorWeatherService,
}
import edu.ie3.simona.agent.participant.evcs.EvcsAgent
import edu.ie3.simona.agent.participant.fixedfeedin.FixedFeedInAgent
@@ -27,12 +25,22 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.Participa
import edu.ie3.simona.agent.participant.wec.WecAgent
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.config.SimonaConfig._
+import edu.ie3.simona.event.ResultEvent
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.GridAgentInitializationException
import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleActivation
import edu.ie3.simona.util.ConfigUtil
import edu.ie3.simona.util.ConfigUtil._
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.ActorContext
+import org.apache.pekko.actor.typed.scaladsl.adapter.{
+ ClassicActorRefOps,
+ TypedActorContextOps,
+ TypedActorRefOps,
+}
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
+import org.slf4j.Logger
import java.time.ZonedDateTime
import java.util.UUID
@@ -62,20 +70,20 @@ import scala.jdk.CollectionConverters._
* @since 2019-07-18
*/
class GridAgentController(
- gridAgentContext: ActorContext,
+ gridAgentContext: ActorContext[_],
environmentRefs: EnvironmentRefs,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
participantsConfig: SimonaConfig.Simona.Runtime.Participant,
outputConfig: SimonaConfig.Simona.Output.Participant,
resolution: Long,
- listener: Iterable[ActorRef],
- log: LoggingAdapter
+ listener: Iterable[ActorRef[ResultEvent]],
+ log: Logger,
) extends LazyLogging {
def buildSystemParticipants(
subGridContainer: SubGridContainer,
- thermalIslandGridsByBusId: Map[UUID, ThermalGrid]
- ): Map[UUID, Set[ActorRef]] = {
+ thermalIslandGridsByBusId: Map[UUID, ThermalGrid],
+ ): Map[UUID, Set[ActorRef[ParticipantMessage]]] = {
val systemParticipants =
filterSysParts(subGridContainer, environmentRefs)
@@ -86,7 +94,7 @@ class GridAgentController(
outputConfig,
systemParticipants,
thermalIslandGridsByBusId,
- environmentRefs
+ environmentRefs,
)
}
@@ -105,7 +113,7 @@ class GridAgentController(
*/
private def filterSysParts(
subGridContainer: SubGridContainer,
- environmentRefs: EnvironmentRefs
+ environmentRefs: EnvironmentRefs,
) = {
val (notProcessedElements, availableSysParts) =
@@ -115,19 +123,19 @@ class GridAgentController(
.foldLeft((Set.empty[String], Vector.empty[SystemParticipantInput])) {
case (
(notProcessedElements, availableSystemParticipants),
- curSysPart
+ curSysPart,
) =>
curSysPart match {
case entity @ (_: BmInput | _: ChpInput | _: EvInput |
_: StorageInput) =>
(
notProcessedElements + entity.getClass.getSimpleName,
- availableSystemParticipants
+ availableSystemParticipants,
)
// only include evcs if ev data service is present
case evcsInput: EvcsInput
if environmentRefs.evDataService.isEmpty =>
- log.warning(
+ log.warn(
s"Evcs ${evcsInput.getId} has been removed because no ev movements service is present."
)
(notProcessedElements, availableSystemParticipants)
@@ -137,7 +145,7 @@ class GridAgentController(
}
if (notProcessedElements.nonEmpty)
- log.warning(
+ log.warn(
s"The following elements have been removed, " +
s"as the agents are not implemented yet: $notProcessedElements"
)
@@ -169,8 +177,8 @@ class GridAgentController(
outputConfig: SimonaConfig.Simona.Output.Participant,
participants: Vector[SystemParticipantInput],
thermalIslandGridsByBusId: Map[UUID, ThermalGrid],
- environmentRefs: EnvironmentRefs
- ): Map[UUID, Set[ActorRef]] = {
+ environmentRefs: EnvironmentRefs,
+ ): Map[UUID, Set[ActorRef[ParticipantMessage]]] = {
/* Prepare the config util for the participant models, which (possibly) utilizes as map to speed up the initialization
* phase */
val participantConfigUtil =
@@ -188,13 +196,13 @@ class GridAgentController(
outputConfigUtil,
participant,
thermalIslandGridsByBusId,
- environmentRefs
+ environmentRefs,
)
introduceAgentToEnvironment(actorRef)
// return uuid to actorRef
node.getUuid -> actorRef
})
- .toSet[(UUID, ActorRef)]
+ .toSet[(UUID, ActorRef[ParticipantMessage])]
.groupMap(entry => entry._1)(entry => entry._2)
}
@@ -204,8 +212,8 @@ class GridAgentController(
outputConfigUtil: OutputConfigUtil,
participantInputModel: SystemParticipantInput,
thermalIslandGridsByBusId: Map[UUID, ThermalGrid],
- environmentRefs: EnvironmentRefs
- ): ActorRef = participantInputModel match {
+ environmentRefs: EnvironmentRefs,
+ ): ActorRef[ParticipantMessage] = participantInputModel match {
case input: FixedFeedInInput =>
buildFixedFeedIn(
input,
@@ -217,7 +225,7 @@ class GridAgentController(
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfigUtil.getOrDefault(NotifierIdentifier.FixedFeedIn)
+ outputConfigUtil.getOrDefault(NotifierIdentifier.FixedFeedIn),
)
case input: LoadInput =>
buildLoad(
@@ -230,7 +238,7 @@ class GridAgentController(
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfigUtil.getOrDefault(NotifierIdentifier.Load)
+ outputConfigUtil.getOrDefault(NotifierIdentifier.Load),
)
case input: PvInput =>
buildPv(
@@ -244,7 +252,7 @@ class GridAgentController(
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfigUtil.getOrDefault(NotifierIdentifier.PvPlant)
+ outputConfigUtil.getOrDefault(NotifierIdentifier.PvPlant),
)
case input: WecInput =>
buildWec(
@@ -258,7 +266,7 @@ class GridAgentController(
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfigUtil.getOrDefault(NotifierIdentifier.Wec)
+ outputConfigUtil.getOrDefault(NotifierIdentifier.Wec),
)
case input: EvcsInput =>
buildEvcs(
@@ -276,7 +284,7 @@ class GridAgentController(
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfigUtil.getOrDefault(NotifierIdentifier.Evcs)
+ outputConfigUtil.getOrDefault(NotifierIdentifier.Evcs),
)
case hpInput: HpInput =>
thermalIslandGridsByBusId.get(hpInput.getThermalBus.getUuid) match {
@@ -288,7 +296,7 @@ class GridAgentController(
environmentRefs.primaryServiceProxy,
environmentRefs.weather,
requestVoltageDeviationThreshold,
- outputConfigUtil.getOrDefault(NotifierIdentifier.Hp)
+ outputConfigUtil.getOrDefault(NotifierIdentifier.Hp),
)
case None =>
throw new GridAgentInitializationException(
@@ -330,31 +338,33 @@ class GridAgentController(
private def buildFixedFeedIn(
fixedFeedInInput: FixedFeedInInput,
modelConfiguration: FixedFeedInRuntimeConfig,
- primaryServiceProxy: ActorRef,
+ primaryServiceProxy: ClassicRef,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ActorRef =
- gridAgentContext.simonaActorOf(
- FixedFeedInAgent.props(
- environmentRefs.scheduler,
- ParticipantInitializeStateData(
- fixedFeedInInput,
- modelConfiguration,
- primaryServiceProxy,
- None,
- simulationStartDate,
- simulationEndDate,
- resolution,
- requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig: NotifierConfig,
+ ): ActorRef[ParticipantMessage] =
+ gridAgentContext.toClassic
+ .simonaActorOf(
+ FixedFeedInAgent.props(
+ environmentRefs.scheduler.toClassic,
+ ParticipantInitializeStateData(
+ fixedFeedInInput,
+ modelConfiguration,
+ primaryServiceProxy,
+ Iterable.empty,
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ ),
+ listener.map(_.toClassic),
),
- listener
- ),
- fixedFeedInInput.getId
- )
+ fixedFeedInInput.getId,
+ )
+ .toTyped
/** Creates a load agent and determines the needed additional information for
* later initialization of the agent.
@@ -381,31 +391,33 @@ class GridAgentController(
private def buildLoad(
loadInput: LoadInput,
modelConfiguration: LoadRuntimeConfig,
- primaryServiceProxy: ActorRef,
+ primaryServiceProxy: ClassicRef,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ActorRef =
- gridAgentContext.simonaActorOf(
- LoadAgent.props(
- environmentRefs.scheduler,
- ParticipantInitializeStateData(
- loadInput,
- modelConfiguration,
- primaryServiceProxy,
- None,
- simulationStartDate,
- simulationEndDate,
- resolution,
- requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig: NotifierConfig,
+ ): ActorRef[ParticipantMessage] =
+ gridAgentContext.toClassic
+ .simonaActorOf(
+ LoadAgent.props(
+ environmentRefs.scheduler.toClassic,
+ ParticipantInitializeStateData(
+ loadInput,
+ modelConfiguration,
+ primaryServiceProxy,
+ Iterable.empty,
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ ),
+ listener.map(_.toClassic),
),
- listener
- ),
- loadInput.getId
- )
+ loadInput.getId,
+ )
+ .toTyped
/** Creates a pv agent and determines the needed additional information for
* later initialization of the agent.
@@ -434,32 +446,34 @@ class GridAgentController(
private def buildPv(
pvInput: PvInput,
modelConfiguration: PvRuntimeConfig,
- primaryServiceProxy: ActorRef,
- weatherService: ActorRef,
+ primaryServiceProxy: ClassicRef,
+ weatherService: ClassicRef,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ActorRef =
- gridAgentContext.simonaActorOf(
- PvAgent.props(
- environmentRefs.scheduler,
- ParticipantInitializeStateData(
- pvInput,
- modelConfiguration,
- primaryServiceProxy,
- Some(Vector(ActorWeatherService(weatherService))),
- simulationStartDate,
- simulationEndDate,
- resolution,
- requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig: NotifierConfig,
+ ): ActorRef[ParticipantMessage] =
+ gridAgentContext.toClassic
+ .simonaActorOf(
+ PvAgent.props(
+ environmentRefs.scheduler.toClassic,
+ ParticipantInitializeStateData(
+ pvInput,
+ modelConfiguration,
+ primaryServiceProxy,
+ Iterable(ActorWeatherService(weatherService)),
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ ),
+ listener.map(_.toClassic),
),
- listener
- ),
- pvInput.getId
- )
+ pvInput.getId,
+ )
+ .toTyped
/** Creates an Evcs agent and determines the needed additional information for
* later initialization of the agent.
@@ -488,39 +502,37 @@ class GridAgentController(
private def buildEvcs(
evcsInput: EvcsInput,
modelConfiguration: EvcsRuntimeConfig,
- primaryServiceProxy: ActorRef,
- evMovementsService: ActorRef,
+ primaryServiceProxy: ClassicRef,
+ evMovementsService: ClassicRef,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ActorRef = {
- val sources = Some(
- Vector(
- ActorEvMovementsService(
- evMovementsService
+ outputConfig: NotifierConfig,
+ ): ActorRef[ParticipantMessage] =
+ gridAgentContext.toClassic
+ .simonaActorOf(
+ EvcsAgent.props(
+ environmentRefs.scheduler.toClassic,
+ ParticipantInitializeStateData(
+ evcsInput,
+ modelConfiguration,
+ primaryServiceProxy,
+ Iterable(
+ ActorEvMovementsService(
+ evMovementsService
+ )
+ ),
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ ),
+ listener.map(_.toClassic),
)
)
- )
- gridAgentContext.simonaActorOf(
- EvcsAgent.props(
- environmentRefs.scheduler,
- ParticipantInitializeStateData(
- evcsInput,
- modelConfiguration,
- primaryServiceProxy,
- sources,
- simulationStartDate,
- simulationEndDate,
- resolution,
- requestVoltageDeviationThreshold,
- outputConfig
- ),
- listener
- )
- )
- }
+ .toTyped
/** Builds an [[HpAgent]] from given input
* @param hpInput
@@ -544,30 +556,32 @@ class GridAgentController(
hpInput: HpInput,
thermalGrid: ThermalGrid,
modelConfiguration: HpRuntimeConfig,
- primaryServiceProxy: ActorRef,
- weatherService: ActorRef,
+ primaryServiceProxy: ClassicRef,
+ weatherService: ClassicRef,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ActorRef =
- gridAgentContext.simonaActorOf(
- HpAgent.props(
- environmentRefs.scheduler,
- ParticipantInitializeStateData(
- hpInput,
- thermalGrid,
- modelConfiguration,
- primaryServiceProxy,
- Some(Vector(ActorWeatherService(weatherService))),
- simulationStartDate,
- simulationEndDate,
- resolution,
- requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig: NotifierConfig,
+ ): ActorRef[ParticipantMessage] =
+ gridAgentContext.toClassic
+ .simonaActorOf(
+ HpAgent.props(
+ environmentRefs.scheduler.toClassic,
+ ParticipantInitializeStateData(
+ hpInput,
+ thermalGrid,
+ modelConfiguration,
+ primaryServiceProxy,
+ Iterable(ActorWeatherService(weatherService)),
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ ),
+ listener.map(_.toClassic),
),
- listener
- ),
- hpInput.getId
- )
+ hpInput.getId,
+ )
+ .toTyped
/** Creates a pv agent and determines the needed additional information for
* later initialization of the agent.
@@ -596,32 +610,34 @@ class GridAgentController(
private def buildWec(
wecInput: WecInput,
modelConfiguration: WecRuntimeConfig,
- primaryServiceProxy: ActorRef,
- weatherService: ActorRef,
+ primaryServiceProxy: ClassicRef,
+ weatherService: ClassicRef,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ActorRef =
- gridAgentContext.simonaActorOf(
- WecAgent.props(
- environmentRefs.scheduler,
- ParticipantInitializeStateData(
- wecInput,
- modelConfiguration,
- primaryServiceProxy,
- Some(Vector(ActorWeatherService(weatherService))),
- simulationStartDate,
- simulationEndDate,
- resolution,
- requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig: NotifierConfig,
+ ): ActorRef[ParticipantMessage] =
+ gridAgentContext.toClassic
+ .simonaActorOf(
+ WecAgent.props(
+ environmentRefs.scheduler.toClassic,
+ ParticipantInitializeStateData(
+ wecInput,
+ modelConfiguration,
+ primaryServiceProxy,
+ Iterable(ActorWeatherService(weatherService)),
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ ),
+ listener.map(_.toClassic),
),
- listener
- ),
- wecInput.getId
- )
+ wecInput.getId,
+ )
+ .toTyped
/** Introduces the given agent to scheduler
*
@@ -629,12 +645,12 @@ class GridAgentController(
* Reference to the actor to add to the environment
*/
private def introduceAgentToEnvironment(
- actorRef: ActorRef
+ actorRef: ActorRef[ParticipantMessage]
): Unit = {
gridAgentContext.watch(actorRef)
environmentRefs.scheduler ! ScheduleActivation(
- actorRef.toTyped,
- INIT_SIM_TICK
+ actorRef.toClassic.toTyped,
+ INIT_SIM_TICK,
)
}
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 038d72cbc5..8f014f40e2 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentData.scala
@@ -6,25 +6,30 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorRef
-import org.apache.pekko.event.LoggingAdapter
import edu.ie3.datamodel.graph.SubGridGate
import edu.ie3.datamodel.models.input.container.{SubGridContainer, ThermalGrid}
import edu.ie3.powerflow.model.PowerFlowResult
import edu.ie3.powerflow.model.PowerFlowResult.SuccessFullPowerFlowResult.ValidNewtonRaphsonPFResult
+import edu.ie3.simona.agent.EnvironmentRefs
import edu.ie3.simona.agent.grid.ReceivedValues.{
ReceivedPowerValues,
- ReceivedSlackVoltageValues
+ ReceivedSlackVoltageValues,
}
import edu.ie3.simona.agent.grid.ReceivedValuesStore.NodeToReceivedPower
+import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage
+import edu.ie3.simona.config.SimonaConfig
+import edu.ie3.simona.event.ResultEvent
import edu.ie3.simona.model.grid.{GridModel, RefSystem}
+import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
FailedPowerFlow,
PowerResponseMessage,
ProvideGridPowerMessage,
- ProvidePowerMessage
+ ProvidePowerMessage,
}
+import org.apache.pekko.actor.typed.ActorRef
+import java.time.ZonedDateTime
import java.util.UUID
sealed trait GridAgentData
@@ -33,14 +38,37 @@ sealed trait GridAgentData
*/
object GridAgentData {
- /** Initial state data of the [[GridAgent]]
+ /** Class holding some [[GridAgent]] values that are immutable.
+ * @param environmentRefs
+ * environment actor refs
+ * @param simonaConfig
+ * config
+ * @param listener
+ * listeners
+ * @param resolution
+ * of the simulation
+ * @param simStartTime
+ * start time of the simulation
+ * @param activationAdapter
+ * adapter for [[Activation]]
*/
- final case object GridAgentUninitializedData extends GridAgentData
+ final case class GridAgentConstantData private (
+ environmentRefs: EnvironmentRefs,
+ simonaConfig: SimonaConfig,
+ listener: Iterable[ActorRef[ResultEvent]],
+ resolution: Long,
+ simStartTime: ZonedDateTime,
+ activationAdapter: ActorRef[Activation],
+ ) {
+ def notifyListeners(event: ResultEvent): Unit = {
+ listener.foreach(listener => listener ! event)
+ }
+ }
/** Data that is send to the [[GridAgent]] directly after startup. It contains
* the main information for initialization. This data should include all
* [[GridAgent]] individual data, for data that is the same for all
- * [[GridAgent]] s please use [[GridAgent.props()]]
+ * [[GridAgent]] s please use [[GridAgent.apply()]]
*
* @param subGridContainer
* raw grid information in the input data format
@@ -54,8 +82,8 @@ object GridAgentData {
final case class GridAgentInitData(
subGridContainer: SubGridContainer,
thermalIslandGrids: Seq[ThermalGrid],
- subGridGateToActorRef: Map[SubGridGate, ActorRef],
- refSystem: RefSystem
+ subGridGateToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]],
+ refSystem: RefSystem,
) extends GridAgentData
with GridAgentDataHelper {
override protected val subgridGates: Vector[SubGridGate] =
@@ -76,13 +104,13 @@ object GridAgentData {
final case class PowerFlowDoneData private (
gridAgentBaseData: GridAgentBaseData,
powerFlowResult: PowerFlowResult,
- pendingRequestAnswers: Set[Int]
+ pendingRequestAnswers: Set[Int],
) extends GridAgentData
object PowerFlowDoneData {
def apply(
gridAgentBaseData: GridAgentBaseData,
- powerFlowResult: PowerFlowResult
+ powerFlowResult: PowerFlowResult,
): PowerFlowDoneData = {
/* Determine the subgrid numbers of all superior grids */
val superiorSubGrids = gridAgentBaseData.gridEnv.subgridGateToActorRef
@@ -101,20 +129,19 @@ object GridAgentData {
def apply(
gridModel: GridModel,
- subgridGateToActorRef: Map[SubGridGate, ActorRef],
- nodeToAssetAgents: Map[UUID, Set[ActorRef]],
+ subgridGateToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]],
+ nodeToAssetAgents: Map[UUID, Set[ActorRef[ParticipantMessage]]],
superiorGridNodeUuids: Vector[UUID],
inferiorGridGates: Vector[SubGridGate],
powerFlowParams: PowerFlowParams,
- log: LoggingAdapter,
- actorName: String
+ actorName: String,
): GridAgentBaseData = {
val currentSweepNo = 0 // initialization is assumed to be always @ sweep 0
val sweepValueStores: Map[Int, SweepValueStore] = Map
.empty[
Int,
- SweepValueStore
+ SweepValueStore,
] // initialization is assumed to be always with no sweep data
val inferiorGridGateToActorRef = subgridGateToActorRef.filter {
case (gate, _) => inferiorGridGates.contains(gate)
@@ -126,11 +153,10 @@ object GridAgentData {
ReceivedValuesStore.empty(
nodeToAssetAgents,
inferiorGridGateToActorRef,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
),
sweepValueStores,
- log,
- actorName
+ actorName,
)
}
@@ -152,7 +178,7 @@ object GridAgentData {
def clean(
gridAgentBaseData: GridAgentBaseData,
superiorGridNodeUuids: Vector[UUID],
- inferiorGridGates: Vector[SubGridGate]
+ inferiorGridGates: Vector[SubGridGate],
): GridAgentBaseData = {
gridAgentBaseData.copy(
@@ -161,10 +187,10 @@ object GridAgentData {
gridAgentBaseData.gridEnv.subgridGateToActorRef.filter {
case (gate, _) => inferiorGridGates.contains(gate)
},
- superiorGridNodeUuids
+ superiorGridNodeUuids,
),
currentSweepNo = 0,
- sweepValueStores = Map.empty[Int, SweepValueStore]
+ sweepValueStores = Map.empty[Int, SweepValueStore],
)
}
@@ -194,8 +220,7 @@ object GridAgentData {
currentSweepNo: Int,
receivedValueStore: ReceivedValuesStore,
sweepValueStores: Map[Int, SweepValueStore],
- log: LoggingAdapter,
- actorName: String
+ actorName: String,
) extends GridAgentData
with GridAgentDataHelper {
@@ -215,14 +240,7 @@ object GridAgentData {
val slackVoltageValuesReady =
receivedValueStore.nodeToReceivedSlackVoltage.values
.forall(_.isDefined)
- log.debug(
- "slackMap: {}",
- receivedValueStore.nodeToReceivedSlackVoltage
- )
- log.debug(
- "powerMap: {}",
- receivedValueStore.nodeToReceivedPower
- )
+
assetAndGridPowerValuesReady & slackVoltageValuesReady
}
@@ -239,7 +257,7 @@ object GridAgentData {
*/
def updateWithReceivedPowerValues(
receivedPowerValues: ReceivedPowerValues,
- replace: Boolean = false
+ replace: Boolean = false,
): GridAgentBaseData = {
val updatedNodeToReceivedPowersMap = receivedPowerValues.values.foldLeft(
receivedValueStore.nodeToReceivedPower
@@ -248,8 +266,8 @@ object GridAgentData {
nodeToReceivedPowerValuesMapWithAddedPowerResponse,
(
senderRef,
- provideGridPowerMessage: ProvideGridPowerMessage
- )
+ provideGridPowerMessage: ProvideGridPowerMessage,
+ ),
) =>
/* Go over all includes messages and add them. */
provideGridPowerMessage.nodalResidualPower.foldLeft(
@@ -257,25 +275,25 @@ object GridAgentData {
) {
case (
nodeToReceivedPowerValuesMapWithAddedExchangedPower,
- exchangedPower
+ exchangedPower,
) =>
updateNodalReceivedPower(
exchangedPower,
nodeToReceivedPowerValuesMapWithAddedExchangedPower,
senderRef,
- replace
+ replace,
)
}
case (
nodeToReceivedPowerValuesMapWithAddedPowerResponse,
- (senderRef, powerResponseMessage)
+ (senderRef, powerResponseMessage),
) =>
// some other singular power response message
updateNodalReceivedPower(
powerResponseMessage,
nodeToReceivedPowerValuesMapWithAddedPowerResponse,
senderRef,
- replace
+ replace,
)
}
this.copy(
@@ -301,8 +319,8 @@ object GridAgentData {
private def updateNodalReceivedPower(
powerResponse: PowerResponseMessage,
nodeToReceived: NodeToReceivedPower,
- senderRef: ActorRef,
- replace: Boolean
+ senderRef: ActorRef[_],
+ replace: Boolean,
): 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 {
@@ -334,7 +352,7 @@ object GridAgentData {
nodeUuid,
throw new RuntimeException(
s"NodeId $nodeUuid is not part of nodeToReceivedPowerValuesMap!"
- )
+ ),
) +
// add or update entry in map of node entries
(senderRef -> Some(powerResponse))
@@ -359,8 +377,8 @@ object GridAgentData {
*/
private def getNodeUuidForSender(
nodeToReceivedPower: NodeToReceivedPower,
- senderRef: ActorRef,
- replace: Boolean
+ senderRef: ActorRef[_],
+ replace: Boolean,
): Option[UUID] =
nodeToReceivedPower
.find { case (_, receivedPowerMessages) =>
@@ -431,13 +449,13 @@ object GridAgentData {
def storeSweepDataAndClearReceiveMaps(
validPowerFlowResult: ValidNewtonRaphsonPFResult,
superiorGridNodeUuids: Vector[UUID],
- inferiorGridGates: Vector[SubGridGate]
+ inferiorGridGates: Vector[SubGridGate],
): GridAgentBaseData = {
val sweepValueStore =
SweepValueStore(
validPowerFlowResult,
gridEnv.gridModel.gridComponents.nodes,
- gridEnv.gridModel.nodeUuidToIndexMap
+ gridEnv.gridModel.nodeUuidToIndexMap,
)
val updatedSweepValueStore =
sweepValueStores + (currentSweepNo -> sweepValueStore)
@@ -449,8 +467,8 @@ object GridAgentData {
gridEnv.subgridGateToActorRef.filter { case (gate, _) =>
inferiorGridGates.contains(gate)
},
- superiorGridNodeUuids
- )
+ superiorGridNodeUuids,
+ ),
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridAgentMessage.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentMessage.scala
new file mode 100644
index 0000000000..a40d5bb9e8
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/agent/grid/GridAgentMessage.scala
@@ -0,0 +1,87 @@
+/*
+ * © 2024. 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
+
+import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
+import edu.ie3.simona.ontology.messages.{Activation, PowerMessage}
+import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
+
+/** Trait for [[GridAgent]] messages.
+ */
+sealed trait GridAgentMessage
+
+/** Defines all messages that can be received by a [[GridAgent]] without the
+ * need for an adapter.
+ */
+object GridAgentMessage {
+
+ /** Necessary because we want to extend [[GridAgentMessage]] in other classes,
+ * but we do want to keep [[GridAgentMessage]] sealed.
+ */
+ private[grid] trait InternalMessage extends GridAgentMessage
+
+ /** GridAgent initialization data can only be constructed once all GridAgent
+ * actors are created. Thus, we need an extra initialization message.
+ *
+ * @param gridAgentInitData
+ * The initialization data
+ */
+ final case class CreateGridAgent(
+ gridAgentInitData: GridAgentInitData,
+ unlockKey: ScheduleKey,
+ ) extends GridAgentMessage
+
+ /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
+ * execute a power flow calculation
+ *
+ * @param tick
+ * current tick
+ */
+ final case class DoPowerFlowTrigger(tick: Long, currentSweepNo: Int)
+ extends GridAgentMessage
+
+ /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
+ * activate the superior grid agent to check for deviation after two sweeps
+ * and see if the power flow converges
+ *
+ * @param tick
+ * current tick
+ */
+ final case class CheckPowerDifferencesTrigger(tick: Long)
+ extends GridAgentMessage
+
+ /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
+ * trigger the [[edu.ie3.simona.agent.grid.GridAgent]] s to prepare
+ * themselves for a new sweep
+ *
+ * @param tick
+ * current tick
+ */
+ final case class PrepareNextSweepTrigger(tick: Long) extends GridAgentMessage
+
+ /** Trigger used inside of [[edu.ie3.simona.agent.grid.DBFSAlgorithm]] to
+ * indicate that a result has been found and each
+ * [[edu.ie3.simona.agent.grid.GridAgent]] should do it's cleanup work
+ *
+ * @param tick
+ * current tick
+ */
+ final case class FinishGridSimulationTrigger(tick: Long)
+ extends GridAgentMessage
+
+ /** Wrapper for activation values
+ *
+ * @param activation
+ * the tick
+ */
+ final case class WrappedActivation(activation: Activation)
+ extends GridAgentMessage
+
+ final case class WrappedPowerMessage(msg: PowerMessage)
+ extends GridAgentMessage
+
+}
diff --git a/src/main/scala/edu/ie3/simona/agent/grid/GridEnvironment.scala b/src/main/scala/edu/ie3/simona/agent/grid/GridEnvironment.scala
index 39dadc489f..76f5ff1ed8 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/GridEnvironment.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/GridEnvironment.scala
@@ -6,11 +6,12 @@
package edu.ie3.simona.agent.grid
-import java.util.UUID
-
-import org.apache.pekko.actor.ActorRef
import edu.ie3.datamodel.graph.SubGridGate
+import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage
import edu.ie3.simona.model.grid.GridModel
+import org.apache.pekko.actor.typed.ActorRef
+
+import java.util.UUID
/** Wrapper class containing all information on the grid environment a
* [[GridAgent]] has access to
@@ -25,6 +26,6 @@ import edu.ie3.simona.model.grid.GridModel
*/
final case class GridEnvironment(
gridModel: GridModel,
- subgridGateToActorRef: Map[SubGridGate, ActorRef],
- nodeToAssetAgents: Map[UUID, Set[ActorRef]]
+ subgridGateToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]],
+ nodeToAssetAgents: Map[UUID, Set[ActorRef[ParticipantMessage]]],
)
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 ecd073b1cf..c872cc8e54 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/GridResultsSupport.scala
@@ -6,7 +6,6 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.event.LoggingAdapter
import breeze.math.Complex
import edu.ie3.datamodel.models.input.connector.ConnectorPort
import edu.ie3.datamodel.models.result.NodeResult
@@ -14,7 +13,7 @@ import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
Transformer2WResult,
- Transformer3WResult
+ Transformer3WResult,
}
import edu.ie3.powerflow.model.NodeData.StateData
import edu.ie3.simona.agent.grid.GridResultsSupport.PartialTransformer3wResult
@@ -24,11 +23,12 @@ import edu.ie3.simona.model.grid.Transformer3wModel.yij
import edu.ie3.simona.model.grid.Transformer3wPowerFlowCase.{
PowerFlowCaseA,
PowerFlowCaseB,
- PowerFlowCaseC
+ PowerFlowCaseC,
}
import edu.ie3.simona.model.grid._
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.quantities.QuantityUtil
+import org.slf4j.Logger
import squants.space.Degrees
import squants.{Amperes, Angle, ElectricCurrent}
import tech.units.indriya.quantity.Quantities
@@ -43,8 +43,6 @@ import scala.math._
*/
private[grid] trait GridResultsSupport {
- protected val log: LoggingAdapter
-
/** Creates a tuple as [[PowerFlowResultEvent]] s entities based on the
* provided grid data
*
@@ -60,8 +58,8 @@ private[grid] trait GridResultsSupport {
*/
def createResultModels(
grid: GridModel,
- sweepValueStore: SweepValueStore
- )(implicit timestamp: ZonedDateTime): PowerFlowResultEvent = {
+ sweepValueStore: SweepValueStore,
+ )(implicit timestamp: ZonedDateTime, log: Logger): PowerFlowResultEvent = {
// no sanity check for duplicated uuid result data as we expect valid data at this point
implicit val sweepValueStoreData: Map[UUID, SweepValueStoreData] =
sweepValueStore.sweepData
@@ -90,7 +88,7 @@ private[grid] trait GridResultsSupport {
grid.gridComponents.switches.map(calcSwitchResult(_, timestamp)),
buildLineResults(grid.gridComponents.lines),
buildTransformer2wResults(grid.gridComponents.transformers),
- buildTransformer3wResults(grid.gridComponents.transformers3w)
+ buildTransformer3wResults(grid.gridComponents.transformers3w),
)
}
@@ -111,7 +109,8 @@ private[grid] trait GridResultsSupport {
private def buildLineResults(lines: Set[LineModel])(implicit
sweepValueStoreData: Map[UUID, SweepValueStoreData],
iNominal: squants.ElectricCurrent,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
+ log: Logger,
): Set[LineResult] = {
lines.flatMap(lineModel => {
sweepValueStoreData
@@ -124,15 +123,15 @@ private[grid] trait GridResultsSupport {
nodeAStateData.stateData,
nodeBStateData.stateData,
iNominal,
- timestamp
+ timestamp,
)
)
case None =>
- log.warning(
+ log.warn(
"Cannot find power flow result data for line {} with nodeA {} and nodeB {}",
lineModel.uuid,
lineModel.nodeAUuid,
- lineModel.nodeBUuid
+ lineModel.nodeBUuid,
)
None
}
@@ -158,7 +157,8 @@ private[grid] trait GridResultsSupport {
implicit
sweepValueStoreData: Map[UUID, SweepValueStoreData],
iNominal: ElectricCurrent,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
+ log: Logger,
): Set[Transformer2WResult] = {
transformers.flatMap(trafo2w => {
sweepValueStoreData
@@ -171,15 +171,15 @@ private[grid] trait GridResultsSupport {
hvNodeStateData.stateData,
lvNodeStateData.stateData,
iNominal,
- timestamp
+ timestamp,
)
)
case None =>
- log.warning(
+ log.warn(
"Cannot find power flow result data for transformer2w {} with hvNode {} and lvNode {}",
trafo2w.uuid,
trafo2w.hvNodeUuid,
- trafo2w.lvNodeUuid
+ trafo2w.lvNodeUuid,
)
None
}
@@ -205,7 +205,8 @@ private[grid] trait GridResultsSupport {
implicit
sweepValueStoreData: Map[UUID, SweepValueStoreData],
iNominal: ElectricCurrent,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
+ log: Logger,
): Set[PartialTransformer3wResult] = transformers3w.flatMap { trafo3w =>
{
(trafo3w.powerFlowCase match {
@@ -229,16 +230,16 @@ private[grid] trait GridResultsSupport {
upperNodeStateData.stateData,
internalNodeStateData.stateData,
iNominal,
- timestamp
+ timestamp,
)
)
case None =>
- log.warning(
+ log.warn(
s"Cannot find power flow result data for transformer3w {} with nodeHv {}, nodeMv {}, nodeLv {} and internalNode ${trafo3w.nodeInternalUuid}",
trafo3w.uuid,
trafo3w.hvNodeUuid,
trafo3w.mvNodeUuid,
- trafo3w.lvNodeUuid
+ trafo3w.lvNodeUuid,
)
None
}
@@ -257,7 +258,7 @@ private[grid] trait GridResultsSupport {
*/
protected def calcNodeResult(
sweepValueStoreData: SweepValueStoreData,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
): NodeResult = {
val nodeStateData = sweepValueStoreData.stateData
@@ -268,7 +269,7 @@ private[grid] trait GridResultsSupport {
timestamp,
sweepValueStoreData.nodeUuid,
Quantities.getQuantity(vMag, PowerSystemUnits.PU),
- Quantities.getQuantity(vAng, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(vAng, PowerSystemUnits.DEGREE_GEOM),
)
}
@@ -284,13 +285,13 @@ private[grid] trait GridResultsSupport {
*/
protected def calcSwitchResult(
switchModel: SwitchModel,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
): SwitchResult = {
/* can be adapted when https://github.com/ie3-institute/PowerSystemDataModel/issues/151 has been resolved */
new SwitchResult(
timestamp,
switchModel.uuid,
- switchModel.isClosed
+ switchModel.isClosed,
)
}
@@ -315,17 +316,17 @@ private[grid] trait GridResultsSupport {
nodeAStateData: StateData,
nodeBStateData: StateData,
iNominal: ElectricCurrent,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
): LineResult = {
if (line.isInOperation) {
val yij = new Complex(
line.gij().value.doubleValue,
- line.bij().value.doubleValue
+ line.bij().value.doubleValue,
)
val y0 = new Complex(
line.g0().value.doubleValue,
- line.b0().value.doubleValue
+ line.b0().value.doubleValue,
)
val (iAComplexPu, iBComplexPu) =
@@ -340,7 +341,7 @@ private[grid] trait GridResultsSupport {
Quantities.getQuantity(iAMag.toAmperes, Units.AMPERE),
Quantities.getQuantity(iAAng.toDegrees, PowerSystemUnits.DEGREE_GEOM),
Quantities.getQuantity(iBMag.toAmperes, Units.AMPERE),
- Quantities.getQuantity(iBAng.toDegrees, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(iBAng.toDegrees, PowerSystemUnits.DEGREE_GEOM),
)
} else {
new LineResult(
@@ -349,7 +350,7 @@ private[grid] trait GridResultsSupport {
QuantityUtil.zeroCompQuantity(Units.AMPERE),
QuantityUtil.zeroCompQuantity(PowerSystemUnits.DEGREE_GEOM),
QuantityUtil.zeroCompQuantity(Units.AMPERE),
- QuantityUtil.zeroCompQuantity(PowerSystemUnits.DEGREE_GEOM)
+ QuantityUtil.zeroCompQuantity(PowerSystemUnits.DEGREE_GEOM),
)
}
}
@@ -377,13 +378,13 @@ private[grid] trait GridResultsSupport {
hvNodeStateData: StateData,
lvNodeStateData: StateData,
iNominal: ElectricCurrent,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
): Transformer2WResult = {
if (trafo2w.isInOperation) {
val (yab, yaa, ybb) = (
TransformerModel.yij(trafo2w),
TransformerModel.y0(trafo2w, ConnectorPort.A),
- TransformerModel.y0(trafo2w, ConnectorPort.B)
+ TransformerModel.y0(trafo2w, ConnectorPort.B),
)
val voltRatioNominal = trafo2w.voltRatioNominal
@@ -393,7 +394,7 @@ private[grid] trait GridResultsSupport {
lvNodeStateData.voltage,
yab,
yaa,
- Some(ybb)
+ Some(ybb),
)
/* Transfer port current A to high voltage level */
@@ -408,7 +409,7 @@ private[grid] trait GridResultsSupport {
Quantities.getQuantity(iAAng.toDegrees, PowerSystemUnits.DEGREE_GEOM),
Quantities.getQuantity(iBMag.toAmperes, Units.AMPERE),
Quantities.getQuantity(iBAng.toDegrees, PowerSystemUnits.DEGREE_GEOM),
- trafo2w.currentTapPos
+ trafo2w.currentTapPos,
)
} else {
@@ -419,7 +420,7 @@ private[grid] trait GridResultsSupport {
QuantityUtil.zeroCompQuantity(PowerSystemUnits.DEGREE_GEOM),
QuantityUtil.zeroCompQuantity(Units.AMPERE),
QuantityUtil.zeroCompQuantity(PowerSystemUnits.DEGREE_GEOM),
- trafo2w.currentTapPos
+ trafo2w.currentTapPos,
)
}
}
@@ -446,7 +447,7 @@ private[grid] trait GridResultsSupport {
nodeStateData: StateData,
internalNodeStateData: StateData,
iNominal: ElectricCurrent,
- timestamp: ZonedDateTime
+ timestamp: ZonedDateTime,
): PartialTransformer3wResult = {
val (_, iComplexPu) = iIJComplexPu(
internalNodeStateData.voltage,
@@ -458,9 +459,9 @@ private[grid] trait GridResultsSupport {
case PowerFlowCaseA => Transformer3wModel.Transformer3wPort.A
case PowerFlowCaseB => Transformer3wModel.Transformer3wPort.B
case PowerFlowCaseC => Transformer3wModel.Transformer3wPort.C
- }
+ },
),
- None
+ None,
)
val (iMag, iAng) = iMagAndAngle(iComplexPu, iNominal)
@@ -472,21 +473,21 @@ private[grid] trait GridResultsSupport {
trafo3w.uuid,
iMag,
iAng,
- trafo3w.currentTapPos
+ trafo3w.currentTapPos,
)
case Transformer3wPowerFlowCase.PowerFlowCaseB =>
PartialTransformer3wResult.PortB(
timestamp,
trafo3w.uuid,
iMag,
- iAng
+ iAng,
)
case Transformer3wPowerFlowCase.PowerFlowCaseC =>
PartialTransformer3wResult.PortC(
timestamp,
trafo3w.uuid,
iMag,
- iAng
+ iAng,
)
}
}
@@ -510,11 +511,11 @@ private[grid] trait GridResultsSupport {
*/
private def iMagAndAngle(
iPu: Complex,
- iNominal: ElectricCurrent
+ iNominal: ElectricCurrent,
): (ElectricCurrent, Angle) =
(
Amperes(iNominal.toAmperes * iPu.abs),
- complexToAngle(iPu)
+ complexToAngle(iPu),
)
/** Calculate the angle of the complex value given. The angle has the proper
@@ -535,7 +536,7 @@ private[grid] trait GridResultsSupport {
Angle can be 90 or 270 degrees, depending on sign of the imaginary part */
angleOffsetCorrection(
Degrees(90d),
- imag
+ imag,
)
case Complex(real, imag) =>
/* Both real and imaginary parts are != 0. This means that the angle
@@ -547,7 +548,7 @@ private[grid] trait GridResultsSupport {
val baseAngle = atan(imag / real).toDegrees
angleOffsetCorrection(
Degrees(baseAngle),
- real
+ real,
)
}
@@ -556,7 +557,7 @@ private[grid] trait GridResultsSupport {
*/
private def angleOffsetCorrection(
angle: Angle,
- dir: Double
+ dir: Double,
): Angle =
if (dir < 0)
angle + Degrees(180d)
@@ -588,11 +589,11 @@ private[grid] trait GridResultsSupport {
ujPu: Complex,
yij: Complex,
y0i: Complex,
- y0j: Option[Complex] = None
+ y0j: Option[Complex] = None,
): (Complex, Complex) = {
(
(uiPu - ujPu) * yij + (uiPu * y0i),
- (ujPu - uiPu) * yij + (ujPu * y0j.getOrElse(y0i))
+ (ujPu - uiPu) * yij + (ujPu * y0j.getOrElse(y0i)),
)
}
@@ -630,7 +631,7 @@ object GridResultsSupport {
override val input: UUID,
override val currentMagnitude: ElectricCurrent,
override val currentAngle: Angle,
- tapPos: Int
+ tapPos: Int,
) extends PartialTransformer3wResult
/** Partial result for the port at the medium voltage side
@@ -648,7 +649,7 @@ object GridResultsSupport {
override val time: ZonedDateTime,
override val input: UUID,
override val currentMagnitude: ElectricCurrent,
- override val currentAngle: Angle
+ override val currentAngle: Angle,
) extends PartialTransformer3wResult
/** Partial result for the port at the low voltage side
@@ -666,7 +667,7 @@ object GridResultsSupport {
override val time: ZonedDateTime,
override val input: UUID,
override val currentMagnitude: ElectricCurrent,
- override val currentAngle: Angle
+ override val currentAngle: Angle,
) extends PartialTransformer3wResult
}
}
diff --git a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowParams.scala b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowParams.scala
index 8e5284cb15..0235cb56de 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowParams.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowParams.scala
@@ -30,5 +30,5 @@ final case class PowerFlowParams(
epsilon: Vector[Double],
maxIterations: Int,
sweepTimeout: Duration,
- stopOnFailure: Boolean
+ stopOnFailure: Boolean,
)
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 afc26f47c1..5d34d06bd4 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/PowerFlowSupport.scala
@@ -6,7 +6,6 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.event.LoggingAdapter
import breeze.math.Complex
import edu.ie3.powerflow.NewtonRaphsonPF
import edu.ie3.powerflow.model.NodeData.{PresetData, StateData}
@@ -18,8 +17,9 @@ import edu.ie3.simona.agent.grid.ReceivedValues.ReceivedSlackVoltageValues
import edu.ie3.simona.exceptions.agent.DBFSAlgorithmException
import edu.ie3.simona.model.grid._
import edu.ie3.simona.ontology.messages.PowerMessage.ProvidePowerMessage
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
import edu.ie3.util.scala.quantities.Kilovars
+import org.slf4j.Logger
import squants.electro.ElectricPotential
import squants.energy.Kilowatts
@@ -32,8 +32,6 @@ import scala.util.{Failure, Success, Try}
*/
trait PowerFlowSupport {
- protected val log: LoggingAdapter
-
/** Composes the current operation point needed by
* [[edu.ie3.powerflow.NewtonRaphsonPF.calculate()]]
*
@@ -71,7 +69,7 @@ trait PowerFlowSupport {
receivedValuesStore: ReceivedValuesStore,
gridMainRefSystem: RefSystem,
targetVoltageFromReceivedData: Boolean = true,
- ignoreTargetVoltage: Boolean = false
+ ignoreTargetVoltage: Boolean = false,
): (Array[PresetData], WithForcedStartVoltages) = {
val (operatingPoints, stateData) = nodes.map { nodeModel =>
// note: currently we only support pq nodes as we not distinguish between pq/pv nodes -
@@ -83,7 +81,7 @@ trait PowerFlowSupport {
nodeModel.uuid,
throw new RuntimeException(
s"Received data for node ${nodeModel.id} [${nodeModel.uuid}] which is not in my nodeUuidToIndexMap!"
- )
+ ),
)
val apparentPower: Complex =
@@ -107,18 +105,18 @@ trait PowerFlowSupport {
.foldLeft(
(
Kilowatts(0d),
- Kilovars(0d)
+ Kilovars(0d),
)
) { case ((pSum, qSum), powerMessage) =>
(
pSum + powerMessage.p,
- qSum + powerMessage.q
+ qSum + powerMessage.q,
)
}
new Complex(
gridMainRefSystem.pInPu(p).value.doubleValue,
- gridMainRefSystem.qInPu(q).value.doubleValue
+ gridMainRefSystem.qInPu(q).value.doubleValue,
)
case None => new Complex(0, 0)
}
@@ -142,7 +140,7 @@ trait PowerFlowSupport {
nodeModel.uuid,
transformers2w,
transformers3w,
- gridMainRefSystem
+ gridMainRefSystem,
)
} else {
// Either the received data shall not be considered or the node is not a slack node
@@ -157,7 +155,7 @@ trait PowerFlowSupport {
nodeIdx,
NodeType.SL,
targetVoltage,
- apparentPower
+ apparentPower,
)
)
@@ -166,9 +164,9 @@ trait PowerFlowSupport {
nodeIdx,
nodeType,
apparentPower,
- targetVoltage.abs
+ targetVoltage.abs,
),
- optStateData
+ optStateData,
)
}.unzip
@@ -195,7 +193,7 @@ trait PowerFlowSupport {
(
combineOperatingPoint(adaptedOperatingPoint.toArray),
- WithForcedStartVoltages(Array(slackNodeData))
+ WithForcedStartVoltages(Array(slackNodeData)),
)
}
@@ -226,7 +224,7 @@ trait PowerFlowSupport {
sweepDataValues: Vector[SweepValueStore.SweepValueStoreData],
transformers2w: Set[TransformerModel],
transformers3w: Set[Transformer3wModel],
- gridMainRefSystem: RefSystem
+ gridMainRefSystem: RefSystem,
): (Array[PresetData], WithForcedStartVoltages) =
sweepDataValues.map { sweepValueStoreData =>
val nodeStateData = sweepValueStoreData.stateData
@@ -247,7 +245,7 @@ trait PowerFlowSupport {
sweepValueStoreData.nodeUuid,
transformers2w,
transformers3w,
- gridMainRefSystem
+ gridMainRefSystem,
)
} else
Complex.one
@@ -258,22 +256,22 @@ trait PowerFlowSupport {
nodeStateData.index,
nodeStateData.nodeType,
nodeStateData.power,
- targetVoltage.abs
+ targetVoltage.abs,
),
Option.when(nodeStateData.nodeType == NodeType.SL)(
StateData(
nodeStateData.index,
nodeStateData.nodeType,
targetVoltage,
- nodeStateData.power
+ nodeStateData.power,
)
- )
+ ),
)
}.unzip match {
case (operatingPoint, stateData) =>
(
combineOperatingPoint(operatingPoint.toArray),
- WithForcedStartVoltages(stateData.flatten.toArray)
+ WithForcedStartVoltages(stateData.flatten.toArray),
)
}
@@ -318,15 +316,15 @@ trait PowerFlowSupport {
private def combinePresetData(a: PresetData, b: PresetData): PresetData = {
require(
a.index == b.index,
- "Preset Data should only be combined when they map to the same index."
+ "Preset Data should only be combined when they map to the same index.",
)
require(
a.nodeType == b.nodeType,
- "Preset Data combination is only supported for the same node types for now."
+ "Preset Data combination is only supported for the same node types for now.",
)
require(
math.abs(a.targetVoltage - b.targetVoltage) < 1e-6,
- "Nodes to be combined have to be located in the same voltage level."
+ "Nodes to be combined have to be located in the same voltage level.",
)
val index = a.index
@@ -337,7 +335,7 @@ trait PowerFlowSupport {
def combineOptionals(
a: Option[Double],
b: Option[Double],
- f: (Double, Double) => Double
+ f: (Double, Double) => Double,
): Option[Double] = (a, b) match {
case (Some(a), Some(b)) => Some(f(a, b))
case (Some(a), None) => Some(a)
@@ -362,7 +360,7 @@ trait PowerFlowSupport {
activePowerMin,
activePowerMax,
reactivePowerMin,
- reactivePowerMax
+ reactivePowerMax,
)
}
@@ -378,7 +376,7 @@ trait PowerFlowSupport {
*/
protected def composeValidNewtonRaphsonPFResultVoltagesDebugString(
validResult: ValidNewtonRaphsonPFResult,
- gridModel: GridModel
+ gridModel: GridModel,
): String = {
val debugString = new mutable.StringBuilder("Power flow result: ")
validResult.nodeData.foreach(nodeStateData => {
@@ -431,23 +429,23 @@ trait PowerFlowSupport {
nodeUuid: UUID,
transformers2w: Set[TransformerModel],
transformers3w: Set[Transformer3wModel],
- gridRefSystem: RefSystem
+ gridRefSystem: RefSystem,
): Complex = {
((
transformers2w.find(_.hvNodeUuid == nodeUuid),
- transformers3w.find(_.nodeInternalUuid == nodeUuid)
+ transformers3w.find(_.nodeInternalUuid == nodeUuid),
) match {
case (Some(transformer2w), None) =>
transferToVoltageLevel(
receivedSlackVoltage.e,
receivedSlackVoltage.f,
- transformer2w
+ transformer2w,
)
case (None, Some(transformer3w)) =>
transferToVoltageLevel(
receivedSlackVoltage.e,
receivedSlackVoltage.f,
- transformer3w
+ transformer3w,
)
case (Some(transformer2w), Some(transformer3w)) =>
throw new RuntimeException(
@@ -480,7 +478,7 @@ trait PowerFlowSupport {
private def transferToVoltageLevel(
e: ElectricPotential,
f: ElectricPotential,
- transformerModel: TransformerModel
+ transformerModel: TransformerModel,
): (ElectricPotential, ElectricPotential) = {
val voltRatio = transformerModel.voltRatioNominal
(e.divide(voltRatio.toDouble), f.divide(voltRatio.toDouble))
@@ -501,7 +499,7 @@ trait PowerFlowSupport {
private def transferToVoltageLevel(
e: ElectricPotential,
f: ElectricPotential,
- transformerModel: Transformer3wModel
+ transformerModel: Transformer3wModel,
): (ElectricPotential, ElectricPotential) = {
val voltRatio = Transformer3wModel.voltRatio(transformerModel)
(e.divide(voltRatio.toDouble), f.divide(voltRatio.toDouble))
@@ -521,10 +519,10 @@ trait PowerFlowSupport {
private def toPu(
e: ElectricPotential,
f: ElectricPotential,
- gridRefSystem: RefSystem
+ gridRefSystem: RefSystem,
): (squants.Dimensionless, squants.Dimensionless) = (
gridRefSystem.vInPu(e),
- gridRefSystem.vInPu(f)
+ gridRefSystem.vInPu(f),
)
/** Build a [[Complex]] from both parts of the voltage
@@ -560,14 +558,14 @@ trait PowerFlowSupport {
gridModel: GridModel,
maxIterations: Int,
operatingPoint: Array[PresetData],
- slackVoltages: WithForcedStartVoltages
- )(epsilons: Vector[Double]): PowerFlowResult = {
+ slackVoltages: WithForcedStartVoltages,
+ )(epsilons: Vector[Double])(implicit log: Logger): PowerFlowResult = {
epsilons.headOption match {
case Some(epsilon) =>
val admittanceMatrix =
GridModel.composeAdmittanceMatrix(
gridModel.nodeUuidToIndexMap,
- gridModel.gridComponents
+ gridModel.gridComponents,
)
// / execute
@@ -577,7 +575,7 @@ trait PowerFlowSupport {
Try {
powerFlow.calculate(
operatingPoint,
- Some(slackVoltages)
+ Some(slackVoltages),
)
}.map {
case _: PowerFlowResult.FailedPowerFlowResult if epsilons.size > 1 =>
@@ -586,13 +584,13 @@ trait PowerFlowSupport {
log.debug(
"NR power flow with ɛ = {} failed. Relaxing to {}.",
epsilon,
- epsilonsLeft.headOption.getOrElse("")
+ epsilonsLeft.headOption.getOrElse(""),
)
newtonRaphsonPF(
gridModel,
maxIterations,
operatingPoint,
- slackVoltages
+ slackVoltages,
)(
epsilonsLeft
)
@@ -603,7 +601,7 @@ trait PowerFlowSupport {
case Failure(exception) =>
throw new DBFSAlgorithmException(
s"Power flow calculation in subgrid ${gridModel.subnetNo} failed.",
- exception
+ exception,
)
}
case None =>
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 53f04d5af8..c1accbb471 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValues.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValues.scala
@@ -6,22 +6,30 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorRef
import edu.ie3.simona.ontology.messages.PowerMessage.PowerResponseMessage
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage
+import VoltageMessage.ProvideSlackVoltageMessage
+import edu.ie3.simona.agent.grid.GridAgentMessage.InternalMessage
+import org.apache.pekko.actor.typed.ActorRef
/** Serves as a wrapper class that allows for matches against received values in
* [[DBFSAlgorithm]]
*/
-sealed trait ReceivedValues
+sealed trait ReceivedValues extends InternalMessage
object ReceivedValues {
- type ActorPowerRequestResponse = (ActorRef, PowerResponseMessage)
- type ActorSlackVoltageRequestResponse = (ActorRef, ProvideSlackVoltageMessage)
+ private type ParticipantPowerRequestResponse =
+ (
+ ActorRef[_],
+ PowerResponseMessage,
+ ) // necessary, because participants are still classic actors
+ private type GridPowerRequestResponse =
+ (ActorRef[GridAgentMessage], PowerResponseMessage)
+ private type ActorSlackVoltageRequestResponse =
+ (ActorRef[GridAgentMessage], ProvideSlackVoltageMessage)
sealed trait ReceivedPowerValues extends ReceivedValues {
- def values: Vector[ActorPowerRequestResponse]
+ def values: Vector[(ActorRef[_], PowerResponseMessage)]
}
/** Wrapper for received asset power values (p, q)
@@ -30,7 +38,7 @@ object ReceivedValues {
* the asset power values and their senders
*/
final case class ReceivedAssetPowerValues(
- values: Vector[ActorPowerRequestResponse]
+ values: Vector[ParticipantPowerRequestResponse]
) extends ReceivedPowerValues
/** Wrapper for received grid power values (p, q)
@@ -39,7 +47,7 @@ object ReceivedValues {
* the grid power values and their senders
*/
final case class ReceivedGridPowerValues(
- values: Vector[ActorPowerRequestResponse]
+ values: Vector[GridPowerRequestResponse]
) extends ReceivedPowerValues
/** Wrapper for received slack voltage values (v)
@@ -51,4 +59,11 @@ object ReceivedValues {
values: Vector[ActorSlackVoltageRequestResponse]
) extends ReceivedValues
+ /** Wrapper for received exception.
+ * @param exception
+ * that was received
+ */
+ final case class ReceivedFailure(
+ exception: Throwable
+ ) extends ReceivedValues
}
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 4c083629f3..a576045703 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/ReceivedValuesStore.scala
@@ -6,17 +6,20 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorRef
import edu.ie3.datamodel.graph.SubGridGate
import edu.ie3.simona.agent.grid.ReceivedValuesStore.{
NodeToReceivedPower,
- NodeToReceivedSlackVoltage
+ NodeToReceivedSlackVoltage,
}
+import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage
import edu.ie3.simona.ontology.messages.PowerMessage.{
PowerResponseMessage,
- ProvidePowerMessage
+ ProvidePowerMessage,
}
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.{ActorRef => classicRef}
import java.util.UUID
@@ -41,13 +44,13 @@ import java.util.UUID
*/
final case class ReceivedValuesStore private (
nodeToReceivedPower: NodeToReceivedPower,
- nodeToReceivedSlackVoltage: NodeToReceivedSlackVoltage
+ nodeToReceivedSlackVoltage: NodeToReceivedSlackVoltage,
)
object ReceivedValuesStore {
type NodeToReceivedPower =
- Map[UUID, Map[ActorRef, Option[PowerResponseMessage]]]
+ Map[UUID, Map[ActorRef[_], Option[PowerResponseMessage]]]
type NodeToReceivedSlackVoltage =
Map[UUID, Option[ExchangeVoltage]]
@@ -68,15 +71,17 @@ object ReceivedValuesStore {
* `empty` [[ReceivedValuesStore]] with pre-initialized options as `None`
*/
def empty(
- nodeToAssetAgents: Map[UUID, Set[ActorRef]],
- inferiorSubGridGateToActorRef: Map[SubGridGate, ActorRef],
- superiorGridNodeUuids: Vector[UUID]
+ nodeToAssetAgents: Map[UUID, Set[ActorRef[ParticipantMessage]]],
+ inferiorSubGridGateToActorRef: Map[SubGridGate, ActorRef[
+ GridAgentMessage
+ ]],
+ superiorGridNodeUuids: Vector[UUID],
): ReceivedValuesStore = {
val (nodeToReceivedPower, nodeToReceivedSlackVoltage) =
buildEmptyReceiveMaps(
nodeToAssetAgents,
inferiorSubGridGateToActorRef,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
ReceivedValuesStore(nodeToReceivedPower, nodeToReceivedSlackVoltage)
}
@@ -94,12 +99,14 @@ object ReceivedValuesStore {
* `empty` [[NodeToReceivedPower]] with pre-initialized options as `None`
*/
private def buildEmptyNodeToReceivedPowerMap(
- nodeToAssetAgents: Map[UUID, Set[ActorRef]],
- inferiorSubGridGateToActorRef: Map[SubGridGate, ActorRef]
+ nodeToAssetAgents: Map[UUID, Set[ActorRef[ParticipantMessage]]],
+ inferiorSubGridGateToActorRef: Map[SubGridGate, ActorRef[
+ GridAgentMessage
+ ]],
): NodeToReceivedPower = {
/* Collect everything, that I expect from my asset agents */
val assetsToReceivedPower: NodeToReceivedPower = nodeToAssetAgents.collect {
- case (uuid: UUID, actorRefs: Set[ActorRef]) =>
+ case (uuid: UUID, actorRefs: Set[ActorRef[ParticipantMessage]]) =>
(uuid, actorRefs.map(actorRef => actorRef -> None).toMap)
}
@@ -111,14 +118,14 @@ object ReceivedValuesStore {
.foldLeft(assetsToReceivedPower) {
case (
subordinateToReceivedPower,
- couplingNodeUuid -> inferiorSubGridRef
+ couplingNodeUuid -> inferiorSubGridRef,
) =>
/* 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,
- Map.empty[ActorRef, Option[ProvidePowerMessage]]
+ Map.empty[ActorRef[_], Option[ProvidePowerMessage]],
) + (inferiorSubGridRef -> None)
/* Update the existing map */
@@ -156,16 +163,18 @@ object ReceivedValuesStore {
* `empty` [[NodeToReceivedSlackVoltage]] and [[NodeToReceivedPower]]
*/
private def buildEmptyReceiveMaps(
- nodeToAssetAgents: Map[UUID, Set[ActorRef]],
- inferiorSubGridGateToActorRef: Map[SubGridGate, ActorRef],
- superiorGridNodeUuids: Vector[UUID]
+ nodeToAssetAgents: Map[UUID, Set[ActorRef[ParticipantMessage]]],
+ inferiorSubGridGateToActorRef: Map[SubGridGate, ActorRef[
+ GridAgentMessage
+ ]],
+ superiorGridNodeUuids: Vector[UUID],
): (NodeToReceivedPower, NodeToReceivedSlackVoltage) = {
(
buildEmptyNodeToReceivedPowerMap(
nodeToAssetAgents,
- inferiorSubGridGateToActorRef
+ inferiorSubGridGateToActorRef,
),
- buildEmptyNodeToReceivedSlackVoltageValuesMap(superiorGridNodeUuids)
+ buildEmptyNodeToReceivedSlackVoltageValuesMap(superiorGridNodeUuids),
)
}
diff --git a/src/main/scala/edu/ie3/simona/agent/grid/SweepValueStore.scala b/src/main/scala/edu/ie3/simona/agent/grid/SweepValueStore.scala
index dfe459fc72..ddc46a1ba3 100644
--- a/src/main/scala/edu/ie3/simona/agent/grid/SweepValueStore.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/SweepValueStore.scala
@@ -36,7 +36,7 @@ case object SweepValueStore {
*/
final case class SweepValueStoreData private (
nodeUuid: UUID,
- stateData: StateData
+ stateData: StateData,
)
/** Creates an empty [[SweepValueStore]] from on a valid power flow result
@@ -55,7 +55,7 @@ case object SweepValueStore {
def apply(
validResult: ValidNewtonRaphsonPFResult,
nodes: Seq[NodeModel],
- nodeUuidToIndexMap: Map[UUID, Int]
+ nodeUuidToIndexMap: Map[UUID, Int],
): SweepValueStore = {
val sweepDataValues = nodes.foldLeft(Vector.empty[SweepValueStoreData])(
(valueStoreDataElements, node) => {
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/VoltageMessage.scala b/src/main/scala/edu/ie3/simona/agent/grid/VoltageMessage.scala
similarity index 78%
rename from src/main/scala/edu/ie3/simona/ontology/messages/VoltageMessage.scala
rename to src/main/scala/edu/ie3/simona/agent/grid/VoltageMessage.scala
index 3c836b599d..41c311f61d 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/VoltageMessage.scala
+++ b/src/main/scala/edu/ie3/simona/agent/grid/VoltageMessage.scala
@@ -4,14 +4,16 @@
* Research group Distribution grid planning and operation
*/
-package edu.ie3.simona.ontology.messages
+package edu.ie3.simona.agent.grid
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.agent.grid.GridAgentMessage.InternalMessage
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import org.apache.pekko.actor.typed.ActorRef
+import squants.electro.ElectricPotential
import java.util.UUID
-import squants.electro.ElectricPotential
-sealed trait VoltageMessage
+sealed trait VoltageMessage extends InternalMessage
/** Message that is send between [[edu.ie3.simona.agent.grid.GridAgent]] s to
* provide voltage information for nodes
@@ -27,7 +29,8 @@ object VoltageMessage {
*/
final case class RequestSlackVoltageMessage(
currentSweepNo: Int,
- nodeUuids: Seq[UUID]
+ nodeUuids: Seq[UUID],
+ sender: ActorRef[GridAgentMessage],
) extends VoltageMessage
/** Provide complex voltage at the nodes that the sender's sub grid shares
@@ -38,7 +41,7 @@ object VoltageMessage {
*/
final case class ProvideSlackVoltageMessage(
currentSweepNo: Int,
- nodalSlackVoltages: Seq[ExchangeVoltage]
+ nodalSlackVoltages: Seq[ExchangeVoltage],
) extends VoltageMessage
object ProvideSlackVoltageMessage {
@@ -55,7 +58,7 @@ object VoltageMessage {
final case class ExchangeVoltage(
nodeUuid: UUID,
e: ElectricPotential,
- f: ElectricPotential
+ f: ElectricPotential,
)
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala
index 063ff508f5..a01c5677a1 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgent.scala
@@ -6,14 +6,11 @@
package edu.ie3.simona.agent.participant
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.actor.{ActorRef, FSM}
import edu.ie3.datamodel.models.input.system.SystemParticipantInput
-import edu.ie3.simona.agent.SimonaAgent
-import edu.ie3.simona.agent.grid.GridAgent.FinishGridSimulationTrigger
import edu.ie3.simona.agent.participant.ParticipantAgent.{
+ FinishParticipantSimulation,
StartCalculationTrigger,
- getAndCheckNodalVoltage
+ getAndCheckNodalVoltage,
}
import edu.ie3.simona.agent.participant.data.Data
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.PrimaryDataWithApparentPower
@@ -21,38 +18,54 @@ import edu.ie3.simona.agent.participant.data.Data.{PrimaryData, SecondaryData}
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
FromOutsideBaseStateData,
- ParticipantModelBaseStateData
+ ParticipantModelBaseStateData,
}
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData._
import edu.ie3.simona.agent.participant.statedata.{
BaseStateData,
DataCollectionStateData,
- ParticipantStateData
+ ParticipantStateData,
}
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.{
Calculate,
- HandleInformation
+ HandleInformation,
}
+import edu.ie3.simona.agent.{SimonaAgent, ValueStore}
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.InconsistentStateException
-import edu.ie3.simona.model.participant.{CalcRelevantData, SystemParticipant}
+import edu.ie3.simona.model.participant.ModelState.ConstantState
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ FlexChangeIndicator,
+ ModelState,
+ SystemParticipant,
+}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.RequestAssetPowerMessage
import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleActivation
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexResponse,
+ IssueFlexControl,
+ RequestFlexOptions,
+}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.{
PrimaryServiceRegistrationMessage,
ProvisionMessage,
- RegistrationResponseMessage
+ RegistrationResponseMessage,
}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
import squants.{Dimensionless, Power}
import java.time.ZonedDateTime
+import scala.reflect.ClassTag
/** Common properties to participant agents
*
@@ -60,6 +73,8 @@ import java.time.ZonedDateTime
* Type of primary data, the calculation model will produce
* @tparam CD
* Type of compilation of [[SecondaryData]], that are needed for calculation
+ * @tparam MS
+ * Type of model state data
* @tparam D
* Type of participant state data to use
* @tparam I
@@ -77,15 +92,30 @@ import java.time.ZonedDateTime
abstract class ParticipantAgent[
PD <: PrimaryDataWithApparentPower[PD],
CD <: CalcRelevantData,
+ MS <: ModelState,
D <: ParticipantStateData[PD],
I <: SystemParticipantInput,
MC <: SimonaConfig.BaseRuntimeConfig,
- M <: SystemParticipant[CD, PD]
-](scheduler: ActorRef, initStateData: ParticipantInitializeStateData[I, MC, PD])
+ M <: SystemParticipant[CD, PD, MS],
+](
+ scheduler: ActorRef,
+ initStateData: ParticipantInitializeStateData[I, MC, PD],
+)(implicit val tag: ClassTag[MS])
extends SimonaAgent[ParticipantStateData[PD]] {
val alternativeResult: PD
+ /** Partial function, that is able to transfer
+ * [[ParticipantModelBaseStateData]] (holding the actual calculation model)
+ * into a pair of active and reactive power
+ */
+ protected val calculateModelPowerFunc: (
+ Long,
+ ParticipantModelBaseStateData[PD, CD, MS, M],
+ MS,
+ squants.Dimensionless,
+ ) => PD
+
// general agent states
// first fsm state of the agent
startWith(Uninitialized, ParticipantUninitializedStateData[PD]())
@@ -94,7 +124,7 @@ abstract class ParticipantAgent[
/* Initialize the agent */
case Event(
Activation(INIT_SIM_TICK),
- _: ParticipantUninitializedStateData[PD]
+ _: ParticipantUninitializedStateData[PD],
) =>
/* Ask the primary service proxy for data. If some is available, it will delegate the request to a worker and
* that will confirm, otherwise, a failed registration is announced. */
@@ -110,61 +140,62 @@ abstract class ParticipantAgent[
initStateData.simulationEndDate,
initStateData.resolution,
initStateData.requestVoltageDeviationThreshold,
- initStateData.outputConfig
+ initStateData.outputConfig,
+ initStateData.maybeEmAgent,
)
}
when(Idle) {
case Event(
- Activation(currentTick),
- modelBaseStateData: ParticipantModelBaseStateData[PD, CD, M]
+ Activation(tick),
+ modelBaseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
) if modelBaseStateData.services.isEmpty =>
/* An activity start trigger is sent and no data is awaited (neither secondary nor primary). Therefore go straight
* ahead to calculations */
/* Hold tick, as state transition is needed */
- holdTick(currentTick)
+ holdTick(tick)
- self ! StartCalculationTrigger(currentTick)
+ self ! StartCalculationTrigger(tick)
/* Remove this tick from the array of foreseen activation ticks */
val additionalActivationTicks =
- modelBaseStateData.additionalActivationTicks.rangeFrom(currentTick + 1)
+ modelBaseStateData.additionalActivationTicks.rangeFrom(tick + 1)
goto(Calculate) using BaseStateData.updateBaseStateData(
modelBaseStateData,
modelBaseStateData.resultValueStore,
modelBaseStateData.requestValueStore,
modelBaseStateData.voltageValueStore,
additionalActivationTicks,
- modelBaseStateData.foreseenDataTicks
+ modelBaseStateData.foreseenDataTicks,
)
case Event(
Activation(currentTick),
- modelBaseStateData: ParticipantModelBaseStateData[PD, CD, M]
+ modelBaseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
) =>
/* An activation is sent, but I'm not sure yet, if secondary data will arrive. Figure out, if someone
* is about to deliver new data and either go to HandleInformation, check and possibly wait for data provision
* messages or directly go to Calculate and utilize what is already there */
handleActivationAndGoToHandleInformation(
currentTick,
- modelBaseStateData
+ modelBaseStateData,
)
case Event(
Activation(currentTick),
- fromOutsideBaseStateData: FromOutsideBaseStateData[M, PD]
+ fromOutsideBaseStateData: FromOutsideBaseStateData[M, PD],
) =>
/* An activation is sent, but I'm still expecting primary data. Go to HandleInformation and wait for
* a data provision message */
handleActivationAndGoToHandleInformation(
currentTick,
- fromOutsideBaseStateData
+ fromOutsideBaseStateData,
)
case Event(
msg: ProvisionMessage[Data],
- baseStateData: BaseStateData[PD]
+ baseStateData: BaseStateData[PD],
) =>
/* Somebody has sent new primary or secondary data. Collect, what is expected for this tick. Go over to data
* handling */
@@ -172,7 +203,7 @@ abstract class ParticipantAgent[
case Event(
RequestAssetPowerMessage(requestTick, eInPu, fInPu),
- baseStateData: BaseStateData[PD]
+ baseStateData: BaseStateData[PD],
) =>
/* Determine the reply and stay in this state (or stash the message if the request cannot yet be answered) */
answerPowerRequestAndStayWithUpdatedStateData(
@@ -180,49 +211,84 @@ abstract class ParticipantAgent[
requestTick,
eInPu,
fInPu,
- alternativeResult
+ alternativeResult,
)
case Event(
- FinishGridSimulationTrigger(tick),
- baseStateData: BaseStateData[PD]
+ FinishParticipantSimulation(tick),
+ baseStateData: BaseStateData[PD],
) =>
// clean up agent result value store
finalizeTickAfterPF(baseStateData, tick)
+
+ case Event(
+ RequestFlexOptions(tick),
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ ) =>
+ val expectedSenders = baseStateData.foreseenDataTicks
+ .collect {
+ case (actorRef, Some(expectedTick)) if expectedTick == tick =>
+ actorRef -> None
+ }
+
+ // Unexpected data provisions (e.g. by ExtEvDataService) are not possible when EM-managed.
+ // These data have to be always provided _before_ flex request is received here.
+
+ if (expectedSenders.nonEmpty) {
+ val nextStateData = DataCollectionStateData(
+ baseStateData,
+ expectedSenders,
+ yetTriggered = true,
+ )
+
+ /* Do await provision messages in HandleInformation */
+ goto(HandleInformation) using nextStateData
+ } else {
+ /* I don't expect any data. Calculate right away */
+ val updatedStateData = handleFlexRequest(baseStateData, tick)
+
+ stay() using updatedStateData
+ }
+
+ case Event(
+ flexCtrl: IssueFlexControl,
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ ) =>
+ handleFlexCtrl(baseStateData, flexCtrl, scheduler)
}
when(HandleInformation) {
/* Receive registration confirm from primary data service -> Set up actor for replay of data */
case Event(
- RegistrationSuccessfulMessage(maybeNextDataTick),
+ RegistrationSuccessfulMessage(serviceRef, maybeNextDataTick),
ParticipantInitializingStateData(
inputModel: InputModelContainer[I],
modelConfig: MC,
- secondaryDataServices,
+ _,
simulationStartDate,
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
- )
+ outputConfig,
+ _,
+ ),
) =>
log.debug("Will replay primary data")
initializeParticipantForPrimaryDataReplay(
inputModel,
modelConfig,
- secondaryDataServices,
simulationStartDate,
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
outputConfig,
- sender() -> maybeNextDataTick,
- scheduler
+ serviceRef -> maybeNextDataTick,
+ scheduler,
)
/* Receive registration refuse from primary data service -> Set up actor for model calculation */
case Event(
- RegistrationResponseMessage.RegistrationFailedMessage,
+ RegistrationResponseMessage.RegistrationFailedMessage(_),
ParticipantInitializingStateData(
inputModel: InputModelContainer[I],
modelConfig: MC,
@@ -231,8 +297,9 @@ abstract class ParticipantAgent[
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
- )
+ outputConfig,
+ maybeEmAgent,
+ ),
) =>
log.debug("Will perform model calculations")
initializeParticipantForModelCalculation(
@@ -244,19 +311,20 @@ abstract class ParticipantAgent[
resolution,
requestVoltageDeviationThreshold,
outputConfig,
- scheduler
+ scheduler,
+ maybeEmAgent,
)
/* Receiving the registration replies from services and collect their next data ticks */
case Event(
msg: RegistrationResponseMessage,
- stateData: CollectRegistrationConfirmMessages[PD]
+ stateData: CollectRegistrationConfirmMessages[PD],
) =>
handleRegistrationResponse(scheduler, msg, stateData)
case Event(
Activation(currentTick),
- stateData: DataCollectionStateData[PD]
+ stateData: DataCollectionStateData[PD],
) =>
/* The actor received an activation. Check, if there is everything at its place. If so, change state
* accordingly, otherwise stay here and wait for the messages */
@@ -265,7 +333,18 @@ abstract class ParticipantAgent[
stateData,
isYetTriggered = true,
currentTick,
- scheduler
+ scheduler,
+ )(stateData.baseStateData.outputConfig)
+
+ case Event(
+ RequestFlexOptions(tick),
+ stateData: DataCollectionStateData[PD],
+ ) =>
+ checkForExpectedDataAndChangeState(
+ stateData,
+ isYetTriggered = true,
+ tick,
+ scheduler,
)(stateData.baseStateData.outputConfig)
case Event(
@@ -273,59 +352,59 @@ abstract class ParticipantAgent[
stateData @ DataCollectionStateData(
baseStateData: BaseStateData[PD],
data,
- isYetTriggered
- )
+ isYetTriggered,
+ ),
) =>
/* We yet have received at least one data provision message. Handle all messages, that follow up for this tick, by
* adding the received data to the collection state data and checking, if everything is at its place */
val unexpectedSender = baseStateData.foreseenDataTicks.exists {
- case (ref, None) => sender() == ref
+ case (ref, None) => msg.serviceRef == ref
case _ => false
}
- if (data.contains(sender()) || unexpectedSender) {
+ if (data.contains(msg.serviceRef) || unexpectedSender) {
/* Update the yet received information */
- val updatedData = data + (sender() -> Some(msg.data))
+ val updatedData = data + (msg.serviceRef -> Some(msg.data))
/* If we have received unexpected data, we also have not been scheduled before */
if (unexpectedSender)
scheduler ! ScheduleActivation(
self.toTyped,
msg.tick,
- msg.unlockKey
+ msg.unlockKey,
)
/* Depending on if a next data tick can be foreseen, either update the entry in the base state data or remove
* it */
val foreSeenDataTicks =
- baseStateData.foreseenDataTicks + (sender() -> msg.nextDataTick)
+ baseStateData.foreseenDataTicks + (msg.serviceRef -> msg.nextDataTick)
val updatedBaseStateData = BaseStateData.updateBaseStateData(
baseStateData,
baseStateData.resultValueStore,
baseStateData.requestValueStore,
baseStateData.voltageValueStore,
baseStateData.additionalActivationTicks,
- foreSeenDataTicks
+ foreSeenDataTicks,
)
val updatedStateData: DataCollectionStateData[PD] = stateData
.copy(
baseStateData = updatedBaseStateData,
- data = updatedData
+ data = updatedData,
)
checkForExpectedDataAndChangeState(
updatedStateData,
isYetTriggered,
- currentTick,
- scheduler
+ msg.tick,
+ scheduler,
)(updatedBaseStateData.outputConfig)
} else
throw new IllegalStateException(
- s"Did not expect message from ${sender()} at tick $currentTick"
+ s"Did not expect message from ${msg.serviceRef} at tick ${msg.tick}"
)
case Event(
RequestAssetPowerMessage(currentTick, _, _),
- DataCollectionStateData(_, data, yetTriggered)
+ DataCollectionStateData(_, data, yetTriggered),
) =>
if (log.isDebugEnabled) {
val awaitedSenders = data.filter(_._2.isEmpty).keys
@@ -339,7 +418,7 @@ abstract class ParticipantAgent[
s"$currentTick from ${sender()}",
awaitedSenders,
yetReceivedSenders,
- if (yetTriggered) "" else "NOT"
+ if (yetTriggered) "" else "NOT",
)
}
stash()
@@ -349,36 +428,56 @@ abstract class ParticipantAgent[
when(Calculate) {
case Event(
StartCalculationTrigger(currentTick),
- modelBaseStateData: ParticipantModelBaseStateData[PD, CD, M]
+ modelBaseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
) =>
/* Model calculation without any secondary data needed */
val voltage = getAndCheckNodalVoltage(modelBaseStateData, currentTick)
+ val lastModelState =
+ getLastOrInitialStateData(modelBaseStateData, currentTick)
+
calculatePowerWithoutSecondaryDataAndGoToIdle(
modelBaseStateData,
+ lastModelState,
currentTick,
scheduler,
voltage,
- calculateModelPowerFunc
)
case Event(
StartCalculationTrigger(currentTick),
- serviceCollectionStateData: DataCollectionStateData[PD]
+ DataCollectionStateData(
+ participantStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ data,
+ _,
+ ),
) =>
+ val updatedReceivedSecondaryData = ValueStore.updateValueStore(
+ participantStateData.receivedSecondaryDataStore,
+ currentTick,
+ data.map { case (actorRef, Some(data: SecondaryData)) =>
+ actorRef -> data
+ },
+ )
+
/* At least parts of the needed data has been received or it is an additional activation, that has been triggered.
* Anyways, the calculation routine has also to take care of filling up missing data. */
+ val lastModelState =
+ getLastOrInitialStateData(participantStateData, currentTick)
calculatePowerWithSecondaryDataAndGoToIdle(
- serviceCollectionStateData,
+ participantStateData.copy(
+ receivedSecondaryDataStore = updatedReceivedSecondaryData
+ ),
+ lastModelState,
currentTick,
- scheduler
+ scheduler,
)
case Event(RequestAssetPowerMessage(currentTick, _, _), _) =>
log.debug(
s"Got asset power request for tick {} from '{}'. Will answer it later.",
currentTick,
- sender()
+ sender(),
)
stash()
stay()
@@ -401,8 +500,6 @@ abstract class ParticipantAgent[
* Input model
* @param modelConfig
* Configuration for the model
- * @param services
- * Optional list of services, that are needed
* @param simulationStartDate
* Real world time date time, when the simulation starts
* @param simulationEndDate
@@ -425,14 +522,13 @@ abstract class ParticipantAgent[
def initializeParticipantForPrimaryDataReplay(
inputModel: InputModelContainer[I],
modelConfig: MC,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
outputConfig: NotifierConfig,
senderToMaybeTick: (ActorRef, Option[Long]),
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[PD]]
/** Abstract definition of initialization method, implementation in
@@ -463,13 +559,14 @@ abstract class ParticipantAgent[
def initializeParticipantForModelCalculation(
inputModel: InputModelContainer[I],
modelConfig: MC,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
outputConfig: NotifierConfig,
- scheduler: ActorRef
+ scheduler: ActorRef,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
): FSM.State[AgentState, ParticipantStateData[PD]]
/** Handles the responses from service providers, this actor has registered
@@ -486,7 +583,7 @@ abstract class ParticipantAgent[
def handleRegistrationResponse(
scheduler: ActorRef,
registrationResponse: RegistrationResponseMessage,
- stateData: CollectRegistrationConfirmMessages[PD]
+ stateData: CollectRegistrationConfirmMessages[PD],
): FSM.State[AgentState, ParticipantStateData[PD]]
/** Handle an [[Activation]] received in [[Idle]]. Prepare the foreseen
@@ -502,7 +599,7 @@ abstract class ParticipantAgent[
*/
private def handleActivationAndGoToHandleInformation(
tick: Long,
- baseStateData: BaseStateData[PD]
+ baseStateData: BaseStateData[PD],
): FSM.State[AgentState, ParticipantStateData[PD]] = {
/* Hold tick, as we are about to changes states for a while */
holdTick(tick)
@@ -520,7 +617,7 @@ abstract class ParticipantAgent[
val nextStateData = DataCollectionStateData(
baseStateData,
expectedSenders,
- yetTriggered = true
+ yetTriggered = true,
)
if (expectedSenders.nonEmpty || unforeseenPossible) {
@@ -533,6 +630,22 @@ abstract class ParticipantAgent[
}
}
+ protected def getLastOrInitialStateData(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ tick: Long,
+ ): MS =
+ ConstantState match {
+ case constantState: MS =>
+ constantState
+ case _ =>
+ baseStateData.stateDataStore
+ .last(tick)
+ .map { case (_, lastState) =>
+ lastState
+ }
+ .getOrElse(createInitialState(baseStateData))
+ }
+
/** Handle an incoming data provision message in Idle, try to figure out who's
* about to send information in this tick as well. Map together all senders
* with their yet apparent information.
@@ -549,7 +662,7 @@ abstract class ParticipantAgent[
def handleDataProvisionAndGoToHandleInformation(
msg: ProvisionMessage[Data],
baseStateData: BaseStateData[PD],
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[PD]]
/** Checks, if all data is available and change state accordingly. Three cases
@@ -579,21 +692,11 @@ abstract class ParticipantAgent[
stateData: DataCollectionStateData[PD],
isYetTriggered: Boolean,
tick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
)(implicit
outputConfig: NotifierConfig
): FSM.State[AgentState, ParticipantStateData[PD]]
- /** Partial function, that is able to transfer
- * [[ParticipantModelBaseStateData]] (holding the actual calculation model)
- * into a pair of active and reactive power
- */
- val calculateModelPowerFunc: (
- Long,
- ParticipantModelBaseStateData[PD, CD, M],
- Dimensionless
- ) => PD
-
/** Abstractly calculate the power output of the participant without needing
* any secondary data. The next state is [[Idle]], sending a
* [[edu.ie3.simona.ontology.messages.SchedulerMessage.Completion]] to
@@ -602,28 +705,23 @@ abstract class ParticipantAgent[
*
* @param baseStateData
* Base state data to update
+ * @param lastModelState
+ * The current model state, before updating it
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
* [[ActorRef]] to the scheduler in the simulation
* @param nodalVoltage
* Current nodal voltage
- * @param calculateModelPowerFunc
- * Function, that transfer the current tick, the state data and the nodal
- * voltage to participants power exchange with the grid
* @return
* [[Idle]] with updated result values
*/
def calculatePowerWithoutSecondaryDataAndGoToIdle(
- baseStateData: ParticipantModelBaseStateData[PD, CD, M],
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ lastModelState: MS,
currentTick: Long,
scheduler: ActorRef,
nodalVoltage: Dimensionless,
- calculateModelPowerFunc: (
- Long,
- ParticipantModelBaseStateData[PD, CD, M],
- Dimensionless
- ) => PD
): FSM.State[AgentState, ParticipantStateData[PD]]
/** Abstractly calculate the power output of the participant utilising
@@ -637,8 +735,11 @@ abstract class ParticipantAgent[
* scheduler and using update result values.
Actual implementation
* can be found in each participant's fundamentals.
*
- * @param collectionStateData
- * State data with collected secondary data.
+ * @param baseStateData
+ * The base state data with collected secondary data
+ * @param lastModelState
+ * The current model state, before applying changes by externally received
+ * data
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -647,11 +748,59 @@ abstract class ParticipantAgent[
* [[Idle]] with updated result values
*/
def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[PD],
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ lastModelState: MS,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[PD]]
+ protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M]
+ ): MS
+
+ protected def handleFlexRequest(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ tick: Long,
+ ): ParticipantModelBaseStateData[PD, CD, MS, M]
+
+ protected def calculateFlexOptions(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ tick: Long,
+ ): ParticipantModelBaseStateData[PD, CD, MS, M]
+
+ protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ tick: Long,
+ ): CD
+
+ protected def handleFlexCtrl(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ flexCtrl: IssueFlexControl,
+ scheduler: ActorRef,
+ ): State
+
+ /** Handle an active power change by flex control.
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ data: CD,
+ lastState: MS,
+ setPower: squants.Power,
+ ): (MS, PD, FlexChangeIndicator)
+
/** Determining the reply to an
* [[edu.ie3.simona.ontology.messages.PowerMessage.RequestAssetPowerMessage]],
* send this answer and stay in the current state. If no reply can be
@@ -684,7 +833,7 @@ abstract class ParticipantAgent[
requestTick: Long,
eInPu: Dimensionless,
fInPu: Dimensionless,
- alternativeResult: PD
+ alternativeResult: PD,
): FSM.State[AgentState, ParticipantStateData[PD]]
/** Abstract definition to notify result listeners from every participant
@@ -699,7 +848,7 @@ abstract class ParticipantAgent[
baseStateData: BaseStateData[_],
currentTick: Long,
activePower: Power,
- reactivePower: ReactivePower
+ reactivePower: ReactivePower,
)(implicit outputConfig: NotifierConfig): Unit
/** Abstract definition to clean up agent value stores after power flow
@@ -715,12 +864,17 @@ abstract class ParticipantAgent[
*/
def finalizeTickAfterPF(
baseStateData: BaseStateData[PD],
- currentTick: Long
+ currentTick: Long,
): FSM.State[AgentState, ParticipantStateData[PD]]
}
object ParticipantAgent {
+ trait ParticipantMessage
+
+ final case class FinishParticipantSimulation(tick: Long)
+ extends ParticipantMessage
+
final case class StartCalculationTrigger(tick: Long)
/** Verifies that a nodal voltage value has been provided in the model
@@ -736,7 +890,7 @@ object ParticipantAgent {
*/
def getAndCheckNodalVoltage(
baseStateData: BaseStateData[_ <: PrimaryData],
- currentTick: Long
+ currentTick: Long,
): Dimensionless = {
baseStateData.voltageValueStore.last(currentTick) match {
case Some((_, voltage)) => voltage
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala
index 98230dbd5a..894c0e1c2e 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentals.scala
@@ -6,15 +6,13 @@
package edu.ie3.simona.agent.participant
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.actor.{ActorRef, FSM, PoisonPill}
-import org.apache.pekko.event.LoggingAdapter
-import org.apache.pekko.util
-import org.apache.pekko.util.Timeout
import breeze.numerics.{ceil, floor, pow, sqrt}
import edu.ie3.datamodel.models.input.system.SystemParticipantInput
import edu.ie3.datamodel.models.result.ResultEntity
-import edu.ie3.datamodel.models.result.system.SystemParticipantResult
+import edu.ie3.datamodel.models.result.system.{
+ FlexOptionsResult,
+ SystemParticipantResult,
+}
import edu.ie3.datamodel.models.result.thermal.ThermalUnitResult
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.ParticipantAgent.StartCalculationTrigger
@@ -24,61 +22,76 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
ApparentPowerAndHeat,
EnrichableData,
- PrimaryDataWithApparentPower
+ PrimaryDataWithApparentPower,
}
-import edu.ie3.simona.agent.participant.data.Data.SecondaryData.DateTime
import edu.ie3.simona.agent.participant.data.Data.{PrimaryData, SecondaryData}
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
FromOutsideBaseStateData,
- ParticipantModelBaseStateData
+ ParticipantModelBaseStateData,
}
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
CollectRegistrationConfirmMessages,
- InputModelContainer
+ InputModelContainer,
}
import edu.ie3.simona.agent.participant.statedata.{
BaseStateData,
DataCollectionStateData,
- ParticipantStateData
+ ParticipantStateData,
}
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.{
Calculate,
- HandleInformation
+ HandleInformation,
}
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.event.ResultEvent
import edu.ie3.simona.event.ResultEvent.{
+ FlexOptionsResultEvent,
ParticipantResultEvent,
- ThermalResultEvent
+ ThermalResultEvent,
}
import edu.ie3.simona.event.notifier.NotifierConfig
+import edu.ie3.simona.exceptions.CriticalFailureException
import edu.ie3.simona.exceptions.agent.{
ActorNotRegisteredException,
AgentInitializationException,
InconsistentStateException,
- InvalidRequestException
+ InvalidRequestException,
}
import edu.ie3.simona.io.result.AccompaniedSimulationResult
-import edu.ie3.simona.model.participant.{CalcRelevantData, SystemParticipant}
+import edu.ie3.simona.model.em.EmTools
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ ModelState,
+ SystemParticipant,
+}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
- AssetPowerUnchangedMessage
+ AssetPowerUnchangedMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.simona.ontology.messages.services.ServiceMessage.{
ProvisionMessage,
- RegistrationResponseMessage
+ RegistrationResponseMessage,
}
import edu.ie3.simona.util.TickUtil._
import edu.ie3.util.quantities.PowerSystemUnits._
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.{Megavars, QuantityUtil, ReactivePower}
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM, PoisonPill}
+import org.apache.pekko.event.LoggingAdapter
+import org.apache.pekko.util
+import org.apache.pekko.util.Timeout
import squants.energy.Megawatts
import squants.{Dimensionless, Each, Energy, Power}
@@ -94,46 +107,26 @@ import scala.util.{Failure, Success, Try}
protected trait ParticipantAgentFundamentals[
PD <: PrimaryDataWithApparentPower[PD],
CD <: CalcRelevantData,
+ MS <: ModelState,
D <: ParticipantStateData[PD],
I <: SystemParticipantInput,
MC <: SimonaConfig.BaseRuntimeConfig,
- M <: SystemParticipant[CD, PD]
-] extends ServiceRegistration[PD, CD, D, I, MC, M] {
- this: ParticipantAgent[PD, CD, D, I, MC, M] =>
+ M <: SystemParticipant[CD, PD, MS],
+] extends ServiceRegistration[PD, CD, MS, D, I, MC, M] {
+ this: ParticipantAgent[PD, CD, MS, D, I, MC, M] =>
protected val pdClassTag: ClassTag[PD]
protected implicit val timeout: util.Timeout = Timeout(10, TimeUnit.SECONDS)
- /** Tries to extract the DateTime value from the base state data and verifies,
- * that it is there
- *
- * @param baseStateData
- * base state data to derive information from
- * @return
- * valid DateTime value
- */
- def getAndCheckDateTime(
- baseStateData: DataCollectionStateData[_]
- ): ZonedDateTime = {
- baseStateData.extract[DateTime]() match {
- case Some(dateTime) => dateTime.dateTime
- case None =>
- throw new RuntimeException(
- "Did not receive expected information about the date time!"
- )
- }
- }
-
override def initializeParticipantForPrimaryDataReplay(
inputModel: InputModelContainer[I],
modelConfig: MC,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
outputConfig: NotifierConfig,
senderToMaybeTick: (ActorRef, Option[Long]),
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
val stateData = determineFromOutsideBaseStateData(
inputModel,
@@ -143,7 +136,7 @@ protected trait ParticipantAgentFundamentals[
resolution,
requestVoltageDeviationThreshold,
outputConfig,
- senderToMaybeTick
+ senderToMaybeTick,
)
/* Confirm final initialization */
@@ -177,7 +170,7 @@ protected trait ParticipantAgentFundamentals[
* @return
* [[FromOutsideBaseStateData]] with required information
*/
- def determineFromOutsideBaseStateData(
+ private def determineFromOutsideBaseStateData(
inputModel: InputModelContainer[I],
modelConfig: MC,
simulationStartDate: ZonedDateTime,
@@ -185,13 +178,13 @@ protected trait ParticipantAgentFundamentals[
resolution: Long,
requestVoltageDeviationThreshold: Double,
outputConfig: NotifierConfig,
- senderToMaybeTick: (ActorRef, Option[Long])
+ senderToMaybeTick: (ActorRef, Option[Long]),
): FromOutsideBaseStateData[M, PD] = {
val model = buildModel(
inputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
FromOutsideBaseStateData(
model,
@@ -210,10 +203,10 @@ protected trait ParticipantAgentFundamentals[
.to(PU)
.getValue
.doubleValue
- )
+ ),
),
ValueStore.forResult[PD](resolution, 2),
- ValueStore(resolution)
+ ValueStore(resolution),
)
}
@@ -233,7 +226,7 @@ protected trait ParticipantAgentFundamentals[
inputModel: InputModelContainer[I],
modelConfig: MC,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): M
/** Initializing the agent based on the uninitialized state and additional
@@ -267,19 +260,29 @@ protected trait ParticipantAgentFundamentals[
override def initializeParticipantForModelCalculation(
inputModel: InputModelContainer[I],
modelConfig: MC,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
outputConfig: NotifierConfig,
- scheduler: ActorRef
+ scheduler: ActorRef,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
): FSM.State[AgentState, ParticipantStateData[PD]] =
try {
/* Register for services */
val awaitRegistrationResponsesFrom =
registerForServices(inputModel.electricalInputModel, services)
+ // register with EM if applicable
+ maybeEmAgent.foreach { emAgent =>
+ emAgent ! RegisterParticipant(
+ inputModel.electricalInputModel.getUuid,
+ self.toTyped[FlexRequest],
+ inputModel.electricalInputModel,
+ )
+ }
+
val baseStateData = determineModelBaseStateData(
inputModel,
modelConfig,
@@ -288,7 +291,8 @@ protected trait ParticipantAgentFundamentals[
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
)
/* If we do have registered with secondary data providers, wait for their responses. If not, the agent does not
@@ -296,7 +300,7 @@ protected trait ParticipantAgentFundamentals[
if (awaitRegistrationResponsesFrom.nonEmpty) {
goto(HandleInformation) using CollectRegistrationConfirmMessages(
baseStateData,
- awaitRegistrationResponsesFrom
+ awaitRegistrationResponsesFrom,
)
} else {
/* Determine the next activation tick, create a ScheduleTriggerMessage and remove the recently triggered tick */
@@ -305,8 +309,22 @@ protected trait ParticipantAgentFundamentals[
)
releaseTick()
- log.debug(s"Going to {}, using {}", Idle, baseStateData)
- scheduler ! Completion(self.toTyped, newTick)
+ maybeEmAgent.foreach { emAgent =>
+ // flex is scheduled for tick 0, if no first tick available
+ emAgent ! ScheduleFlexRequest(
+ inputModel.electricalInputModel.getUuid,
+ newTick.getOrElse(0),
+ )
+ }
+
+ // important: if we are EM-managed, there is no new tick for the
+ // scheduler, since we are activated by the EmAgent from now on
+ scheduler ! Completion(
+ self.toTyped,
+ newTick.filterNot(_ => baseStateData.isEmManaged),
+ )
+
+ log.debug(s"Going to {}, using {}", Idle, nextBaseStateData)
goto(Idle) using nextBaseStateData
}
} catch {
@@ -322,13 +340,14 @@ protected trait ParticipantAgentFundamentals[
def determineModelBaseStateData(
inputModel: InputModelContainer[I],
modelConfig: MC,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ParticipantModelBaseStateData[PD, CD, M]
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
+ ): ParticipantModelBaseStateData[PD, CD, MS, M]
/** Determine all ticks between the operation start and end of the
* participant, that are at a full hour or integer multiples of the data's
@@ -349,7 +368,7 @@ protected trait ParticipantAgentFundamentals[
simulationStartDate: ZonedDateTime,
resolution: Long,
operationStart: Long,
- operationEnd: Long
+ operationEnd: Long,
): SortedSet[Long] = {
/* The profile load model holds values in the specified resolution (e.g. for each full quarter hour (00:00,
* 00:15, ...)). As the simulation might not start at an integer multiple of the resolution, we have to
@@ -385,7 +404,7 @@ protected trait ParticipantAgentFundamentals[
*/
def firstFullResolutionInSimulation(
simulationStartDate: ZonedDateTime,
- resolution: Long
+ resolution: Long,
): Long = {
if (3600L % resolution != 0)
throw new AgentInitializationException(
@@ -411,18 +430,19 @@ protected trait ParticipantAgentFundamentals[
def handleRegistrationResponse(
scheduler: ActorRef,
registrationResponse: RegistrationResponseMessage,
- stateData: CollectRegistrationConfirmMessages[PD]
+ stateData: CollectRegistrationConfirmMessages[PD],
): FSM.State[AgentState, ParticipantStateData[PD]] =
registrationResponse match {
case RegistrationResponseMessage.RegistrationSuccessfulMessage(
- maybeNextTick
+ serviceRef,
+ maybeNextTick,
) =>
val remainingResponses =
- stateData.pendingResponses.filter(_ != sender())
+ stateData.pendingResponses.filter(_ != serviceRef)
/* If the sender announces a new next tick, add it to the list of expected ticks, else remove the current entry */
val foreseenDataTicks =
- stateData.baseStateData.foreseenDataTicks + (sender() -> maybeNextTick)
+ stateData.baseStateData.foreseenDataTicks + (serviceRef -> maybeNextTick)
if (remainingResponses.isEmpty) {
/* All agent have responded. Determine the next to be used state data and reply completion to scheduler. */
@@ -432,12 +452,12 @@ protected trait ParticipantAgentFundamentals[
stateData.baseStateData.requestValueStore,
stateData.baseStateData.voltageValueStore,
stateData.baseStateData.additionalActivationTicks,
- foreseenDataTicks
+ foreseenDataTicks,
)
goToIdleReplyCompletionAndScheduleTriggerForNextAction(
complementedBaseStateData,
- scheduler
+ scheduler,
)
} else {
val foreseenDataTicksFlattened = foreseenDataTicks.collect {
@@ -447,13 +467,13 @@ protected trait ParticipantAgentFundamentals[
/* We not have yet received all responses. Wait here a bit for next messages. */
stay() using stateData.copy(
pendingResponses = remainingResponses,
- foreseenNextDataTicks = foreseenDataTicksFlattened
+ foreseenNextDataTicks = foreseenDataTicksFlattened,
)
}
- case RegistrationResponseMessage.RegistrationFailedMessage =>
+ case RegistrationResponseMessage.RegistrationFailedMessage(serviceRef) =>
self ! PoisonPill
throw new ActorNotRegisteredException(
- s"Registration of actor $actorName for ${sender()} failed."
+ s"Registration of actor $actorName for $serviceRef failed."
)
}
@@ -473,7 +493,7 @@ protected trait ParticipantAgentFundamentals[
override def handleDataProvisionAndGoToHandleInformation(
msg: ProvisionMessage[Data],
baseStateData: BaseStateData[PD],
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
/* Figure out, who is going to send data in this tick */
val expectedSenders = baseStateData.foreseenDataTicks
@@ -481,11 +501,11 @@ protected trait ParticipantAgentFundamentals[
optTick match {
case Some(tick) if msg.tick == tick =>
// expected data
- if (actorRef == sender())
+ if (actorRef == msg.serviceRef)
Some(actorRef -> Some(msg.data))
else
Some(actorRef -> None)
- case None if actorRef == sender() =>
+ case None if actorRef == msg.serviceRef =>
// unexpected data
Some(actorRef -> Some(msg.data))
case _ =>
@@ -494,21 +514,38 @@ protected trait ParticipantAgentFundamentals[
}
val unexpectedSender = baseStateData.foreseenDataTicks.exists {
- case (ref, None) => sender() == ref
+ case (ref, None) => msg.serviceRef == ref
case _ => false
}
/* If we have received unexpected data, we also have not been scheduled before */
- if (unexpectedSender)
- scheduler ! ScheduleActivation(
- self.toTyped,
- msg.tick,
- msg.unlockKey
- )
+ if (unexpectedSender) {
+ baseStateData match {
+ case modelStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ val maybeEmAgent = modelStateData.flexStateData.map(_.emAgent)
+
+ maybeEmAgent match {
+ case Some(emAgent) =>
+ emAgent ! ScheduleFlexRequest(
+ modelStateData.model.getUuid,
+ msg.tick,
+ msg.unlockKey,
+ )
+ case None =>
+ scheduler ! ScheduleActivation(
+ self.toTyped,
+ msg.tick,
+ msg.unlockKey,
+ )
+ }
+ case _ =>
+ false
+ }
+ }
/* If the sender announces a new next tick, add it to the list of expected ticks, else remove the current entry */
val foreseenDataTicks =
- baseStateData.foreseenDataTicks + (sender() -> msg.nextDataTick)
+ baseStateData.foreseenDataTicks + (msg.serviceRef -> msg.nextDataTick)
/* Go over to handling these information */
val nextStateData = DataCollectionStateData(
@@ -518,10 +555,10 @@ protected trait ParticipantAgentFundamentals[
baseStateData.requestValueStore,
baseStateData.voltageValueStore,
baseStateData.additionalActivationTicks,
- foreseenDataTicks
+ foreseenDataTicks,
),
expectedSenders,
- yetTriggered = false
+ yetTriggered = false,
)
goto(HandleInformation) using nextStateData
}
@@ -553,7 +590,7 @@ protected trait ParticipantAgentFundamentals[
stateData: DataCollectionStateData[PD],
isYetTriggered: Boolean,
tick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
)(implicit
outputConfig: NotifierConfig
): FSM.State[AgentState, ParticipantStateData[PD]] = {
@@ -562,7 +599,7 @@ protected trait ParticipantAgentFundamentals[
stateData.baseStateData match {
case fromOutsideBaseStateData: BaseStateData.FromOutsideBaseStateData[
M,
- PD
+ PD,
] =>
/* Determine the way, the reactive power may be filled up */
val reactivePowerFunc =
@@ -576,14 +613,14 @@ protected trait ParticipantAgentFundamentals[
announceSimulationResult(
stateData.baseStateData,
tick,
- AccompaniedSimulationResult(mostRecentData)
+ AccompaniedSimulationResult(mostRecentData),
)
val resultValueStore = fromOutsideBaseStateData.resultValueStore
val updatedResultValueStore = ValueStore.updateValueStore(
resultValueStore,
- currentTick,
- mostRecentData
+ tick,
+ mostRecentData,
)
val baseStateDataWithUpdatedResults =
BaseStateData.updateBaseStateData(
@@ -592,24 +629,47 @@ protected trait ParticipantAgentFundamentals[
stateData.baseStateData.requestValueStore,
stateData.baseStateData.voltageValueStore,
stateData.baseStateData.additionalActivationTicks,
- stateData.baseStateData.foreseenDataTicks
+ stateData.baseStateData.foreseenDataTicks,
)
goToIdleReplyCompletionAndScheduleTriggerForNextAction(
baseStateDataWithUpdatedResults,
- scheduler
+ scheduler,
)
case Failure(exception) =>
log.error(
"Was not able to extract received primary data correctly. Tear down the simulation. Failed with",
- exception
+ exception,
)
throw exception
}
- case _: BaseStateData.ModelBaseStateData[_, _, _] =>
+ case modelStateData: BaseStateData.ParticipantModelBaseStateData[
+ PD,
+ CD,
+ MS,
+ M,
+ ] if modelStateData.isEmManaged =>
+ val updatedReceivedSecondaryData = ValueStore.updateValueStore(
+ modelStateData.receivedSecondaryDataStore,
+ tick,
+ stateData.data.map { case (actorRef, Some(data: SecondaryData)) =>
+ actorRef -> data
+ },
+ )
+
+ // we don't go to calculate state, but return to idle directly
+ val updatedStateData = handleFlexRequest(
+ modelStateData.copy(
+ receivedSecondaryDataStore = updatedReceivedSecondaryData
+ ),
+ tick,
+ )
+ goto(Idle) using updatedStateData
+
+ case _: BaseStateData.ModelBaseStateData[_, _, _, _] =>
/* Go to calculation state and send a trigger for this to myself as well */
- self ! StartCalculationTrigger(currentTick)
+ self ! StartCalculationTrigger(tick)
goto(Calculate) using stateData
case x =>
throw new IllegalStateException(
@@ -622,6 +682,296 @@ protected trait ParticipantAgentFundamentals[
}
}
+ override protected def handleFlexRequest(
+ participantStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ tick: Long,
+ ): ParticipantModelBaseStateData[PD, CD, MS, M] = {
+ val updatedBaseStateData = calculateFlexOptions(
+ participantStateData,
+ tick,
+ )
+
+ val updatedFlexData = updatedBaseStateData.flexStateData.getOrElse(
+ throw new InconsistentStateException("Flex state data is missing!")
+ )
+
+ val newestFlexOptions = updatedFlexData.lastFlexOptions
+ .getOrElse(
+ throw new RuntimeException(
+ s"Flex options have not been calculated by agent ${participantStateData.modelUuid}"
+ )
+ )
+
+ updatedFlexData.emAgent ! newestFlexOptions
+
+ updatedBaseStateData
+ }
+
+ override protected def calculateFlexOptions(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ tick: Long,
+ ): ParticipantModelBaseStateData[PD, CD, MS, M] = {
+
+ implicit val startDateTime: ZonedDateTime = baseStateData.startDate
+
+ val relevantData =
+ createCalcRelevantData(
+ baseStateData,
+ tick,
+ )
+
+ val lastState = getLastOrInitialStateData(baseStateData, tick)
+
+ val flexOptions =
+ baseStateData.model.determineFlexOptions(relevantData, lastState)
+
+ // announce flex options (we can do this right away, since this
+ // does not include reactive power which could change later)
+ if (baseStateData.outputConfig.flexResult) {
+ val flexResult = flexOptions match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ new FlexOptionsResult(
+ tick.toDateTime,
+ modelUuid,
+ referencePower.toMegawatts.asMegaWatt,
+ minPower.toMegawatts.asMegaWatt,
+ maxPower.toMegawatts.asMegaWatt,
+ )
+ }
+
+ notifyListener(FlexOptionsResultEvent(flexResult))
+ }
+
+ baseStateData.copy(
+ flexStateData = baseStateData.flexStateData.map(data =>
+ data.copy(lastFlexOptions = Some(flexOptions))
+ )
+ )
+ }
+
+ override protected def handleFlexCtrl(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ flexCtrl: IssueFlexControl,
+ scheduler: ActorRef,
+ ): State = {
+ /* Collect all needed information */
+ val flexStateData = baseStateData.flexStateData.getOrElse(
+ throw new IllegalStateException(
+ s"Received $flexCtrl, but participant agent is not in EM mode"
+ )
+ )
+ val relevantData = createCalcRelevantData(
+ baseStateData,
+ flexCtrl.tick,
+ )
+ val lastState = getLastOrInitialStateData(baseStateData, flexCtrl.tick)
+
+ val flexOptions = flexStateData.lastFlexOptions
+ .getOrElse(
+ throw new IllegalStateException(
+ "Flex options have not been calculated before."
+ )
+ )
+
+ val setPointActivePower =
+ EmTools.determineFlexPower(flexOptions, flexCtrl)
+
+ /* Handle the flex signal */
+ val (updatedState, result, flexChangeIndicator) =
+ handleControlledPowerChange(
+ flexCtrl.tick,
+ baseStateData,
+ relevantData,
+ lastState,
+ setPointActivePower,
+ )
+
+ // sanity check, simulation would hang if this matches
+ flexChangeIndicator.changesAtTick match {
+ case Some(changeAtTick) if changeAtTick <= flexCtrl.tick =>
+ throw new CriticalFailureException(
+ s"Scheduling agent ${self.path} (${baseStateData.modelUuid}) for activation at tick $changeAtTick, although current tick is ${flexCtrl.tick}"
+ )
+ case _ =>
+ }
+
+ // store new state data
+ val updatedStateData: ParticipantModelBaseStateData[PD, CD, MS, M] =
+ baseStateData
+ .copy(
+ stateDataStore = ValueStore.updateValueStore(
+ baseStateData.stateDataStore,
+ flexCtrl.tick,
+ updatedState,
+ )
+ )
+
+ // Send out results etc.
+ val stateDataWithResults = handleCalculatedResult(
+ updatedStateData,
+ result,
+ flexCtrl.tick,
+ )
+
+ // determine next tick
+ val expectedDataComesNext =
+ pollNextActivationTrigger(stateDataWithResults).exists { dataTick =>
+ flexChangeIndicator.changesAtTick.forall(_ >= dataTick)
+ }
+ val (nextActivation, stateDataFinal) = Option
+ .when(expectedDataComesNext)(
+ popNextActivationTrigger(stateDataWithResults)
+ )
+ .getOrElse((flexChangeIndicator.changesAtTick, stateDataWithResults))
+
+ flexStateData.emAgent ! FlexCtrlCompletion(
+ baseStateData.modelUuid,
+ result.toApparentPower,
+ flexChangeIndicator.changesAtNextActivation,
+ nextActivation,
+ )
+
+ stay() using stateDataFinal
+ }
+
+ /** Additional actions on a new calculated simulation result. Typically: Send
+ * out result to listeners and save result in corresponding ValueStore
+ *
+ * @param baseStateData
+ * The base state data
+ * @param result
+ * that has been calculated for the current tick
+ * @param currentTick
+ * the current tick
+ * @return
+ * updated base state data
+ */
+ protected def handleCalculatedResult(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ result: PD,
+ currentTick: Long,
+ ): ParticipantModelBaseStateData[PD, CD, MS, M] = {
+
+ // announce last result to listeners
+ announceSimulationResult(
+ baseStateData,
+ currentTick,
+ AccompaniedSimulationResult(result),
+ )(baseStateData.outputConfig)
+
+ baseStateData.copy(
+ resultValueStore = ValueStore.updateValueStore(
+ baseStateData.resultValueStore,
+ currentTick,
+ result,
+ )
+ )
+ }
+
+ /** Calculate the power output of the participant without needing any
+ * secondary data. The next state is [[Idle]], sending a [[Completion]] to
+ * scheduler and using update result values.
+ *
+ * @param baseStateData
+ * Base state data to update
+ * @param lastModelState
+ * The current model state, before updating it
+ * @param currentTick
+ * Tick, the trigger belongs to
+ * @param scheduler
+ * [[ActorRef]] to the scheduler in the simulation
+ * @return
+ * [[Idle]] with updated result values
+ */
+ override def calculatePowerWithoutSecondaryDataAndGoToIdle(
+ baseStateData: ParticipantModelBaseStateData[PD, CD, MS, M],
+ lastModelState: MS,
+ currentTick: Long,
+ scheduler: ActorRef,
+ nodalVoltage: squants.Dimensionless,
+ ): FSM.State[AgentState, ParticipantStateData[PD]] = {
+ val calcRelevantData =
+ createCalcRelevantData(baseStateData, currentTick)
+
+ val updatedState =
+ updateState(
+ currentTick,
+ lastModelState,
+ calcRelevantData,
+ nodalVoltage,
+ baseStateData.model,
+ )
+
+ val result = calculateModelPowerFunc(
+ currentTick,
+ baseStateData,
+ updatedState,
+ nodalVoltage,
+ )
+
+ val updatedResultValueStore =
+ ValueStore.updateValueStore(
+ baseStateData.resultValueStore,
+ currentTick,
+ result,
+ )
+
+ /* Inform the listeners about new result */
+ announceSimulationResult(
+ baseStateData,
+ currentTick,
+ AccompaniedSimulationResult(result),
+ )(baseStateData.outputConfig)
+
+ val updatedStateDataStore = ValueStore.updateValueStore(
+ baseStateData.stateDataStore,
+ currentTick,
+ updatedState,
+ )
+
+ /* In this case, without secondary data, the agent has been triggered by an ActivityStartTrigger by itself,
+ * therefore pop the next one */
+ val baseStateDataWithUpdatedResultStore =
+ baseStateData.copy(
+ resultValueStore = updatedResultValueStore,
+ stateDataStore = updatedStateDataStore,
+ )
+
+ goToIdleReplyCompletionAndScheduleTriggerForNextAction(
+ baseStateDataWithUpdatedResultStore,
+ scheduler,
+ )
+ }
+
+ /** Update the last known model state with the given external, relevant data
+ *
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
+ */
+ protected def updateState(
+ tick: Long,
+ modelState: MS,
+ calcRelevantData: CD,
+ nodalVoltage: squants.Dimensionless,
+ model: M,
+ ): MS
+
/** Determining the active to reactive power function to apply
*
* @param tick
@@ -633,7 +983,7 @@ protected trait ParticipantAgentFundamentals[
*/
def getReactivePowerFunction(
tick: Long,
- baseStateData: FromOutsideBaseStateData[M, PD]
+ baseStateData: FromOutsideBaseStateData[M, PD],
): Power => ReactivePower =
if (baseStateData.fillUpReactivePowerWithModelFunc) {
/* Use the model's active to reactive power function */
@@ -665,7 +1015,7 @@ protected trait ParticipantAgentFundamentals[
*/
def prepareData(
data: Map[ActorRef, Option[_ <: Data]],
- reactivePowerFunction: Power => ReactivePower
+ reactivePowerFunction: Power => ReactivePower,
): Try[PD] =
data.headOption
.flatMap { case (_, maybeData) =>
@@ -723,18 +1073,63 @@ protected trait ParticipantAgentFundamentals[
*/
def goToIdleReplyCompletionAndScheduleTriggerForNextAction(
baseStateData: BaseStateData[PD],
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
/* Determine the very next tick, where activation is required */
val (maybeNextTick, updatedBaseStateData) =
popNextActivationTrigger(baseStateData)
releaseTick()
- scheduler ! Completion(self.toTyped, maybeNextTick)
+
+ val emManaged = baseStateData match {
+ case modelStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ val maybeEmAgent = modelStateData.flexStateData.map(_.emAgent)
+
+ maybeEmAgent.foreach {
+ _ ! ScheduleFlexRequest(
+ modelStateData.model.getUuid,
+ maybeNextTick.getOrElse(0),
+ )
+ }
+
+ maybeEmAgent.isDefined
+ case _ =>
+ false
+ }
+
+ // Only for completing initialization:
+ // if we are EM-managed, there is no new tick for the
+ // scheduler, since we are activated by the EmAgent from now on
+ scheduler ! Completion(
+ self.toTyped,
+ maybeNextTick.filterNot(_ => emManaged),
+ )
unstashAll()
goto(Idle) using updatedBaseStateData
}
+ def pollNextActivationTrigger(
+ baseStateData: BaseStateData[PD]
+ ): Option[Long] = {
+ /* Determine what comes next: An additional activation or new data - or both at once */
+ val nextAdditionalActivation =
+ baseStateData.additionalActivationTicks.headOption
+ val nextDataTick =
+ baseStateData.foreseenDataTicks.values.toSeq.sorted.headOption.flatten
+
+ (nextAdditionalActivation, nextDataTick) match {
+ case (None, Some(dataTick)) =>
+ Some(dataTick)
+ case (Some(additionalTick), Some(dataTick))
+ if dataTick < additionalTick =>
+ Some(dataTick)
+ case (Some(additionalTick), _) =>
+ Some(additionalTick)
+ case (None, None) =>
+ None
+ }
+ }
+
/** Pop the next tick, in which the agent wishes to get triggered, build a
* regarding message and update the base state data. This might be either a
* tick, where new data is foreseen to be sent by a
@@ -762,7 +1157,7 @@ protected trait ParticipantAgentFundamentals[
/* There is only a data tick available */
(
Some(dataTick),
- baseStateData
+ baseStateData,
)
case (Some(additionalTick), Some(dataTick))
if dataTick < additionalTick =>
@@ -770,7 +1165,7 @@ protected trait ParticipantAgentFundamentals[
* trigger. */
(
Some(dataTick),
- baseStateData
+ baseStateData,
)
case (Some(additionalTick), _) =>
/* The next activation is additional (either there is no foreseen data tick or it is after the additional tick.
@@ -783,12 +1178,12 @@ protected trait ParticipantAgentFundamentals[
baseStateData.requestValueStore,
baseStateData.voltageValueStore,
upcomingActivationTicks,
- baseStateData.foreseenDataTicks
+ baseStateData.foreseenDataTicks,
)
(
Some(additionalTick),
- updatedBaseStateData
+ updatedBaseStateData,
)
case (None, None) =>
/* We don't know nothing about either additional activation nor new incoming data */
@@ -828,10 +1223,8 @@ protected trait ParticipantAgentFundamentals[
requestTick: Long,
eInPu: Dimensionless,
fInPu: Dimensionless,
- alternativeResult: PD
+ alternativeResult: PD,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
- log.debug(s"Received asset power request for tick {}", requestTick)
-
/* Check, if there is any calculation foreseen for this tick. If so, wait with the response. */
val activationExpected =
baseStateData.additionalActivationTicks.headOption.exists(_ < requestTick)
@@ -839,10 +1232,10 @@ protected trait ParticipantAgentFundamentals[
baseStateData.foreseenDataTicks.values.flatten.exists(_ < requestTick)
if (activationExpected || dataExpected) {
log.debug(
- s"I got a request for power from '{}' for tick '{}', but I'm still waiting for new" +
- s" results before this tick. Waiting with the response.",
+ s"Received power request from '{}' for tick '{}', but I'm still waiting for new results before " +
+ s"this tick. Waiting with the response.",
sender(),
- requestTick
+ requestTick,
)
stash()
stay() using baseStateData
@@ -853,7 +1246,7 @@ protected trait ParticipantAgentFundamentals[
sqrt(
pow(eInPu.toEach, 2) + pow(
fInPu.toEach,
- 2
+ 2,
)
)
)
@@ -863,7 +1256,7 @@ protected trait ParticipantAgentFundamentals[
ValueStore.updateValueStore(
baseStateData.voltageValueStore,
requestTick,
- nodalVoltage
+ nodalVoltage,
)
/* Determine the most recent request */
val mostRecentRequest =
@@ -876,7 +1269,7 @@ protected trait ParticipantAgentFundamentals[
requestTick,
updatedVoltageStore,
nodalVoltage,
- lastNodalVoltage
+ lastNodalVoltage,
).getOrElse {
/* If a fast reply is not possible, determine it the old fashioned way */
determineReply(
@@ -885,7 +1278,7 @@ protected trait ParticipantAgentFundamentals[
mostRecentRequest,
nodalVoltage,
updatedVoltageStore,
- alternativeResult
+ alternativeResult,
)
}
}
@@ -918,7 +1311,7 @@ protected trait ParticipantAgentFundamentals[
requestTick: Long,
voltageValueStore: ValueStore[Dimensionless],
nodalVoltage: Dimensionless,
- lastNodalVoltage: Option[(Long, Dimensionless)]
+ lastNodalVoltage: Option[(Long, Dimensionless)],
): Option[FSM.State[AgentState, ParticipantStateData[PD]]] = {
implicit val outputConfig: NotifierConfig =
baseStateData.outputConfig
@@ -937,10 +1330,15 @@ protected trait ParticipantAgentFundamentals[
voltageValueStore = voltageValueStore
) replying AssetPowerUnchangedMessage(
latestProvidedValues.p,
- latestProvidedValues.q
+ latestProvidedValues.q,
)
)
- case modelBaseStateData: ParticipantModelBaseStateData[PD, CD, M] =>
+ case modelBaseStateData: ParticipantModelBaseStateData[
+ PD,
+ CD,
+ MS,
+ M,
+ ] =>
/* Check, if the last request has been made with the same nodal voltage. If not, recalculate the reactive
* power. */
implicit val puTolerance: Dimensionless =
@@ -955,7 +1353,7 @@ protected trait ParticipantAgentFundamentals[
Some(
stay() using modelBaseStateData replying AssetPowerUnchangedMessage(
latestProvidedValues.p,
- latestProvidedValues.q
+ latestProvidedValues.q,
)
)
} else {
@@ -1000,7 +1398,7 @@ protected trait ParticipantAgentFundamentals[
mostRecentRequest: Option[(Long, PD)],
nodalVoltage: Dimensionless,
updatedVoltageValueStore: ValueStore[Dimensionless],
- alternativeResult: PD
+ alternativeResult: PD,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
/* No fast reply possible --> Some calculations have to be made */
mostRecentRequest match {
@@ -1012,7 +1410,12 @@ protected trait ParticipantAgentFundamentals[
if lastRequestTick == requestTick =>
/* Repetitive request for the same tick, but with different voltage */
baseStateData match {
- case modelBaseStateData: ParticipantModelBaseStateData[PD, CD, M] =>
+ case modelBaseStateData: ParticipantModelBaseStateData[
+ PD,
+ CD,
+ MS,
+ M,
+ ] =>
/* Active power is yet calculated, but reactive power needs update */
val nextReactivePower = modelBaseStateData.model
.calculateReactivePower(lastResult.p, nodalVoltage)
@@ -1022,17 +1425,17 @@ protected trait ParticipantAgentFundamentals[
ValueStore.updateValueStore(
baseStateData.requestValueStore,
requestTick,
- lastResult.withReactivePower(nextReactivePower)
+ lastResult.withReactivePower(nextReactivePower),
)
val nextStateData =
modelBaseStateData.copy(
requestValueStore = updatedRequestValueStore,
- voltageValueStore = updatedVoltageValueStore
+ voltageValueStore = updatedVoltageValueStore,
)
stay() using nextStateData replying AssetPowerChangedMessage(
lastResult.p,
- nextReactivePower
+ nextReactivePower,
)
case unexpectedStateData =>
throw new IllegalStateException(
@@ -1047,7 +1450,7 @@ protected trait ParticipantAgentFundamentals[
getRelevantResultData(
requestTick,
baseStateData.resultValueStore,
- baseStateData.requestValueStore
+ baseStateData.requestValueStore,
) match {
case Some(relevantData) =>
/* There is at least one relevant simulation result apparent, which might also be the most recent one
@@ -1058,7 +1461,7 @@ protected trait ParticipantAgentFundamentals[
requestTick,
nodalVoltage,
updatedVoltageValueStore,
- alternativeResult
+ alternativeResult,
)
case None =>
/* There is no simulation result at all. Reply with zero power */
@@ -1066,7 +1469,7 @@ protected trait ParticipantAgentFundamentals[
baseStateData,
alternativeResult,
requestTick,
- updatedVoltageValueStore
+ updatedVoltageValueStore,
)
}
}
@@ -1092,7 +1495,7 @@ protected trait ParticipantAgentFundamentals[
def getRelevantResultData(
requestTick: Long,
resultValueStore: ValueStore[PD],
- requestValueStore: ValueStore[PD]
+ requestValueStore: ValueStore[PD],
): Option[RelevantResultValues[PD]] = {
/* The actual tick window for averaging is the last request tick and this request tick (both including) */
val (averagingWindowStart, averagingWindowEnd) =
@@ -1102,7 +1505,7 @@ protected trait ParticipantAgentFundamentals[
* averaging window and it's end (both including) are relevant for averaging the simulated primary data */
val firstRelevantTick = determineFirstRelevantTick(
averagingWindowStart,
- resultValueStore
+ resultValueStore,
)
/* Let's see, if we got some simulation results between the first relevant simulation tick and this request's tick */
@@ -1115,7 +1518,7 @@ protected trait ParticipantAgentFundamentals[
RelevantResultValues(
averagingWindowStart,
averagingWindowEnd,
- simulationResults
+ simulationResults,
)
)
} else {
@@ -1135,7 +1538,7 @@ protected trait ParticipantAgentFundamentals[
*/
private def determineTickWindow(
requestTick: Long,
- requestValueStore: ValueStore[_]
+ requestValueStore: ValueStore[_],
): (Long, Long) =
requestValueStore.lastKnownTick(requestTick - 1) match {
case Some(lastRequestTick) => (lastRequestTick, requestTick)
@@ -1156,7 +1559,7 @@ protected trait ParticipantAgentFundamentals[
*/
private def determineFirstRelevantTick(
lastRequestTick: Long,
- resultValueStore: ValueStore[_]
+ resultValueStore: ValueStore[_],
): Long =
resultValueStore.lastKnownTick(lastRequestTick) match {
case Some(firstRelevantDataTick) => firstRelevantDataTick
@@ -1189,7 +1592,7 @@ protected trait ParticipantAgentFundamentals[
requestTick: Long,
nodalVoltage: Dimensionless,
voltageValueStore: ValueStore[Dimensionless],
- alternativeResult: PD
+ alternativeResult: PD,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
if (relevantResults.relevantData.nonEmpty) {
averagePowerAndStay(
@@ -1199,18 +1602,18 @@ protected trait ParticipantAgentFundamentals[
relevantResults.windowStart,
relevantResults.windowEnd,
nodalVoltage,
- voltageValueStore
+ voltageValueStore,
)
} else {
log.debug(
s"No relevant data apparent, stay and reply with alternative result {}.",
- alternativeResult
+ alternativeResult,
)
stayWithUpdatedRequestValueStore(
baseData,
alternativeResult,
requestTick,
- voltageValueStore
+ voltageValueStore,
)
}
}
@@ -1245,20 +1648,20 @@ protected trait ParticipantAgentFundamentals[
windowStartTick: Long,
windowEndTick: Long,
nodalVoltage: Dimensionless,
- voltageValueStore: ValueStore[Dimensionless]
+ voltageValueStore: ValueStore[Dimensionless],
): FSM.State[AgentState, ParticipantStateData[PD]] = {
val averageResult = determineAverageResult(
baseData,
tickToResult,
windowStartTick,
windowEndTick,
- nodalVoltage
+ nodalVoltage,
)
stayWithUpdatedRequestValueStore(
baseData,
averageResult,
requestTick,
- voltageValueStore
+ voltageValueStore,
)
}
@@ -1284,12 +1687,12 @@ protected trait ParticipantAgentFundamentals[
tickToResult: Map[Long, PD],
windowStartTick: Long,
windowEndTick: Long,
- nodalVoltage: Dimensionless
+ nodalVoltage: Dimensionless,
): PD = {
/* Determine, how the single model would transfer the active into reactive power */
val activeToReactivePowerFunction = baseStateData match {
case _: FromOutsideBaseStateData[M, PD] => None
- case modelBaseStateData: ParticipantModelBaseStateData[PD, CD, M] =>
+ case modelBaseStateData: ParticipantModelBaseStateData[PD, CD, MS, M] =>
Some(
modelBaseStateData.model.activeToReactivePowerFunc(nodalVoltage)
)
@@ -1299,7 +1702,7 @@ protected trait ParticipantAgentFundamentals[
tickToResult,
windowStartTick,
windowEndTick,
- activeToReactivePowerFunction
+ activeToReactivePowerFunction,
)
}
@@ -1322,7 +1725,7 @@ protected trait ParticipantAgentFundamentals[
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ] = None
+ ] = None,
): PD
/** Updates the given base state data by inserting updated request value store
@@ -1346,13 +1749,13 @@ protected trait ParticipantAgentFundamentals[
baseStateData: BaseStateData[PD],
averageResult: PD,
requestTick: Long,
- voltageValueStore: ValueStore[Dimensionless]
+ voltageValueStore: ValueStore[Dimensionless],
): FSM.State[AgentState, ParticipantStateData[PD]] = {
val updatedRequestValueStore =
ValueStore.updateValueStore(
baseStateData.requestValueStore,
requestTick,
- averageResult
+ averageResult,
)
val nextStateData = BaseStateData.updateBaseStateData(
baseStateData,
@@ -1360,7 +1763,7 @@ protected trait ParticipantAgentFundamentals[
updatedRequestValueStore,
voltageValueStore,
baseStateData.additionalActivationTicks,
- baseStateData.foreseenDataTicks
+ baseStateData.foreseenDataTicks,
)
averageResult.toApparentPower match {
@@ -1369,63 +1772,6 @@ protected trait ParticipantAgentFundamentals[
}
}
- /** Calculate the power output of the participant without needing any
- * secondary data. The next state is [[Idle]], sending a [[Completion]] to
- * scheduler and using update result values.
- *
- * @param baseStateData
- * Base state data to update
- * @param currentTick
- * Tick, the trigger belongs to
- * @param scheduler
- * [[ActorRef]] to the scheduler in the simulation
- * @return
- * [[Idle]] with updated result values
- */
- override def calculatePowerWithoutSecondaryDataAndGoToIdle(
- baseStateData: ParticipantModelBaseStateData[PD, CD, M],
- currentTick: Long,
- scheduler: ActorRef,
- nodalVoltage: Dimensionless,
- calculateModelPowerFunc: (
- Long,
- ParticipantModelBaseStateData[PD, CD, M],
- Dimensionless
- ) => PD
- ): FSM.State[AgentState, ParticipantStateData[PD]] = {
- val result =
- calculateModelPowerFunc(currentTick, baseStateData, nodalVoltage)
-
- val updatedResultValueStore =
- ValueStore.updateValueStore(
- baseStateData.resultValueStore,
- currentTick,
- result
- )
-
- /* Inform the listeners about new result */
- announceSimulationResult(
- baseStateData,
- currentTick,
- AccompaniedSimulationResult(result)
- )(baseStateData.outputConfig)
-
- /* In this case, without secondary data, the agent has been triggered by an ActivityStartTrigger by itself,
- * therefore pop the next one */
- val baseStateDataWithUpdatedResultStore = BaseStateData.updateBaseStateData(
- baseStateData,
- updatedResultValueStore,
- baseStateData.requestValueStore,
- baseStateData.voltageValueStore,
- baseStateData.additionalActivationTicks,
- baseStateData.foreseenDataTicks
- )
- goToIdleReplyCompletionAndScheduleTriggerForNextAction(
- baseStateDataWithUpdatedResultStore,
- scheduler
- )
- }
-
/** Notify listeners about a new simulation result of the participant agent,
* if the config says so.
*
@@ -1438,10 +1784,10 @@ protected trait ParticipantAgentFundamentals[
* @param outputConfig
* Configuration of the output behaviour
*/
- def announceSimulationResult(
+ protected def announceSimulationResult(
baseStateData: BaseStateData[PD],
tick: Long,
- result: AccompaniedSimulationResult[PD]
+ result: AccompaniedSimulationResult[PD],
)(implicit outputConfig: NotifierConfig): Unit =
if (outputConfig.simulationResultInfo) {
notifyListener(
@@ -1452,8 +1798,8 @@ protected trait ParticipantAgentFundamentals[
.foreach(notifyListener(_))
}
- /** Update the result and calc relevant data value stores, inform all
- * registered listeners and go to Idle using the updated base state data
+ /** Update the result value store, inform all registered listeners and go to
+ * Idle using the updated base state data
*
* @param scheduler
* Actor reference of the scheduler
@@ -1470,43 +1816,28 @@ protected trait ParticipantAgentFundamentals[
scheduler: ActorRef,
baseStateData: BaseStateData[PD],
result: AccompaniedSimulationResult[PD],
- relevantData: CD
+ relevantData: CD,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
/* Update the value stores */
val updatedValueStore =
ValueStore.updateValueStore(
baseStateData.resultValueStore,
currentTick,
- result.primaryData
+ result.primaryData,
)
- val updatedRelevantDataStore =
- baseStateData match {
- case data: BaseStateData.ModelBaseStateData[_, _, _] =>
- ValueStore.updateValueStore(
- data.calcRelevantDateStore,
- currentTick,
- relevantData
- )
- case _ =>
- throw new InconsistentStateException(
- "Cannot find calculation relevant data to update."
- )
- }
-
/* Inform the listeners about new result */
announceSimulationResult(
baseStateData,
currentTick,
- result
+ result,
)(baseStateData.outputConfig)
/* Update the base state data */
val baseStateDateWithUpdatedResults =
baseStateData match {
- case data: ParticipantModelBaseStateData[PD, CD, M] =>
+ case data: ParticipantModelBaseStateData[PD, CD, MS, M] =>
data.copy(
- resultValueStore = updatedValueStore,
- calcRelevantDateStore = updatedRelevantDataStore
+ resultValueStore = updatedValueStore
)
case _ =>
throw new InconsistentStateException(
@@ -1516,7 +1847,7 @@ protected trait ParticipantAgentFundamentals[
goToIdleReplyCompletionAndScheduleTriggerForNextAction(
baseStateDateWithUpdatedResults,
- scheduler
+ scheduler,
)
}
@@ -1538,7 +1869,7 @@ protected trait ParticipantAgentFundamentals[
baseStateData: BaseStateData[_],
tick: Long,
activePower: Power,
- reactivePower: ReactivePower
+ reactivePower: ReactivePower,
)(implicit outputConfig: NotifierConfig): Unit =
if (outputConfig.powerRequestReply) {
log.warning(
@@ -1558,7 +1889,7 @@ protected trait ParticipantAgentFundamentals[
*/
override def finalizeTickAfterPF(
baseStateData: BaseStateData[PD],
- currentTick: Long
+ currentTick: Long,
): FSM.State[AgentState, ParticipantStateData[PD]] = {
baseStateData.requestValueStore.last(currentTick).foreach {
case (_, data) =>
@@ -1567,7 +1898,7 @@ protected trait ParticipantAgentFundamentals[
baseStateData,
currentTick,
data.p,
- data.q
+ data.q,
)(baseStateData.outputConfig)
}
goto(Idle) using baseStateData
@@ -1587,7 +1918,7 @@ protected trait ParticipantAgentFundamentals[
def buildResultEvent(
baseStateData: BaseStateData[PD],
tick: Long,
- result: PD
+ result: PD,
): ParticipantResultEvent = {
val uuid = baseStateData.modelUuid
val dateTime = tick.toDateTime(baseStateData.startDate)
@@ -1631,12 +1962,12 @@ protected trait ParticipantAgentFundamentals[
protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: PD
+ result: PD,
): SystemParticipantResult
/** Returns secondary service of type T or throws exception
* @param services
- * the services Option used in
+ * the services used in
* [[edu.ie3.simona.agent.participant.statedata.BaseStateData.ModelBaseStateData]]
* @param tag
* ClassTag of T
@@ -1646,14 +1977,9 @@ protected trait ParticipantAgentFundamentals[
* secondary service of given type
*/
protected def getService[T <: SecondaryDataService[_]](
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]]
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]]
)(implicit tag: ClassTag[T]): ActorRef =
services
- .getOrElse(
- throw new InconsistentStateException(
- "No services provided by ParticipantModelBaseStateData."
- )
- )
.find {
case _: T => true
case _ => false
@@ -1666,7 +1992,7 @@ protected trait ParticipantAgentFundamentals[
.actorRef
}
-case object ParticipantAgentFundamentals {
+object ParticipantAgentFundamentals {
/** Hold all necessary information for later averaging of participant
* simulations' results.
@@ -1686,7 +2012,7 @@ case object ParticipantAgentFundamentals {
final case class RelevantResultValues[+PD <: PrimaryData](
windowStart: Long,
windowEnd: Long,
- relevantData: Map[Long, PD]
+ relevantData: Map[Long, PD],
)
/** Determine the average apparent power within the given tick window
@@ -1709,21 +2035,21 @@ case object ParticipantAgentFundamentals {
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
] = None,
- log: LoggingAdapter
+ log: LoggingAdapter,
): ApparentPower = {
val p = QuantityUtil.average[Power, Energy](
tickToResults.map { case (tick, pd) =>
tick -> pd.p
},
windowStart,
- windowEnd
+ windowEnd,
) match {
case Success(pSuccess) =>
pSuccess
case Failure(exception) =>
log.warning(
"Unable to determine average active power. Apply 0 instead. Cause:\n\t{}",
- exception
+ exception,
)
Megawatts(0d)
}
@@ -1739,14 +2065,14 @@ case object ParticipantAgentFundamentals {
}
},
windowStart,
- windowEnd
+ windowEnd,
) match {
case Success(pSuccess) =>
Megavars(pSuccess.toMegawatts)
case Failure(exception) =>
log.warning(
"Unable to determine average reactive power. Apply 0 instead. Cause:\n\t{}",
- exception
+ exception,
)
Megavars(0d)
}
@@ -1774,14 +2100,14 @@ case object ParticipantAgentFundamentals {
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
] = None,
- log: LoggingAdapter
+ log: LoggingAdapter,
): ApparentPowerAndHeat = {
val tickToResultsApparentPower: Map[Long, ApparentPower] =
tickToResults.map { case (tick, pd) =>
(
tick,
- ApparentPower(Megawatts(pd.p.toMegawatts), Megavars(pd.q.toMegavars))
+ ApparentPower(Megawatts(pd.p.toMegawatts), Megavars(pd.q.toMegavars)),
)
}
@@ -1790,7 +2116,7 @@ case object ParticipantAgentFundamentals {
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
val qDot = QuantityUtil.average[Power, Energy](
@@ -1798,13 +2124,13 @@ case object ParticipantAgentFundamentals {
tick -> Megawatts(pd.qDot.toMegawatts)
},
windowStart,
- windowEnd
+ windowEnd,
) match {
case Success(qDotSuccess) => qDotSuccess
case Failure(exception) =>
log.warning(
"Unable to determine average thermal power. Apply 0 instead. Cause:\n\t{}",
- exception
+ exception,
)
Megawatts(0d)
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala
index d600049a27..b8b2ef33ad 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/ServiceRegistration.scala
@@ -14,24 +14,29 @@ import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.{
ActorEvMovementsService,
ActorPriceService,
- ActorWeatherService
+ ActorWeatherService,
}
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.config.SimonaConfig
-import edu.ie3.simona.model.participant.{CalcRelevantData, SystemParticipant}
import edu.ie3.simona.exceptions.agent.ServiceRegistrationException
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ ModelState,
+ SystemParticipant,
+}
import edu.ie3.simona.ontology.messages.services.EvMessage.RegisterForEvDataMessage
import edu.ie3.simona.ontology.messages.services.WeatherMessage.RegisterForWeatherMessage
trait ServiceRegistration[
PD <: PrimaryDataWithApparentPower[PD],
CD <: CalcRelevantData,
+ MS <: ModelState,
D <: ParticipantStateData[PD],
I <: SystemParticipantInput,
MC <: SimonaConfig.BaseRuntimeConfig,
- M <: SystemParticipant[CD, PD]
+ M <: SystemParticipant[CD, PD, MS],
] {
- this: ParticipantAgent[PD, CD, D, I, MC, M] =>
+ this: ParticipantAgent[PD, CD, MS, D, I, MC, M] =>
/** Registers the agent for the needed services and collects all actor
* references, with which the actor has been registered
@@ -41,19 +46,15 @@ trait ServiceRegistration[
* @param services
* Definition of where to get what
* @return
- * a vector of actor references to wait for responses
+ * an iterable of actor references to wait for responses
*/
def registerForServices(
inputModel: I,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]]
- ): Vector[ActorRef] =
- services
- .map(sources =>
- sources.flatMap(service =>
- registerForSecondaryService(service, inputModel)
- )
- )
- .getOrElse(Vector.empty[ActorRef])
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
+ ): Iterable[ActorRef] =
+ services.flatMap(service =>
+ registerForSecondaryService(service, inputModel)
+ )
/** Register for the distinct secondary service
*
@@ -71,20 +72,20 @@ trait ServiceRegistration[
S <: SecondaryData
](
serviceDefinition: SecondaryDataService[S],
- inputModel: I
+ inputModel: I,
): Option[ActorRef] = serviceDefinition match {
case SecondaryDataService.ActorPriceService(_) =>
log.debug(
s"Attempt to register for {}. This is currently not supported.",
- ActorPriceService
+ ActorPriceService,
)
None
- case ActorWeatherService(actorRef) =>
- registerForWeather(actorRef, inputModel)
- Some(actorRef)
- case ActorEvMovementsService(actorRef) =>
- registerForEvMovements(actorRef, inputModel)
- Some(actorRef)
+ case ActorWeatherService(serviceRef) =>
+ registerForWeather(serviceRef, inputModel)
+ Some(serviceRef)
+ case ActorEvMovementsService(serviceRef) =>
+ registerForEvMovements(serviceRef, inputModel)
+ Some(serviceRef)
}
/** Register for the weather service
@@ -97,7 +98,7 @@ trait ServiceRegistration[
*/
private def registerForWeather(
actorRef: ActorRef,
- inputModel: I
+ inputModel: I,
): Unit = {
/* If we are asked to register for weather, determine the proper geo position */
val geoPosition = inputModel.getNode.getGeoPosition
@@ -117,19 +118,19 @@ trait ServiceRegistration[
/** Register for the EV movement service
*
- * @param actorRef
+ * @param serviceRef
* Actor reference of the EV movements service
* @param inputModel
* Input model of the simulation mode
* @return
*/
private def registerForEvMovements(
- actorRef: ActorRef,
- inputModel: I
+ serviceRef: ActorRef,
+ inputModel: I,
): Unit = {
inputModel match {
case evcsInput: EvcsInput =>
- actorRef ! RegisterForEvDataMessage(evcsInput.getUuid)
+ serviceRef ! RegisterForEvDataMessage(evcsInput.getUuid)
case _ =>
throw new ServiceRegistrationException(
s"Cannot register for EV movements information at node ${inputModel.getNode.getId} " +
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/data/Data.scala b/src/main/scala/edu/ie3/simona/agent/participant/data/Data.scala
index 2bba0f0509..35748f755f 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/data/Data.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/data/Data.scala
@@ -62,7 +62,7 @@ object Data {
val ZERO_POWER: ApparentPower = ApparentPower(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
/** Active power as participant simulation result
@@ -76,7 +76,7 @@ object Data {
override def toApparentPower: ApparentPower =
ApparentPower(
p,
- Megavars(0d)
+ Megavars(0d),
)
override def add(q: ReactivePower): ApparentPower =
@@ -92,7 +92,7 @@ object Data {
*/
final case class ApparentPower(
override val p: Power,
- override val q: ReactivePower
+ override val q: ReactivePower,
) extends PrimaryDataWithApparentPower[ApparentPower] {
override def toApparentPower: ApparentPower = this
@@ -109,14 +109,14 @@ object Data {
*/
final case class ActivePowerAndHeat(
override val p: Power,
- override val qDot: Power
+ override val qDot: Power,
) extends PrimaryData
with Heat
with EnrichableData[ApparentPowerAndHeat] {
override def toApparentPower: ApparentPower =
ApparentPower(
p,
- Megavars(0d)
+ Megavars(0d),
)
override def add(q: ReactivePower): ApparentPowerAndHeat =
@@ -135,7 +135,7 @@ object Data {
final case class ApparentPowerAndHeat(
override val p: Power,
override val q: ReactivePower,
- override val qDot: Power
+ override val qDot: Power,
) extends PrimaryDataWithApparentPower[ApparentPowerAndHeat]
with Heat {
override def toApparentPower: ApparentPower =
@@ -161,7 +161,7 @@ object Data {
),
Kilowatts(
qDot.to(PowerSystemUnits.KILOWATT).getValue.doubleValue
- )
+ ),
)
)
case _ =>
@@ -181,7 +181,7 @@ object Data {
),
Kilovars(
q.to(PowerSystemUnits.KILOVAR).getValue.doubleValue
- )
+ ),
)
)
case _ =>
@@ -201,7 +201,7 @@ object Data {
),
Kilowatts(
qDot.to(PowerSystemUnits.KILOWATT).getValue.doubleValue
- )
+ ),
)
)
case _ =>
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala
index 3814da101c..b2c428242d 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgent.scala
@@ -6,30 +6,33 @@
package edu.ie3.simona.agent.participant.evcs
-import org.apache.pekko.actor.{ActorRef, Props}
import edu.ie3.datamodel.models.input.system.EvcsInput
-import edu.ie3.simona.agent.participant.{
- ParticipantAgent,
- ParticipantAgentFundamentals
-}
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- ZERO_POWER
+ ZERO_POWER,
}
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService
import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData
+import edu.ie3.simona.agent.participant.{
+ ParticipantAgent,
+ ParticipantAgentFundamentals,
+}
import edu.ie3.simona.agent.state.AgentState.Idle
import edu.ie3.simona.config.SimonaConfig.EvcsRuntimeConfig
-import edu.ie3.simona.model.participant.EvcsModel
-import edu.ie3.simona.model.participant.EvcsModel.EvcsRelevantData
+import edu.ie3.simona.model.participant.evcs.EvcsModel
+import edu.ie3.simona.model.participant.evcs.EvcsModel.{
+ EvcsRelevantData,
+ EvcsState,
+}
import edu.ie3.simona.ontology.messages.services.EvMessage.{
DepartingEvsRequest,
- EvFreeLotsRequest
+ EvFreeLotsRequest,
}
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.{ActorRef, Props}
import squants.Power
object EvcsAgent {
@@ -38,15 +41,15 @@ object EvcsAgent {
initStateData: ParticipantInitializeStateData[
EvcsInput,
EvcsRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- listener: Iterable[ActorRef]
+ listener: Iterable[ActorRef],
): Props =
Props(
new EvcsAgent(
scheduler,
initStateData,
- listener
+ listener,
)
)
@@ -60,16 +63,17 @@ class EvcsAgent(
initStateData: ParticipantInitializeStateData[
EvcsInput,
EvcsRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends ParticipantAgent[
ApparentPower,
EvcsRelevantData,
+ EvcsState,
ParticipantStateData[ApparentPower],
EvcsInput,
EvcsRuntimeConfig,
- EvcsModel
+ EvcsModel,
](scheduler, initStateData)
with EvcsAgentFundamentals {
override val alternativeResult: ApparentPower = ZERO_POWER
@@ -80,21 +84,24 @@ class EvcsAgent(
modelBaseStateData: ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
- ]
+ EvcsState,
+ EvcsModel,
+ ],
) =>
handleFreeLotsRequest(tick, modelBaseStateData)
stay()
+
case Event(
DepartingEvsRequest(tick, departingEvs),
modelBaseStateData: ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
- ]
+ EvcsState,
+ EvcsModel,
+ ],
) =>
val updatedStateData =
- handleDepartingEvsRequest(tick, modelBaseStateData, departingEvs)
+ handleDepartingEvsRequest(tick, departingEvs, modelBaseStateData)
stay() using updatedStateData
}
@@ -117,13 +124,13 @@ class EvcsAgent(
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ]
+ ],
): ApparentPower =
ParticipantAgentFundamentals.averageApparentPower(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala
index 1f3be0eff2..9bf4dde900 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/evcs/EvcsAgentFundamentals.scala
@@ -6,12 +6,10 @@
package edu.ie3.simona.agent.participant.evcs
-import org.apache.pekko.actor.{ActorRef, FSM}
-import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.models.input.system.EvcsInput
import edu.ie3.datamodel.models.result.system.{
EvcsResult,
- SystemParticipantResult
+ SystemParticipantResult,
}
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.ParticipantAgent.getAndCheckNodalVoltage
@@ -21,35 +19,46 @@ import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService
import edu.ie3.simona.agent.participant.evcs.EvcsAgent.neededServices
-import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
+import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
+ FlexControlledData,
+ ParticipantModelBaseStateData,
+}
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
import edu.ie3.simona.agent.participant.statedata.{
- DataCollectionStateData,
- ParticipantStateData
+ BaseStateData,
+ ParticipantStateData,
}
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
-import edu.ie3.simona.api.data.ev.model.EvModel
import edu.ie3.simona.config.SimonaConfig.EvcsRuntimeConfig
+import edu.ie3.simona.event.ResultEvent.ParticipantResultEvent
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.{
AgentInitializationException,
InconsistentStateException,
- InvalidRequestException
+ InvalidRequestException,
}
-import edu.ie3.simona.io.result.AccompaniedSimulationResult
-import edu.ie3.simona.model.participant.EvcsModel
-import edu.ie3.simona.model.participant.EvcsModel.EvcsRelevantData
-import edu.ie3.simona.ontology.messages.services.EvMessage.{
- ArrivingEvsData,
- DepartingEvsResponse,
- FreeLotsResponse
+import edu.ie3.simona.model.participant.FlexChangeIndicator
+import edu.ie3.simona.model.participant.evcs.EvcsModel
+import edu.ie3.simona.model.participant.evcs.EvcsModel.{
+ EvcsRelevantData,
+ EvcsState,
}
-import edu.ie3.simona.service.ev.ExtEvDataService.FALLBACK_EV_MOVEMENTS_STEM_DISTANCE
-import edu.ie3.util.quantities.PowerSystemUnits.PU
+import edu.ie3.simona.ontology.messages.PowerMessage.AssetPowerChangedMessage
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
+}
+import edu.ie3.simona.ontology.messages.services.EvMessage._
+import edu.ie3.simona.util.SimonaConstants
+import edu.ie3.simona.util.TickUtil.RichZonedDateTime
+import edu.ie3.util.quantities.PowerSystemUnits.{MEGAVAR, MEGAWATT, PU}
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
-import edu.ie3.util.scala.quantities.Kilovars
-import squants.energy.Kilowatts
+import edu.ie3.util.scala.quantities.Megavars
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
+import squants.energy.Megawatts
import squants.{Dimensionless, Each}
import java.time.ZonedDateTime
@@ -61,12 +70,12 @@ protected trait EvcsAgentFundamentals
extends ParticipantAgentFundamentals[
ApparentPower,
EvcsRelevantData,
+ EvcsState,
ParticipantStateData[ApparentPower],
EvcsInput,
EvcsRuntimeConfig,
- EvcsModel
- ]
- with LazyLogging {
+ EvcsModel,
+ ] {
this: EvcsAgent =>
override protected val pdClassTag: ClassTag[ApparentPower] =
classTag[ApparentPower]
@@ -79,12 +88,12 @@ protected trait EvcsAgentFundamentals
* @param modelConfig
* Configuration of the model
* @param services
- * Optional collection of services to register with
+ * Collection of services to register with
* @param simulationStartDate
* Real world time date time, when the simulation starts
* @param simulationEndDate
* Real world time date time, when the simulation ends
- * @param timeBin
+ * @param resolution
* Agents regular time bin it wants to be triggered e.g one hour
* @param requestVoltageDeviationThreshold
* Threshold, after which two nodal voltage magnitudes from participant
@@ -98,107 +107,63 @@ protected trait EvcsAgentFundamentals
override def determineModelBaseStateData(
inputModel: InputModelContainer[EvcsInput],
modelConfig: EvcsRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
- timeBin: Long,
+ resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
): ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
+ EvcsState,
+ EvcsModel,
] = {
/* Check for needed services */
- if (
- !services.exists(serviceDefinitions =>
- serviceDefinitions.map(_.getClass).containsSlice(neededServices)
- )
- )
+ if (!services.toSeq.map(_.getClass).containsSlice(neededServices))
throw new AgentInitializationException(
s"EvcsAgent cannot be initialized without an ev data service!"
)
- baseStateDataForModelCalculation(
- inputModel,
- modelConfig,
- services,
- simulationStartDate,
- simulationEndDate,
- timeBin,
- requestVoltageDeviationThreshold,
- outputConfig
- )
- }
-
- /** Determine needed base state data for model calculation simulation mode.
- *
- * @param inputModel
- * Input model
- * @param modelConfig
- * Configuration for the model
- * @param servicesOpt
- * [[Option]] on a vector of [[SecondaryDataService]] s
- * @param simulationStartDate
- * Real world time date time, when the simulation starts
- * @param simulationEndDate
- * Real world time date time, when the simulation ends
- * @param timeBin
- * Agents regular time bin it wants to be triggered e.g one hour
- * @param requestVoltageDeviationThreshold
- * Threshold, after which two nodal voltage magnitudes from participant
- * power requests for the same tick are considered to be different
- * @param outputConfig
- * Config of the output behaviour for simulation results
- * @return
- * Needed base state data for model calculation
- */
- def baseStateDataForModelCalculation(
- inputModel: InputModelContainer[EvcsInput],
- modelConfig: EvcsRuntimeConfig,
- servicesOpt: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
- simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime,
- timeBin: Long,
- requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ParticipantModelBaseStateData[
- ApparentPower,
- EvcsRelevantData,
- EvcsModel
- ] = {
-
/* Build the calculation model */
val model =
buildModel(
inputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
- ParticipantModelBaseStateData[ApparentPower, EvcsRelevantData, EvcsModel](
+ ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ](
simulationStartDate,
simulationEndDate,
model,
- servicesOpt,
+ services,
outputConfig,
SortedSet.empty, // Additional activation of the evcs agent is not needed
Map.empty,
requestVoltageDeviationThreshold,
ValueStore.forVoltage(
- timeBin * 10,
+ resolution,
Each(
inputModel.electricalInputModel.getNode
.getvTarget()
.to(PU)
.getValue
.doubleValue
- )
+ ),
),
- ValueStore.forResult(timeBin, 10),
- ValueStore(timeBin * 10),
- ValueStore(timeBin * 10)
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ maybeEmAgent.map(FlexControlledData(_, self.toTyped[FlexRequest])),
)
}
@@ -206,24 +171,111 @@ protected trait EvcsAgentFundamentals
inputModel: InputModelContainer[EvcsInput],
modelConfig: EvcsRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): EvcsModel = EvcsModel(
inputModel.electricalInputModel,
modelConfig.scaling,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
+ modelConfig.chargingStrategy,
+ modelConfig.lowestEvSoc,
)
+ override protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ]
+ ): EvcsState =
+ EvcsState(
+ Seq.empty,
+ Map.empty,
+ SimonaConstants.FIRST_TICK_IN_SIMULATION,
+ )
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ],
+ tick: Long,
+ ): EvcsRelevantData = {
+ // always only take arrivals for the current tick
+ // or empty sequence if none arrived
+ val arrivingEvs = baseStateData.receivedSecondaryDataStore
+ .getOrElse(tick, Map.empty)
+ .collectFirst {
+ // filter secondary data for arriving EVs data
+ case (_, arrivingEvsData: ArrivingEvsData) =>
+ arrivingEvsData.arrivals
+ }
+ .getOrElse(Seq.empty)
+
+ EvcsRelevantData(tick, arrivingEvs)
+ }
+
+ /** Handle an active power change by flex control.
+ *
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ],
+ data: EvcsRelevantData,
+ lastState: EvcsState,
+ setPower: squants.Power,
+ ): (EvcsState, ApparentPower, FlexChangeIndicator) = {
+ /* Calculate the power */
+ val voltage = getAndCheckNodalVoltage(baseStateData, tick)
+
+ val reactivePower = baseStateData.model.calculateReactivePower(
+ setPower,
+ voltage,
+ )
+ val result = ApparentPower(setPower, reactivePower)
+
+ /* Handle the request within the model */
+ val (updatedState, flexChangeIndicator) =
+ baseStateData.model.handleControlledPowerChange(data, lastState, setPower)
+ (updatedState, result, flexChangeIndicator)
+ }
+
/** Partial function, that is able to transfer
* [[ParticipantModelBaseStateData]] (holding the actual calculation model)
* into a pair of active and reactive power
*/
override val calculateModelPowerFunc: (
Long,
- ParticipantModelBaseStateData[ApparentPower, EvcsRelevantData, EvcsModel],
- Dimensionless
+ ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ],
+ EvcsState,
+ Dimensionless,
) => ApparentPower =
- (_, _, _) =>
+ (_, _, _, _) =>
throw new InvalidRequestException(
"Evcs model cannot be run without secondary data."
)
@@ -238,9 +290,11 @@ protected trait EvcsAgentFundamentals
* [[edu.ie3.simona.ontology.messages.SchedulerMessage.Completion]] to
* scheduler and using update result values.
*
- * @param collectionStateData
- * State data with collected, comprehensive secondary data.
- * @param currentTick
+ * @param baseStateData
+ * The base state data with collected secondary data
+ * @param lastModelState
+ * Last model state
+ * @param tick
* Tick, the trigger belongs to
* @param scheduler
* [[ActorRef]] to the scheduler in the simulation
@@ -248,44 +302,40 @@ protected trait EvcsAgentFundamentals
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPower],
- currentTick: Long,
- scheduler: ActorRef
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ],
+ lastModelState: EvcsState,
+ tick: Long,
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] = {
-
- collectionStateData.baseStateData match {
- case modelBaseStateData: ParticipantModelBaseStateData[
- ApparentPower,
- EvcsRelevantData,
- EvcsModel
- ] =>
- /* extract EV data from secondary data, which should have been requested and received before */
- collectionStateData.data.values
- .collectFirst {
- // filter secondary data for arriving EVs data
- case Some(arrivingEvs: ArrivingEvsData) =>
- handleArrivingEvsAndGoIdle(
- currentTick,
- scheduler,
- modelBaseStateData,
- arrivingEvs.arrivals
- )
- }
- .getOrElse(
- throw new InconsistentStateException(
- s"The model ${modelBaseStateData.model} was not provided with needed EV data."
- )
+ /* extract EV data from secondary data, which should have been requested and received before */
+ baseStateData.receivedSecondaryDataStore
+ .getOrElse(tick, Map.empty)
+ .values
+ .collectFirst {
+ // filter secondary data for arriving EVs data
+ case _: ArrivingEvsData =>
+ handleArrivingEvsAndGoIdle(
+ tick,
+ scheduler,
+ baseStateData,
)
-
- case _ =>
+ }
+ .getOrElse(
throw new InconsistentStateException(
- "Cannot find a model for model calculation."
+ s"The model ${baseStateData.model} was not provided with needed EV data."
)
- }
+ )
+
}
/** Returns the number of free parking lots based on the last available state
* data.
+ *
* @param tick
* The tick that free lots have been requested for
* @param modelBaseStateData
@@ -294,23 +344,22 @@ protected trait EvcsAgentFundamentals
protected def handleFreeLotsRequest(
tick: Long,
modelBaseStateData: ParticipantModelBaseStateData[
- _ <: ApparentPower,
- _,
- _
- ]
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ],
): Unit = {
val evServiceRef = getService[ActorEvMovementsService](
modelBaseStateData.services
)
- val (_, lastEvs) =
- getTickIntervalAndLastEvs(tick, modelBaseStateData)
-
- val evcsModel = getEvcsModel(modelBaseStateData)
+ val lastState =
+ getLastOrInitialStateData(modelBaseStateData, tick - 1)
evServiceRef ! FreeLotsResponse(
- evcsModel.uuid,
- evcsModel.chargingPoints - lastEvs.size
+ modelBaseStateData.model.uuid,
+ modelBaseStateData.model.chargingPoints - lastState.evs.size,
)
}
@@ -320,95 +369,107 @@ protected trait EvcsAgentFundamentals
*
* @param tick
* The current simulation tick
- * @param modelBaseStateData
+ * @param baseStateData
* The current Base state data
* @param requestedDepartingEvs
* The UUIDs of EVs that are requested to be returned
*/
protected def handleDepartingEvsRequest(
tick: Long,
- modelBaseStateData: ParticipantModelBaseStateData[
+ requestedDepartingEvs: Seq[UUID],
+ baseStateData: ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
+ EvcsState,
+ EvcsModel,
],
- requestedDepartingEvs: Seq[UUID]
): ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
+ EvcsState,
+ EvcsModel,
] = {
-
val evServiceRef = getService[ActorEvMovementsService](
- modelBaseStateData.services
+ baseStateData.services
)
- // retrieve the last updated set of parked EVs
- val (tickInterval, lastEvs) =
- getTickIntervalAndLastEvs(tick, modelBaseStateData)
+ // we don't take the state at the current tick, since
+ // that one cannot contain the departing EVs anymore
+ val lastState = baseStateData.stateDataStore
+ .last(tick - 1)
+ .map { case (_, lastState) =>
+ lastState
+ }
+ .getOrElse(
+ throw new InvalidRequestException(
+ s"Cannot return departing EVs from EVCS ${baseStateData.modelUuid} since we have no parked EVs"
+ )
+ )
- validateDepartures(lastEvs, requestedDepartingEvs)
+ baseStateData.model.validateDepartures(lastState.evs, requestedDepartingEvs)
- val evcsModel = getEvcsModel(modelBaseStateData)
+ val updatedEvs = baseStateData.model.applySchedule(
+ lastState,
+ tick,
+ )
- val voltage =
- getAndCheckNodalVoltage(modelBaseStateData, tick)
+ val (departingEvs, stayingEvs) = updatedEvs.partition { ev =>
+ requestedDepartingEvs.contains(ev.uuid)
+ }
- // calculate power with evs that have been parked up until now
- val relevantData =
- EvcsRelevantData(
- tickInterval,
- lastEvs
+ // send back departing EVs
+ if (requestedDepartingEvs.nonEmpty) {
+ evServiceRef ! DepartingEvsResponse(
+ baseStateData.modelUuid,
+ departingEvs,
)
+ }
- val (result, evModelsCalculated) =
- evcsModel.calculatePowerAndEvSoc(
+ /* Calculate evcs power for interval since last update, save for updating value store, and inform listeners */
+ val updatedResultValueStore =
+ determineResultsAnnounceUpdateValueStore(
+ lastState,
tick,
- voltage,
- relevantData
+ baseStateData,
)
- val (departingEvs, stayingEvs) =
- evModelsCalculated.partition { ev =>
- // EV has been parked up until now and is now departing
- requestedDepartingEvs.contains(ev.getUuid)
- }
+ val stayingSchedules =
+ lastState.schedule
+ .filterNot(requestedDepartingEvs.contains)
+ .view
+ .mapValues {
+ // Remove schedules that ended before or at current tick.
+ // Schedule entries ending at current tick do not have any
+ // impact on the schedule from the current tick on
+ _.filter(_.tickStop > tick)
+ }
+ .toMap
- val updatedRelevantData = relevantData.copy(
- currentEvs = stayingEvs
- )
+ val newState = EvcsState(stayingEvs, stayingSchedules, tick)
- if (departingEvs.nonEmpty) {
- evServiceRef ! DepartingEvsResponse(
- evcsModel.uuid,
- departingEvs
- )
- }
-
- /* Update the base state data */
- updateValueStoresInformListeners(
- modelBaseStateData,
- tick,
- AccompaniedSimulationResult(result),
- updatedRelevantData
+ baseStateData.copy(
+ stateDataStore = ValueStore.updateValueStore(
+ baseStateData.stateDataStore,
+ tick,
+ newState,
+ ),
+ resultValueStore = updatedResultValueStore,
)
}
- /** Handles EV arrivals as part of ExtEvDataService secondary data. After
- * adding the arriving EVs to the set of staying evs, resulting charging
- * power is calculated. A completion message is sent to scheduler without
- * scheduling new activations.
+ /** Handles data message that contains information on arriving EVs. Updates
+ * already parked EVs (if applicable) and adds new arrivals. Also calculates
+ * new schedules for all staying and arriving EVs. Sends completion message
+ * to scheduler without scheduling new activations.
*
* @param tick
- * The current tick that has been triggered
+ * The current tick that data has arrived for
* @param scheduler
* The scheduler ref
* @param modelBaseStateData
* The state data
- * @param arrivingEvs
- * The movement data on arrivingEvs that has been received
* @return
- * [[Idle]] with updated result values
+ * [[Idle]] with updated relevant data store
*/
private def handleArrivingEvsAndGoIdle(
tick: Long,
@@ -416,170 +477,304 @@ protected trait EvcsAgentFundamentals
modelBaseStateData: ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
+ EvcsState,
+ EvcsModel,
],
- arrivingEvs: Seq[EvModel]
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] = {
- val evcsModel = getEvcsModel(modelBaseStateData)
+ val relevantData =
+ createCalcRelevantData(modelBaseStateData, tick)
- // retrieve the last updated set of parked EVs, which could stem from
- // the current tick if there were departures for this tick as well
- val (tickInterval, lastEvs) =
- getTickIntervalAndLastEvs(tick, modelBaseStateData)
+ val lastState = getLastOrInitialStateData(modelBaseStateData, tick)
- validateArrivals(lastEvs, arrivingEvs, evcsModel.chargingPoints)
+ val currentEvs = modelBaseStateData.model.determineCurrentEvs(
+ relevantData,
+ lastState,
+ )
- val relevantData =
- EvcsRelevantData(
- tickInterval,
- lastEvs
- )
+ // if new EVs arrived, a new scheduling must be calculated.
+ val newSchedule = modelBaseStateData.model.calculateNewScheduling(
+ relevantData,
+ currentEvs,
+ )
- val updatedStateData =
- if (tickInterval > 0) {
- // if we haven't had any departing EVs for this tick,
- // this also means that we have not caught up with
- // calculating the current SOC
+ // create new current state
+ val newState = EvcsState(currentEvs, newSchedule, tick)
- val voltage =
- getAndCheckNodalVoltage(modelBaseStateData, tick)
+ val updatedStateDataStore = ValueStore.updateValueStore(
+ modelBaseStateData.stateDataStore,
+ tick,
+ newState,
+ )
- // calculate power with evs that have been parked up until now
- val (result, evModelsCalculated) =
- evcsModel.calculatePowerAndEvSoc(
- tick,
- voltage,
- relevantData
- )
+ /* Update the base state data with the updated result value store and relevant data store */
+ val updatedBaseStateData = modelBaseStateData.copy(
+ stateDataStore = updatedStateDataStore
+ )
- val updatedRelevantData = relevantData.copy(
- currentEvs = evModelsCalculated ++ arrivingEvs
- )
+ // We're only here if we're not flex-controlled, thus sending a Completion is always right
+ goToIdleReplyCompletionAndScheduleTriggerForNextAction(
+ updatedBaseStateData,
+ scheduler,
+ )
+ }
- updateValueStoresInformListeners(
- modelBaseStateData,
- tick,
- AccompaniedSimulationResult(result),
- updatedRelevantData
+ /** Determine a reply on a
+ * [[edu.ie3.simona.ontology.messages.PowerMessage.RequestAssetPowerMessage]]
+ * by looking up the detailed simulation results, averaging them and
+ * returning the equivalent state transition.
+ *
+ * @param requestTick
+ * The tick, the request belongs to
+ * @param baseStateData
+ * Base state data
+ * @param mostRecentRequest
+ * The request reply, that most recently has been sent
+ * @param nodalVoltage
+ * Current nodal voltage
+ * @param updatedVoltageValueStore
+ * Value store with updated nodal voltages
+ * @param alternativeResult
+ * Alternative result to use, if no reasonable result can be obtained
+ * @return
+ * Matching state transition
+ */
+ override def determineReply(
+ requestTick: Long,
+ baseStateData: BaseStateData[ApparentPower],
+ mostRecentRequest: Option[(Long, ApparentPower)],
+ nodalVoltage: squants.Dimensionless,
+ updatedVoltageValueStore: ValueStore[squants.Dimensionless],
+ alternativeResult: ApparentPower,
+ ): FSM.State[AgentState, ParticipantStateData[ApparentPower]] = {
+ /* No fast reply possible --> Some calculations have to be made */
+ mostRecentRequest match {
+ case Some((lastRequestTick, _)) if lastRequestTick > requestTick =>
+ throw new InvalidRequestException(
+ "Got a request for a tick, whereas a later tick already has been answered. This behaviour is not yet specified!"
)
- } else {
- // if some EVs were departing at the current tick,
- // we're already up-to-date in that regard
+ case Some((lastRequestTick, lastResult))
+ if lastRequestTick == requestTick =>
+ /* Repetitive request for the same tick, but with different voltage */
+ baseStateData match {
+ case modelBaseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ] =>
+ /* Active power is yet calculated, but reactive power needs update */
+ val nextReactivePower = modelBaseStateData.model
+ .calculateReactivePower(lastResult.p, nodalVoltage)
+
+ /* Determine the reply, based new circumstances */
+ val updatedRequestValueStore =
+ ValueStore.updateValueStore(
+ baseStateData.requestValueStore,
+ requestTick,
+ lastResult.withReactivePower(nextReactivePower),
+ )
- val updatedRelevantData = relevantData.copy(
- currentEvs = lastEvs ++ arrivingEvs
- )
+ val nextStateData =
+ modelBaseStateData.copy(
+ requestValueStore = updatedRequestValueStore,
+ voltageValueStore = updatedVoltageValueStore,
+ )
- val updatedRelevantDataStore =
- ValueStore.updateValueStore(
- modelBaseStateData.calcRelevantDateStore,
- tick,
- updatedRelevantData
- )
+ stay() using nextStateData replying AssetPowerChangedMessage(
+ lastResult.p,
+ nextReactivePower,
+ )
+ case unexpectedStateData =>
+ throw new IllegalStateException(
+ s"The request reply should not be re-calculated for state data '$unexpectedStateData'"
+ )
+ }
- modelBaseStateData.copy(
- calcRelevantDateStore = updatedRelevantDataStore
- )
- }
+ case _ =>
+ /* There hasn't been a request for this tick, yet. Check, if there are simulation results. If at least one
+ * is apparent, average them and answer the request. If no simulation results is apparent at all, reply with
+ * zero power, although this case should have been handled earlier */
+
+ /* ADDED: Update base state data before answering the power request to include the scheduling up to this tick.
+ * Also, save the new voltage information for the calc relevant data store.
+ */
+ val updatedBaseStateData =
+ baseStateData match {
+ case modelBaseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ] =>
+ val lastState =
+ getLastOrInitialStateData(modelBaseStateData, requestTick)
+
+ val updatedResultValueStore =
+ determineResultsAnnounceUpdateValueStore(
+ lastState,
+ requestTick,
+ modelBaseStateData,
+ )
+
+ modelBaseStateData.copy(
+ resultValueStore = updatedResultValueStore
+ )
- goToIdleReplyCompletionAndScheduleTriggerForNextAction(
- updatedStateData,
- scheduler
- )
+ case unexpectedStateData =>
+ throw new IllegalStateException(
+ s"Unexpected state data '$unexpectedStateData'"
+ )
+ }
+
+ getRelevantResultData(
+ requestTick,
+ updatedBaseStateData.resultValueStore,
+ updatedBaseStateData.requestValueStore,
+ ) match {
+ case Some(relevantData) =>
+ /* There is at least one relevant simulation result apparent, which might also be the most recent one
+ * before the last request. But this is still the most recent one considered being valid. */
+ averagePowerAndStay(
+ updatedBaseStateData,
+ relevantData,
+ requestTick,
+ nodalVoltage,
+ updatedVoltageValueStore,
+ alternativeResult,
+ )
+ case None =>
+ /* There is no simulation result at all. Reply with zero power */
+ stayWithUpdatedRequestValueStore(
+ updatedBaseStateData,
+ alternativeResult,
+ requestTick,
+ updatedVoltageValueStore,
+ )
+ }
+ }
}
- /** Update the result and calc relevant data value stores and inform all
- * registered listeners
+ /** Speciality with EVCS: result are always calculated up until the current
+ * tick. Thus, the result received as a parameter is discarded.
*
* @param baseStateData
- * The base state data of the collection state
- * @param tick
- * The current tick
+ * The base state data
* @param result
- * Result of simulation
- * @param relevantData
- * Data, that have been relevant to this calculation
+ * Is ignored here, result up until this tick are calculated
+ * @param currentTick
+ * the current tick
* @return
- * Desired state change
+ * updated base state data
*/
- private final def updateValueStoresInformListeners(
+ override def handleCalculatedResult(
baseStateData: ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
+ EvcsState,
+ EvcsModel,
],
- tick: Long,
- result: AccompaniedSimulationResult[ApparentPower],
- relevantData: EvcsRelevantData
+ result: ApparentPower,
+ currentTick: Long,
): ParticipantModelBaseStateData[
ApparentPower,
EvcsRelevantData,
- EvcsModel
+ EvcsState,
+ EvcsModel,
] = {
- /* Update the value stores */
- val updatedValueStore =
- ValueStore.updateValueStore(
- baseStateData.resultValueStore,
- tick,
- result.primaryData
- )
- val updatedRelevantDataStore =
- ValueStore.updateValueStore(
- baseStateData.calcRelevantDateStore,
- tick,
- relevantData
- )
- /* Inform the listeners about new result */
- announceSimulationResult(
- baseStateData,
- tick,
- result
- )(baseStateData.outputConfig)
+ // calculate results from last schedule
+ baseStateData.stateDataStore
+ .last(currentTick - 1)
+ .map { case (lastTick, lastState) =>
+ baseStateData.resultValueStore.get(lastTick) match {
+ case Some(_) =>
+ // We already have a result for this tick, likely
+ // because EVs already departed at this tick.
+ // Thus, skip recalculating and sending out results.
+ baseStateData
+ case None =>
+ val updatedResultValueStore =
+ determineResultsAnnounceUpdateValueStore(
+ lastState,
+ currentTick,
+ baseStateData,
+ )
+
+ baseStateData.copy(
+ resultValueStore = updatedResultValueStore
+ ): ParticipantModelBaseStateData[
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ]
+ }
+ }
+ .getOrElse(baseStateData)
- /* Update the base state data */
- baseStateData.copy(
- resultValueStore = updatedValueStore,
- calcRelevantDateStore = updatedRelevantDataStore
- )
}
- private def getTickIntervalAndLastEvs(
+ /** Determine evcs results, announce them to listeners and update the result
+ * value store.
+ *
+ * @param lastState
+ * The state (including schedule) to calculate results for
+ * @param currentTick
+ * The tick up to which results should be calculated for
+ * @param modelBaseStateData
+ * Model base state data
+ * @return
+ * The updated result value store
+ */
+ private def determineResultsAnnounceUpdateValueStore(
+ lastState: EvcsState,
currentTick: Long,
modelBaseStateData: ParticipantModelBaseStateData[
- _ <: ApparentPower,
- _,
- _
- ]
- ): (Long, Set[EvModel]) = {
- modelBaseStateData.calcRelevantDateStore
- .last(currentTick) match {
- case Some((tick, EvcsRelevantData(_, evs))) =>
- (currentTick - tick, evs)
- case _ =>
- /* At the first tick, we are not able to determine the tick interval from last tick
- * (since there is none). Then we use a fall back ev stem distance.
- * As no evs are charging then, the tick interval should be ignored anyway. */
- (FALLBACK_EV_MOVEMENTS_STEM_DISTANCE, Set.empty[EvModel])
+ ApparentPower,
+ EvcsRelevantData,
+ EvcsState,
+ EvcsModel,
+ ],
+ ): ValueStore[ApparentPower] = {
+
+ val voltage = modelBaseStateData.voltageValueStore
+ .last(currentTick)
+ .map { case (_, voltage) =>
+ voltage
+ }
+ .getOrElse(Each(1d))
+
+ val (evResults, evcsResults) = modelBaseStateData.model.createResults(
+ lastState,
+ currentTick,
+ voltage,
+ )
+
+ // send out EV results
+ evResults.foreach { result =>
+ listener.foreach(_ ! ParticipantResultEvent(result))
}
- }
- private def getEvcsModel(
- modelBaseStateData: ParticipantModelBaseStateData[
- _ <: ApparentPower,
- _,
- _
- ]
- ): EvcsModel =
- modelBaseStateData.model match {
- case model: EvcsModel =>
- model
- case unsupportedModel =>
- throw new InconsistentStateException(
- s"Wrong model: $unsupportedModel!"
+ evcsResults.foldLeft(modelBaseStateData.resultValueStore) {
+ case (resultValueStore, result) =>
+ /* Inform the listeners about new result */
+ if (modelBaseStateData.outputConfig.simulationResultInfo)
+ notifyListener(
+ ParticipantResultEvent(result)
+ )
+
+ /* Update resultValueStore with result */
+ ValueStore.updateValueStore(
+ resultValueStore,
+ result.getTime.toTick(modelBaseStateData.startDate),
+ ApparentPower(
+ Megawatts(result.getP.to(MEGAWATT).getValue.doubleValue),
+ Megavars(result.getQ.to(MEGAVAR).getValue.doubleValue),
+ ),
)
}
+ }
/** Determines the correct result.
*
@@ -595,67 +790,36 @@ protected trait EvcsAgentFundamentals
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPower
+ result: ApparentPower,
): SystemParticipantResult =
new EvcsResult(
dateTime,
uuid,
result.p.toMegawatts.asMegaWatt,
- result.q.toMegavars.asMegaVar
+ result.q.toMegavars.asMegaVar,
)
- /** Checks whether requested departing EVs are consistent with currently
- * connected EVs. Only logs warnings, does not throw exceptions.
- *
- * @param lastEvs
- * EVs of the last tick
- * @param departures
- * Departing EVs at the current tick
- */
- protected def validateDepartures(
- lastEvs: Set[EvModel],
- departures: Seq[UUID]
- ): Unit = {
- departures.foreach { ev =>
- if (!lastEvs.exists(_.getUuid == ev))
- logger.warn(
- s"EV $ev should depart from this station (according to external simulation), but has not been parked here."
- )
- }
- }
-
- /** Checks whether provided arriving EVs are consistent with charging station
- * specifications and currently connected EVs. Only logs warnings, does not
- * throw exceptions.
+ /** Update the last known model state with the given external, relevant data
*
- * @param lastEvs
- * EVs of the last tick
- * @param arrivals
- * Arriving EVs at the current tick
- * @param chargingPoints
- * max number of charging points available at this CS
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
*/
- protected def validateArrivals(
- lastEvs: Set[EvModel],
- arrivals: Seq[EvModel],
- chargingPoints: Int
- ): Unit = {
-
- arrivals.foreach { ev =>
- if (lastEvs.exists(_.getUuid == ev.getUuid))
- logger.warn(
- s"EV ${ev.getId} should arrive at this station (according to external simulation), but is already parked here."
- )
- }
-
- val newCount = lastEvs.size +
- arrivals.count { ev =>
- !lastEvs.exists(_.getUuid == ev.getUuid)
- }
-
- if (newCount > chargingPoints)
- logger.warn(
- "More EVs are parking at this station than physically possible."
- )
- }
+ override protected def updateState(
+ tick: Long,
+ modelState: EvcsState,
+ calcRelevantData: EvcsRelevantData,
+ nodalVoltage: squants.Dimensionless,
+ model: EvcsModel,
+ ): EvcsState = modelState
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgent.scala
index a5c90fe112..9b5b681f90 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgent.scala
@@ -6,7 +6,6 @@
package edu.ie3.simona.agent.participant.fixedfeedin
-import org.apache.pekko.actor.{ActorRef, Props}
import edu.ie3.datamodel.models.input.system.FixedFeedInInput
import edu.ie3.simona.agent.participant.ParticipantAgent
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
@@ -15,6 +14,8 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.Participa
import edu.ie3.simona.config.SimonaConfig.FixedFeedInRuntimeConfig
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
import edu.ie3.simona.model.participant.FixedFeedInModel
+import edu.ie3.simona.model.participant.ModelState.ConstantState
+import org.apache.pekko.actor.{ActorRef, Props}
object FixedFeedInAgent {
def props(
@@ -22,9 +23,9 @@ object FixedFeedInAgent {
initStateData: ParticipantInitializeStateData[
FixedFeedInInput,
FixedFeedInRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- listener: Iterable[ActorRef]
+ listener: Iterable[ActorRef],
): Props =
Props(new FixedFeedInAgent(scheduler, initStateData, listener))
}
@@ -41,16 +42,17 @@ class FixedFeedInAgent(
initStateData: ParticipantInitializeStateData[
FixedFeedInInput,
FixedFeedInRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends ParticipantAgent[
ApparentPower,
FixedRelevantData.type,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
FixedFeedInInput,
FixedFeedInRuntimeConfig,
- FixedFeedInModel
+ FixedFeedInModel,
](scheduler, initStateData)
with FixedFeedInAgentFundamentals {
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgentFundamentals.scala
index 3f5717d8c6..cbaabae42e 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/fixedfeedin/FixedFeedInAgentFundamentals.scala
@@ -6,41 +6,54 @@
package edu.ie3.simona.agent.participant.fixedfeedin
-import org.apache.pekko.actor.{ActorRef, FSM}
import edu.ie3.datamodel.models.input.system.FixedFeedInInput
import edu.ie3.datamodel.models.result.system.{
FixedFeedInResult,
- SystemParticipantResult
+ SystemParticipantResult,
}
import edu.ie3.simona.agent.ValueStore
+import edu.ie3.simona.agent.participant.ParticipantAgent.getAndCheckNodalVoltage
import edu.ie3.simona.agent.participant.ParticipantAgentFundamentals
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- ZERO_POWER
+ ZERO_POWER,
}
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
-import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
-import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
-import edu.ie3.simona.agent.participant.statedata.{
- DataCollectionStateData,
- ParticipantStateData
+import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
+ FlexControlledData,
+ ParticipantModelBaseStateData,
}
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
import edu.ie3.simona.config.SimonaConfig.FixedFeedInRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.{
InconsistentStateException,
- InvalidRequestException
+ InvalidRequestException,
}
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
-import edu.ie3.simona.model.participant.FixedFeedInModel
+import edu.ie3.simona.model.participant.ModelState.ConstantState
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ FixedFeedInModel,
+ FlexChangeIndicator,
+ ModelState,
+}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
+}
import edu.ie3.simona.util.SimonaConstants
import edu.ie3.simona.util.TickUtil.RichZonedDateTime
import edu.ie3.util.quantities.PowerSystemUnits.PU
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
import squants.{Dimensionless, Each, Power}
import java.time.ZonedDateTime
@@ -52,10 +65,11 @@ protected trait FixedFeedInAgentFundamentals
extends ParticipantAgentFundamentals[
ApparentPower,
FixedRelevantData.type,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
FixedFeedInInput,
FixedFeedInRuntimeConfig,
- FixedFeedInModel
+ FixedFeedInModel,
] {
this: FixedFeedInAgent =>
override protected val pdClassTag: ClassTag[ApparentPower] =
@@ -70,7 +84,7 @@ protected trait FixedFeedInAgentFundamentals
* @param modelConfig
* Configuration of the model
* @param services
- * Optional collection of services to register with
+ * Collection of services to register with
* @param simulationStartDate
* Real world time date time, when the simulation starts
* @param simulationEndDate
@@ -89,16 +103,18 @@ protected trait FixedFeedInAgentFundamentals
override def determineModelBaseStateData(
inputModel: InputModelContainer[FixedFeedInInput],
modelConfig: FixedFeedInRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
): ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- FixedFeedInModel
+ ConstantState.type,
+ FixedFeedInModel,
] = {
/* Build the calculation model */
val model =
@@ -106,7 +122,7 @@ protected trait FixedFeedInAgentFundamentals
inputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
/* Go and collect all ticks, in which new data will be available. Also register for
@@ -123,13 +139,14 @@ protected trait FixedFeedInAgentFundamentals
SortedSet[Long](
SimonaConstants.FIRST_TICK_IN_SIMULATION,
model.operationInterval.start,
- model.operationInterval.end
+ model.operationInterval.end,
).filterNot(_ == lastTickInSimulation)
ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- FixedFeedInModel
+ ConstantState.type,
+ FixedFeedInModel,
](
simulationStartDate,
simulationEndDate,
@@ -147,11 +164,13 @@ protected trait FixedFeedInAgentFundamentals
.to(PU)
.getValue
.doubleValue
- )
+ ),
),
- ValueStore.forResult(resolution, 2),
ValueStore(resolution),
- ValueStore(resolution)
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ maybeEmAgent.map(FlexControlledData(_, self.toTyped[FlexRequest])),
)
}
@@ -159,34 +178,104 @@ protected trait FixedFeedInAgentFundamentals
inputModel: InputModelContainer[FixedFeedInInput],
modelConfig: FixedFeedInRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): FixedFeedInModel = FixedFeedInModel(
inputModel.electricalInputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
+ override protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ FixedFeedInModel,
+ ]
+ ): ModelState.ConstantState.type = ConstantState
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ FixedFeedInModel,
+ ],
+ tick: Long,
+ ): FixedRelevantData.type =
+ FixedRelevantData
+
+ /** Handle an active power change by flex control.
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ FixedFeedInModel,
+ ],
+ data: FixedRelevantData.type,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, ApparentPower, FlexChangeIndicator) = {
+ /* Calculate result */
+ val voltage = getAndCheckNodalVoltage(baseStateData, tick)
+
+ val reactivePower = baseStateData.model.calculateReactivePower(
+ setPower,
+ voltage,
+ )
+ val result = ApparentPower(setPower, reactivePower)
+
+ /* Handle the request within the model */
+ val (updatedState, flexChangeIndicator) =
+ baseStateData.model.handleControlledPowerChange(data, lastState, setPower)
+ (updatedState, result, flexChangeIndicator)
+ }
+
override val calculateModelPowerFunc: (
Long,
ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- FixedFeedInModel
+ ConstantState.type,
+ FixedFeedInModel,
],
- Dimensionless
+ ConstantState.type,
+ Dimensionless,
) => ApparentPower = (
currentTick: Long,
baseStateData: ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- FixedFeedInModel
+ ConstantState.type,
+ FixedFeedInModel,
],
- voltage: Dimensionless
+ state: ConstantState.type,
+ voltage: Dimensionless,
) =>
baseStateData.model match {
case fixedModel: FixedFeedInModel =>
- fixedModel.calculatePower(currentTick, voltage, FixedRelevantData)
+ fixedModel.calculatePower(
+ currentTick,
+ voltage,
+ state,
+ FixedRelevantData,
+ )
case unsupportedModel =>
throw new InconsistentStateException(
s"The model $unsupportedModel is not supported!"
@@ -203,8 +292,10 @@ protected trait FixedFeedInAgentFundamentals
* [[edu.ie3.simona.ontology.messages.SchedulerMessage.Completion]] to
* scheduler and using update result values.
*
- * @param collectionStateData
- * State data with collected, comprehensive secondary data.
+ * @param baseStateData
+ * The base state data with collected secondary data
+ * @param lastModelState
+ * Optional last model state
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -213,9 +304,15 @@ protected trait FixedFeedInAgentFundamentals
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPower],
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ FixedFeedInModel,
+ ],
+ lastModelState: ConstantState.type,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] =
throw new InvalidRequestException(
"Request to calculate power with secondary data cannot be processed in a fixed feed in agent."
@@ -240,14 +337,14 @@ protected trait FixedFeedInAgentFundamentals
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ] = None
+ ] = None,
): ApparentPower =
ParticipantAgentFundamentals.averageApparentPower(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
/** Determines the correct result.
@@ -264,12 +361,36 @@ protected trait FixedFeedInAgentFundamentals
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPower
+ result: ApparentPower,
): SystemParticipantResult =
new FixedFeedInResult(
dateTime,
uuid,
result.p.toMegawatts.asMegaWatt,
- result.q.toMegavars.asMegaVar
+ result.q.toMegavars.asMegaVar,
)
+
+ /** Update the last known model state with the given external, relevant data
+ *
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
+ */
+ override protected def updateState(
+ tick: Long,
+ modelState: ModelState.ConstantState.type,
+ calcRelevantData: CalcRelevantData.FixedRelevantData.type,
+ nodalVoltage: squants.Dimensionless,
+ model: FixedFeedInModel,
+ ): ModelState.ConstantState.type = modelState
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgent.scala
index 758711c3bd..0be0c2877b 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgent.scala
@@ -6,7 +6,6 @@
package edu.ie3.simona.agent.participant.hp
-import org.apache.pekko.actor.{ActorRef, Props}
import edu.ie3.datamodel.models.input.system.HpInput
import edu.ie3.simona.agent.participant.ParticipantAgent
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPowerAndHeat
@@ -16,7 +15,8 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData
import edu.ie3.simona.config.SimonaConfig.HpRuntimeConfig
import edu.ie3.simona.model.participant.HpModel
-import edu.ie3.simona.model.participant.HpModel.HpRelevantData
+import edu.ie3.simona.model.participant.HpModel.{HpRelevantData, HpState}
+import org.apache.pekko.actor.{ActorRef, Props}
object HpAgent {
def props(
@@ -24,15 +24,15 @@ object HpAgent {
initStateData: ParticipantInitializeStateData[
HpInput,
HpRuntimeConfig,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
],
- listener: Iterable[ActorRef]
+ listener: Iterable[ActorRef],
): Props =
Props(
new HpAgent(
scheduler,
initStateData,
- listener
+ listener,
)
)
@@ -46,18 +46,19 @@ class HpAgent(
initStateData: ParticipantInitializeStateData[
HpInput,
HpRuntimeConfig,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends ParticipantAgent[
ApparentPowerAndHeat,
HpRelevantData,
+ HpState,
ParticipantStateData[
ApparentPowerAndHeat
],
HpInput,
HpRuntimeConfig,
- HpModel
+ HpModel,
](scheduler, initStateData)
with HpAgentFundamentals {
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala
index a804bd5671..0fb9103312 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/hp/HpAgentFundamentals.scala
@@ -6,11 +6,10 @@
package edu.ie3.simona.agent.participant.hp
-import org.apache.pekko.actor.{ActorRef, FSM}
import edu.ie3.datamodel.models.input.system.HpInput
import edu.ie3.datamodel.models.result.system.{
HpResult,
- SystemParticipantResult
+ SystemParticipantResult,
}
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.ParticipantAgent.getAndCheckNodalVoltage
@@ -19,15 +18,17 @@ import edu.ie3.simona.agent.participant.data.Data
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPowerAndHeat
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.hp.HpAgent.neededServices
-import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
+import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
+ FlexControlledData,
+ ParticipantModelBaseStateData,
+}
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
InputModelContainer,
- WithHeatInputContainer
+ WithHeatInputContainer,
}
import edu.ie3.simona.agent.participant.statedata.{
BaseStateData,
- DataCollectionStateData,
- ParticipantStateData
+ ParticipantStateData,
}
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
@@ -36,19 +37,25 @@ import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.{
AgentInitializationException,
InconsistentStateException,
- InvalidRequestException
+ InvalidRequestException,
}
import edu.ie3.simona.io.result.AccompaniedSimulationResult
-import edu.ie3.simona.model.participant.HpModel
import edu.ie3.simona.model.participant.HpModel.{HpRelevantData, HpState}
+import edu.ie3.simona.model.participant.{FlexChangeIndicator, HpModel}
import edu.ie3.simona.model.thermal.ThermalGrid
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
+}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
-import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.quantities.PowerSystemUnits.PU
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.{Megavars, ReactivePower}
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
import squants.energy.Megawatts
import squants.{Dimensionless, Each, Power}
-import tech.units.indriya.quantity.Quantities
import java.time.ZonedDateTime
import java.util.UUID
@@ -59,10 +66,11 @@ trait HpAgentFundamentals
extends ParticipantAgentFundamentals[
ApparentPowerAndHeat,
HpRelevantData,
+ HpState,
ParticipantStateData[ApparentPowerAndHeat],
HpInput,
HpRuntimeConfig,
- HpModel
+ HpModel,
] {
this: HpAgent =>
override protected val pdClassTag: ClassTag[ApparentPowerAndHeat] =
@@ -70,7 +78,7 @@ trait HpAgentFundamentals
override val alternativeResult: ApparentPowerAndHeat = ApparentPowerAndHeat(
Megawatts(0d),
Megavars(0d),
- Megawatts(0d)
+ Megawatts(0d),
)
/** Partial function, that is able to transfer
@@ -82,15 +90,102 @@ trait HpAgentFundamentals
BaseStateData.ParticipantModelBaseStateData[
ApparentPowerAndHeat,
HpRelevantData,
- HpModel
+ HpState,
+ HpModel,
],
- Dimensionless
+ HpState,
+ Dimensionless,
) => ApparentPowerAndHeat =
- (_, _, _) =>
+ (_, _, _, _) =>
throw new InvalidRequestException(
- "HP model cannot be run without secondary data."
+ "Heat pump model cannot be run without secondary data."
)
+ override protected def createInitialState(
+ baseStateData: BaseStateData.ParticipantModelBaseStateData[
+ ApparentPowerAndHeat,
+ HpRelevantData,
+ HpState,
+ HpModel,
+ ]
+ ): HpState = startingState(baseStateData.model.thermalGrid)
+
+ private def startingState(
+ thermalGrid: ThermalGrid
+ ): HpState = HpState(
+ isRunning = false,
+ -1,
+ None,
+ Megawatts(0d),
+ Megawatts(0d),
+ ThermalGrid.startingState(thermalGrid),
+ None,
+ )
+
+ /** Handle an active power change by flex control.
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPowerAndHeat,
+ HpRelevantData,
+ HpState,
+ HpModel,
+ ],
+ data: HpRelevantData,
+ lastState: HpState,
+ setPower: squants.Power,
+ ): (HpState, ApparentPowerAndHeat, FlexChangeIndicator) = {
+ /* Determine needed information */
+ val voltage =
+ getAndCheckNodalVoltage(baseStateData, tick)
+ val relevantData = createCalcRelevantData(baseStateData, tick)
+
+ val modelState = baseStateData.stateDataStore.last(tick) match {
+ case Some((lastTick, _)) if lastTick == tick =>
+ /* We already updated the state for this tick, take the one before */
+ baseStateData.stateDataStore.last(tick - 1) match {
+ case Some((_, earlierModelState)) => earlierModelState
+ case None =>
+ throw new InconsistentStateException(
+ s"Unable to get state for heat pump '${baseStateData.model.getUuid}' in tick ${tick - 1}."
+ )
+ }
+ case Some((_, lastModelState)) =>
+ lastModelState
+ case None =>
+ throw new InconsistentStateException(
+ s"Unable to get state for heat pump '${baseStateData.model.getUuid}' in tick $tick."
+ )
+ }
+
+ /* Handle the control request */
+ val (updatedState, flexChangeIndicator) = baseStateData.model
+ .handleControlledPowerChange(relevantData, modelState, setPower)
+
+ /* Calculate power results */
+ val result = baseStateData.model.calculatePower(
+ tick,
+ voltage,
+ updatedState,
+ relevantData,
+ )
+
+ (updatedState, result, flexChangeIndicator)
+ }
+
/** Abstractly calculate the power output of the participant utilising
* secondary data. However, it might appear, that not the complete set of
* secondary data is available for the given tick. This might especially be
@@ -102,8 +197,10 @@ trait HpAgentFundamentals
* scheduler and using update result values. Actual implementation
* can be found in each participant's fundamentals.
*
- * @param collectionStateData
+ * @param baseStateData
* State data with collected secondary data.
+ * @param lastModelState
+ * Optional last model state
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -112,86 +209,82 @@ trait HpAgentFundamentals
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPowerAndHeat],
+ baseStateData: BaseStateData.ParticipantModelBaseStateData[
+ ApparentPowerAndHeat,
+ HpRelevantData,
+ HpState,
+ HpModel,
+ ],
+ lastModelState: HpState,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPowerAndHeat]] = {
- val voltage =
- getAndCheckNodalVoltage(collectionStateData.baseStateData, currentTick)
-
- val (result, relevantData) =
- collectionStateData.baseStateData match {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.model match {
- case hpModel: HpModel =>
- /* extract weather data from secondary data, which should have been requested and received before */
- val weatherData =
- collectionStateData.data
- .collectFirst {
- // filter secondary data for weather data
- case (_, Some(data: WeatherData)) => data
- }
- .getOrElse(
- throw new InconsistentStateException(
- s"The model ${modelBaseStateData.model} was not provided with needed weather data."
- )
- )
- /* Try to get the last calc relevant data. It contains the hp state after the last calculation. If no data
- * can be found, this is the first calculation step and therefore define starting state. */
- val hpState = modelBaseStateData.calcRelevantDateStore.last(
- currentTick
- ) match {
- case Some((_, HpRelevantData(lastState, _, _))) => lastState
- case None =>
- startingState(hpModel.thermalGrid)
- }
-
- val relevantData =
- HpRelevantData(
- hpState,
- currentTick,
- weatherData.temp
- )
-
- val power = hpModel.calculatePower(
- currentTick,
- voltage,
- relevantData
- )
+ /* Determine needed information */
+ val voltage =
+ getAndCheckNodalVoltage(baseStateData, currentTick)
+ val relevantData = createCalcRelevantData(baseStateData, currentTick)
- val accompanyingResults = hpModel.thermalGrid.results(
- relevantData.hpState.thermalGridState
- )(modelBaseStateData.startDate)
+ /* Determine the next state */
+ val updatedState =
+ updateState(
+ currentTick,
+ lastModelState,
+ relevantData,
+ voltage,
+ baseStateData.model,
+ )
- (
- AccompaniedSimulationResult(power, accompanyingResults),
- relevantData
- )
- case _ =>
- throw new InconsistentStateException(
- "Cannot find a model for model calculation."
- )
- }
- }
+ /* Calculate power results */
+ val power = baseStateData.model.calculatePower(
+ currentTick,
+ voltage,
+ updatedState,
+ relevantData,
+ )
+ val accompanyingResults = baseStateData.model.thermalGrid.results(
+ lastModelState.thermalGridState
+ )(baseStateData.startDate)
+ val result = AccompaniedSimulationResult(power, accompanyingResults)
+ val updatedStateDataStore = ValueStore.updateValueStore(
+ baseStateData.stateDataStore,
+ currentTick,
+ updatedState,
+ )
+ val updatedBaseStateData =
+ baseStateData.copy(stateDataStore = updatedStateDataStore)
updateValueStoresInformListenersAndGoToIdleWithUpdatedBaseStateData(
scheduler,
- collectionStateData.baseStateData,
+ updatedBaseStateData,
result,
- relevantData
+ relevantData,
)
}
- private def startingState(
- thermalGrid: ThermalGrid
- ): HpState = HpState(
- isRunning = false,
- -1,
- Megawatts(0d),
- Megawatts(0d),
- ThermalGrid.startingState(thermalGrid)
- )
+ /** Update the last known model state with the given external, relevant data
+ *
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
+ */
+ protected override def updateState(
+ tick: Long,
+ modelState: HpState,
+ calcRelevantData: HpRelevantData,
+ nodalVoltage: squants.Dimensionless,
+ model: HpModel,
+ ): HpState = model.determineState(modelState, calcRelevantData)
/** Abstract definition, individual implementations found in individual agent
* fundamental classes
@@ -199,18 +292,20 @@ trait HpAgentFundamentals
override def determineModelBaseStateData(
inputModel: InputModelContainer[HpInput],
modelConfig: HpRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: Data.SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: Data.SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
): BaseStateData.ParticipantModelBaseStateData[
ApparentPowerAndHeat,
HpRelevantData,
- HpModel
+ HpState,
+ HpModel,
] = {
- if (!services.exists(_.map(_.getClass).containsSlice(neededServices)))
+ if (!services.toSeq.map(_.getClass).containsSlice(neededServices))
throw new AgentInitializationException(
"HpAgent cannot be initialized without its needed services."
)
@@ -222,12 +317,22 @@ trait HpAgentFundamentals
withHeatContainer,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
+
+ /* Determine a proper starting model state and save it into the base state data */
+ val startingModelState = startingState(model.thermalGrid)
+ val stateDataStore = ValueStore.updateValueStore(
+ ValueStore(resolution),
+ -1L,
+ startingModelState,
+ )
+
ParticipantModelBaseStateData[
ApparentPowerAndHeat,
HpRelevantData,
- HpModel
+ HpState,
+ HpModel,
](
simulationStartDate,
simulationEndDate,
@@ -244,12 +349,14 @@ trait HpAgentFundamentals
.getvTarget()
.to(PU)
.getValue
- .doubleValue()
- )
+ .doubleValue
+ ),
),
ValueStore(resolution),
ValueStore(resolution),
- ValueStore(resolution)
+ ValueStore(resolution),
+ stateDataStore,
+ maybeEmAgent.map(FlexControlledData(_, self.toTyped[FlexRequest])),
)
case unsupported =>
throw new AgentInitializationException(
@@ -259,6 +366,42 @@ trait HpAgentFundamentals
}
}
+ override protected def createCalcRelevantData(
+ baseStateData: BaseStateData.ParticipantModelBaseStateData[
+ ApparentPowerAndHeat,
+ HpRelevantData,
+ HpState,
+ HpModel,
+ ],
+ tick: Long,
+ ): HpRelevantData = {
+ /* extract weather data from secondary data, which should have been requested and received before */
+ val weatherData =
+ baseStateData.receivedSecondaryDataStore
+ .last(tick)
+ .flatMap { case (receivedTick, receivedValues) =>
+ if (receivedTick != tick)
+ log.debug(
+ s"The model ${baseStateData.model.getUuid} needs to do calculations with values received " +
+ s"in tick $receivedTick, as no weather data has been received in tick $tick."
+ )
+ receivedValues.collectFirst {
+ // filter secondary data for weather data
+ case (_, data: WeatherData) => data
+ }
+ }
+ .getOrElse(
+ throw new InconsistentStateException(
+ s"The model ${baseStateData.model} was not provided with needed weather data."
+ )
+ )
+
+ HpRelevantData(
+ tick,
+ weatherData.temp.inKelvin,
+ )
+ }
+
/** Abstract method to build the calculation model from input
*
* @param inputModel
@@ -275,7 +418,7 @@ trait HpAgentFundamentals
inputModel: InputModelContainer[HpInput],
modelConfig: HpRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): HpModel = inputModel match {
case ParticipantStateData.SimpleInputContainer(_) =>
throw new AgentInitializationException(
@@ -288,7 +431,7 @@ trait HpAgentFundamentals
modelConfig.scaling,
simulationStartDate,
simulationEndDate,
- ThermalGrid(thermalGrid)
+ ThermalGrid(thermalGrid),
)
}
@@ -311,14 +454,14 @@ trait HpAgentFundamentals
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ]
+ ],
): ApparentPowerAndHeat =
ParticipantAgentFundamentals.averageApparentPowerAndHeat(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
/** Determines the correct result.
@@ -335,12 +478,12 @@ trait HpAgentFundamentals
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPowerAndHeat
+ result: ApparentPowerAndHeat,
): SystemParticipantResult = new HpResult(
dateTime,
uuid,
- Quantities.getQuantity(result.p.toKilowatts, PowerSystemUnits.KILOWATT),
- Quantities.getQuantity(result.q.toKilovars, PowerSystemUnits.KILOVAR),
- Quantities.getQuantity(result.qDot.toKilowatts, PowerSystemUnits.KILOWATT)
+ result.p.toMegawatts.asMegaWatt,
+ result.q.toMegavars.asMegaVar,
+ result.qDot.toMegawatts.asMegaWatt,
)
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala
index 19fb399742..338547efbc 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgent.scala
@@ -6,19 +6,19 @@
package edu.ie3.simona.agent.participant.load
-import org.apache.pekko.actor.{ActorRef, Props}
import edu.ie3.datamodel.models.input.system.LoadInput
import edu.ie3.simona.agent.participant.ParticipantAgent
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.agent.participant.load.LoadAgentFundamentals.{
FixedLoadAgentFundamentals,
ProfileLoadAgentFundamentals,
- RandomLoadAgentFundamentals
+ RandomLoadAgentFundamentals,
}
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData
import edu.ie3.simona.config.SimonaConfig.LoadRuntimeConfig
import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel
import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel.ProfileRelevantData
import edu.ie3.simona.model.participant.load.random.RandomLoadModel
@@ -26,8 +26,9 @@ import edu.ie3.simona.model.participant.load.random.RandomLoadModel.RandomReleva
import edu.ie3.simona.model.participant.load.{
FixedLoadModel,
LoadModel,
- LoadModelBehaviour
+ LoadModelBehaviour,
}
+import org.apache.pekko.actor.{ActorRef, Props}
object LoadAgent {
def props(
@@ -35,9 +36,9 @@ object LoadAgent {
initStateData: ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- listener: Iterable[ActorRef]
+ listener: Iterable[ActorRef],
): Props =
LoadModelBehaviour(initStateData.modelConfig.modelBehaviour) match {
case LoadModelBehaviour.FIX =>
@@ -57,12 +58,12 @@ object LoadAgent {
initStateData: ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends LoadAgent[
FixedLoadModel.FixedLoadRelevantData.type,
- FixedLoadModel
+ FixedLoadModel,
](scheduler, initStateData, listener)
with FixedLoadAgentFundamentals
@@ -71,12 +72,12 @@ object LoadAgent {
initStateData: ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends LoadAgent[
ProfileRelevantData,
- ProfileLoadModel
+ ProfileLoadModel,
](scheduler, initStateData, listener)
with ProfileLoadAgentFundamentals
@@ -85,12 +86,12 @@ object LoadAgent {
initStateData: ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends LoadAgent[
RandomRelevantData,
- RandomLoadModel
+ RandomLoadModel,
](scheduler, initStateData, listener)
with RandomLoadAgentFundamentals
}
@@ -107,16 +108,17 @@ abstract class LoadAgent[LD <: LoadRelevantData, LM <: LoadModel[LD]](
initStateData: ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends ParticipantAgent[
ApparentPower,
LD,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
LoadInput,
LoadRuntimeConfig,
- LM
+ LM,
](scheduler, initStateData)
with LoadAgentFundamentals[LD, LM] {
/*
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala
index 64b37b56ae..79df65997c 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/load/LoadAgentFundamentals.scala
@@ -6,26 +6,26 @@
package edu.ie3.simona.agent.participant.load
-import org.apache.pekko.actor.{ActorRef, FSM}
import edu.ie3.datamodel.models.input.system.LoadInput
import edu.ie3.datamodel.models.result.system.{
LoadResult,
- SystemParticipantResult
+ SystemParticipantResult,
}
import edu.ie3.simona.agent.ValueStore
+import edu.ie3.simona.agent.participant.ParticipantAgent.getAndCheckNodalVoltage
import edu.ie3.simona.agent.participant.ParticipantAgentFundamentals
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- ZERO_POWER
+ ZERO_POWER,
}
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
-import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
-import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
-import edu.ie3.simona.agent.participant.statedata.{
- DataCollectionStateData,
- ParticipantStateData
+import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
+ FlexControlledData,
+ ParticipantModelBaseStateData,
}
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
import edu.ie3.simona.config.SimonaConfig.LoadRuntimeConfig
@@ -33,21 +33,27 @@ import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.InconsistentStateException
import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.load.FixedLoadModel.FixedLoadRelevantData
import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel.ProfileRelevantData
import edu.ie3.simona.model.participant.load.profile.{
LoadProfileStore,
- ProfileLoadModel
+ ProfileLoadModel,
}
import edu.ie3.simona.model.participant.load.random.RandomLoadModel.RandomRelevantData
import edu.ie3.simona.model.participant.load.random.{
RandomLoadModel,
- RandomLoadParamStore
+ RandomLoadParamStore,
}
import edu.ie3.simona.model.participant.load.{
FixedLoadModel,
LoadModel,
- LoadReference
+ LoadReference,
+}
+import edu.ie3.simona.model.participant.{FlexChangeIndicator, ModelState}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
}
import edu.ie3.simona.util.SimonaConstants
import edu.ie3.simona.util.TickUtil._
@@ -55,6 +61,9 @@ import edu.ie3.util.quantities.PowerSystemUnits.PU
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
import squants.{Dimensionless, Each, Power}
import java.time.ZonedDateTime
@@ -67,10 +76,11 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
]] extends ParticipantAgentFundamentals[
ApparentPower,
LD,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
LoadInput,
LoadRuntimeConfig,
- LM
+ LM,
] {
this: LoadAgent[LD, LM] =>
override protected val pdClassTag: ClassTag[ApparentPower] =
@@ -85,7 +95,7 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
* @param modelConfig
* Configuration of the model
* @param services
- * Optional collection of services to register with
+ * Collection of services to register with
* @param simulationStartDate
* Real world time date time, when the simulation starts
* @param simulationEndDate
@@ -104,20 +114,26 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
override def determineModelBaseStateData(
inputModel: InputModelContainer[LoadInput],
modelConfig: LoadRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ParticipantModelBaseStateData[ApparentPower, LD, LM] = {
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
+ ): ParticipantModelBaseStateData[
+ ApparentPower,
+ LD,
+ ConstantState.type,
+ LM,
+ ] = {
/* Build the calculation model */
val model =
buildModel(
inputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
/* Go and collect all ticks, in which activation is needed in addition to the activations made by incoming data.
@@ -136,27 +152,27 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
SortedSet[Long](
SimonaConstants.FIRST_TICK_IN_SIMULATION,
fixedLoadModel.operationInterval.start,
- fixedLoadModel.operationInterval.end
+ fixedLoadModel.operationInterval.end,
).filterNot(_ == lastTickInSimulation)
case profileLoadModel: ProfileLoadModel =>
activationTicksInOperationTime(
simulationStartDate,
LoadProfileStore.resolution.getSeconds,
profileLoadModel.operationInterval.start,
- profileLoadModel.operationInterval.end
+ profileLoadModel.operationInterval.end,
)
case randomLoadModel: RandomLoadModel =>
activationTicksInOperationTime(
simulationStartDate,
RandomLoadParamStore.resolution.getSeconds,
randomLoadModel.operationInterval.start,
- randomLoadModel.operationInterval.end
+ randomLoadModel.operationInterval.end,
)
case _ =>
SortedSet.empty[Long]
}
- ParticipantModelBaseStateData[ApparentPower, LD, LM](
+ ParticipantModelBaseStateData[ApparentPower, LD, ConstantState.type, LM](
simulationStartDate,
simulationEndDate,
model,
@@ -173,11 +189,13 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
.to(PU)
.getValue
.doubleValue
- )
+ ),
),
- ValueStore.forResult(resolution, 2),
ValueStore(resolution),
- ValueStore(resolution)
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ maybeEmAgent.map(FlexControlledData(_, self.toTyped[FlexRequest])),
)
}
@@ -185,24 +203,80 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
inputModel: InputModelContainer[LoadInput],
modelConfig: LoadRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): LM = {
val operationInterval: OperationInterval =
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- inputModel.electricalInputModel.getOperationTime
+ inputModel.electricalInputModel.getOperationTime,
)
val reference = LoadReference(inputModel.electricalInputModel, modelConfig)
- buildModel(inputModel.electricalInputModel, operationInterval, reference)
+ buildModel(
+ inputModel.electricalInputModel,
+ operationInterval,
+ modelConfig,
+ reference,
+ )
}
protected def buildModel(
inputModel: LoadInput,
operationInterval: OperationInterval,
- reference: LoadReference
+ modelConfig: LoadRuntimeConfig,
+ reference: LoadReference,
): LM
+ override protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ LD,
+ ConstantState.type,
+ LM,
+ ]
+ ): ModelState.ConstantState.type = ConstantState
+
+ /** Handle an active power change by flex control.
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ LD,
+ ConstantState.type,
+ LM,
+ ],
+ data: LD,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, ApparentPower, FlexChangeIndicator) = {
+ /* Calculate result */
+ val voltage = getAndCheckNodalVoltage(baseStateData, tick)
+
+ val reactivePower = baseStateData.model.calculateReactivePower(
+ setPower,
+ voltage,
+ )
+ val result = ApparentPower(setPower, reactivePower)
+
+ /* Handle the request within the model */
+ val (updatedState, flexChangeIndicator) =
+ baseStateData.model.handleControlledPowerChange(data, lastState, setPower)
+ (updatedState, result, flexChangeIndicator)
+ }
+
/** Calculate the power output of the participant utilising secondary data.
* However, it might appear, that not the complete set of secondary data is
* available for the given tick. This might especially be true, if the actor
@@ -213,8 +287,10 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
* [[edu.ie3.simona.ontology.messages.SchedulerMessage.Completion]] to
* scheduler and using update result values.
*
- * @param collectionStateData
- * State data with collected, comprehensive secondary data.
+ * @param baseStateData
+ * The base state data with collected secondary data
+ * @param maybeLastModelState
+ * Optional last model state
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -223,9 +299,15 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPower],
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ LD,
+ ConstantState.type,
+ LM,
+ ],
+ lastModelState: ConstantState.type,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] =
throw new InconsistentStateException(
s"Load model is not able to calculate power with secondary data."
@@ -250,14 +332,14 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ] = None
+ ] = None,
): ApparentPower =
ParticipantAgentFundamentals.averageApparentPower(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
/** Determines the correct result.
@@ -274,33 +356,55 @@ protected trait LoadAgentFundamentals[LD <: LoadRelevantData, LM <: LoadModel[
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPower
+ result: ApparentPower,
): SystemParticipantResult =
new LoadResult(
dateTime,
uuid,
result.p.toMegawatts.asMegaWatt,
- result.q.toMegavars.asMegaVar
+ result.q.toMegavars.asMegaVar,
)
+
+ override protected def updateState(
+ tick: Long,
+ modelState: ModelState.ConstantState.type,
+ calcRelevantData: LD,
+ nodalVoltage: squants.Dimensionless,
+ model: LM,
+ ): ModelState.ConstantState.type = modelState
}
-case object LoadAgentFundamentals {
+object LoadAgentFundamentals {
trait FixedLoadAgentFundamentals
extends LoadAgentFundamentals[
FixedLoadModel.FixedLoadRelevantData.type,
- FixedLoadModel
+ FixedLoadModel,
] {
this: LoadAgent.FixedLoadAgent =>
override def buildModel(
inputModel: LoadInput,
operationInterval: OperationInterval,
- reference: LoadReference
- ): FixedLoadModel = {
- val model = FixedLoadModel(inputModel, operationInterval, 1d, reference)
- model.enable()
- model
- }
+ modelConfig: LoadRuntimeConfig,
+ reference: LoadReference,
+ ): FixedLoadModel =
+ FixedLoadModel(
+ inputModel,
+ modelConfig.scaling,
+ operationInterval,
+ reference,
+ )
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedLoadRelevantData.type,
+ ConstantState.type,
+ FixedLoadModel,
+ ],
+ tick: Long,
+ ): FixedLoadRelevantData.type =
+ FixedLoadRelevantData
/** Partial function, that is able to transfer
* [[ParticipantModelBaseStateData]] (holding the actual calculation model)
@@ -311,37 +415,62 @@ case object LoadAgentFundamentals {
ParticipantModelBaseStateData[
ApparentPower,
FixedLoadRelevantData.type,
- FixedLoadModel
+ ConstantState.type,
+ FixedLoadModel,
],
- Dimensionless
+ ConstantState.type,
+ Dimensionless,
) => ApparentPower = (
tick: Long,
baseStateData: ParticipantModelBaseStateData[
ApparentPower,
FixedLoadRelevantData.type,
- FixedLoadModel
+ ConstantState.type,
+ FixedLoadModel,
],
- voltage: Dimensionless
+ state: ConstantState.type,
+ voltage: Dimensionless,
) =>
- baseStateData.model.calculatePower(tick, voltage, FixedLoadRelevantData)
+ baseStateData.model.calculatePower(
+ tick,
+ voltage,
+ state,
+ FixedLoadRelevantData,
+ )
}
trait ProfileLoadAgentFundamentals
extends LoadAgentFundamentals[
ProfileRelevantData,
- ProfileLoadModel
+ ProfileLoadModel,
] {
this: LoadAgent.ProfileLoadAgent =>
override def buildModel(
inputModel: LoadInput,
operationInterval: OperationInterval,
- reference: LoadReference
- ): ProfileLoadModel = {
- val model = ProfileLoadModel(inputModel, operationInterval, 1d, reference)
- model.enable()
- model
- }
+ modelConfig: LoadRuntimeConfig,
+ reference: LoadReference,
+ ): ProfileLoadModel =
+ ProfileLoadModel(
+ inputModel,
+ operationInterval,
+ modelConfig.scaling,
+ reference,
+ )
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ ProfileRelevantData,
+ ConstantState.type,
+ ProfileLoadModel,
+ ],
+ currentTick: Long,
+ ): ProfileRelevantData =
+ ProfileRelevantData(
+ currentTick.toDateTime(baseStateData.startDate)
+ )
/** Partial function, that is able to transfer
* [[ParticipantModelBaseStateData]] (holding the actual calculation model)
@@ -352,34 +481,56 @@ case object LoadAgentFundamentals {
ParticipantModelBaseStateData[
ApparentPower,
ProfileRelevantData,
- ProfileLoadModel
+ ConstantState.type,
+ ProfileLoadModel,
],
- Dimensionless
- ) => ApparentPower = (tick, baseStateData, voltage) => {
- val profileLoadModel = baseStateData.model
- val profileRelevantData = ProfileRelevantData(
- tick.toDateTime(baseStateData.startDate)
+ ConstantState.type,
+ Dimensionless,
+ ) => ApparentPower = (tick, baseStateData, _, voltage) => {
+ val profileRelevantData =
+ createCalcRelevantData(baseStateData, tick)
+
+ baseStateData.model.calculatePower(
+ currentTick,
+ voltage,
+ ConstantState,
+ profileRelevantData,
)
- profileLoadModel.calculatePower(currentTick, voltage, profileRelevantData)
}
}
trait RandomLoadAgentFundamentals
extends LoadAgentFundamentals[
RandomRelevantData,
- RandomLoadModel
+ RandomLoadModel,
] {
this: LoadAgent.RandomLoadAgent =>
override def buildModel(
inputModel: LoadInput,
operationInterval: OperationInterval,
- reference: LoadReference
- ): RandomLoadModel = {
- val model = RandomLoadModel(inputModel, operationInterval, 1d, reference)
- model.enable()
- model
- }
+ modelConfig: LoadRuntimeConfig,
+ reference: LoadReference,
+ ): RandomLoadModel =
+ RandomLoadModel(
+ inputModel,
+ operationInterval,
+ modelConfig.scaling,
+ reference,
+ )
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ RandomRelevantData,
+ ConstantState.type,
+ RandomLoadModel,
+ ],
+ tick: Long,
+ ): RandomRelevantData =
+ RandomRelevantData(
+ tick.toDateTime(baseStateData.startDate)
+ )
/** Partial function, that is able to transfer
* [[ParticipantModelBaseStateData]] (holding the actual calculation model)
@@ -390,15 +541,21 @@ case object LoadAgentFundamentals {
ParticipantModelBaseStateData[
ApparentPower,
RandomRelevantData,
- RandomLoadModel
+ ConstantState.type,
+ RandomLoadModel,
],
- Dimensionless
- ) => ApparentPower = (tick, baseStateData, voltage) => {
- val randomLoadModel = baseStateData.model
- val profileRelevantData = RandomRelevantData(
- tick.toDateTime(baseStateData.startDate)
+ ConstantState.type,
+ Dimensionless,
+ ) => ApparentPower = (tick, baseStateData, _, voltage) => {
+ val profileRelevantData =
+ createCalcRelevantData(baseStateData, tick)
+
+ baseStateData.model.calculatePower(
+ currentTick,
+ voltage,
+ ConstantState,
+ profileRelevantData,
)
- randomLoadModel.calculatePower(currentTick, voltage, profileRelevantData)
}
}
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgent.scala
index d82002aaec..bf6f5db3b7 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgent.scala
@@ -6,7 +6,6 @@
package edu.ie3.simona.agent.participant.pv
-import org.apache.pekko.actor.{ActorRef, Props}
import edu.ie3.datamodel.models.input.system.PvInput
import edu.ie3.simona.agent.participant.ParticipantAgent
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
@@ -15,8 +14,10 @@ import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.Acto
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData
import edu.ie3.simona.config.SimonaConfig.PvRuntimeConfig
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.PvModel
import edu.ie3.simona.model.participant.PvModel.PvRelevantData
+import org.apache.pekko.actor.{ActorRef, Props}
object PvAgent {
def props(
@@ -24,15 +25,15 @@ object PvAgent {
initStateData: ParticipantInitializeStateData[
PvInput,
PvRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- listener: Iterable[ActorRef]
+ listener: Iterable[ActorRef],
): Props =
Props(
new PvAgent(
scheduler,
initStateData,
- listener
+ listener,
)
)
@@ -53,19 +54,20 @@ class PvAgent(
initStateData: ParticipantInitializeStateData[
PvInput,
PvRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends ParticipantAgent[
ApparentPower,
PvRelevantData,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
PvInput,
PvRuntimeConfig,
- PvModel
+ PvModel,
](
scheduler,
- initStateData
+ initStateData,
)
with PvAgentFundamentals {
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgentFundamentals.scala
index 64d955ff73..a60fa87b63 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/pv/PvAgentFundamentals.scala
@@ -6,28 +6,27 @@
package edu.ie3.simona.agent.participant.pv
-import org.apache.pekko.actor.{ActorRef, FSM}
import edu.ie3.datamodel.models.input.system.PvInput
import edu.ie3.datamodel.models.result.system.{
PvResult,
- SystemParticipantResult
+ SystemParticipantResult,
}
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.ParticipantAgent.getAndCheckNodalVoltage
import edu.ie3.simona.agent.participant.ParticipantAgentFundamentals
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- ZERO_POWER
+ ZERO_POWER,
}
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.pv.PvAgent.neededServices
-import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
-import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
-import edu.ie3.simona.agent.participant.statedata.{
- DataCollectionStateData,
- ParticipantStateData
+import edu.ie3.simona.agent.participant.statedata.BaseStateData.{
+ FlexControlledData,
+ ParticipantModelBaseStateData,
}
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
import edu.ie3.simona.config.SimonaConfig.PvRuntimeConfig
@@ -35,17 +34,29 @@ import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.{
AgentInitializationException,
InconsistentStateException,
- InvalidRequestException
+ InvalidRequestException,
}
import edu.ie3.simona.io.result.AccompaniedSimulationResult
-import edu.ie3.simona.model.participant.PvModel
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.PvModel.PvRelevantData
+import edu.ie3.simona.model.participant.{
+ FlexChangeIndicator,
+ ModelState,
+ PvModel,
+}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
+}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
import edu.ie3.simona.service.weather.WeatherService.FALLBACK_WEATHER_STEM_DISTANCE
import edu.ie3.simona.util.TickUtil.TickLong
import edu.ie3.util.quantities.PowerSystemUnits.PU
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
import squants.{Dimensionless, Each, Power}
import java.time.ZonedDateTime
@@ -57,10 +68,11 @@ protected trait PvAgentFundamentals
extends ParticipantAgentFundamentals[
ApparentPower,
PvRelevantData,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
PvInput,
PvRuntimeConfig,
- PvModel
+ PvModel,
] {
this: PvAgent =>
override protected val pdClassTag: ClassTag[ApparentPower] =
@@ -75,7 +87,7 @@ protected trait PvAgentFundamentals
* @param modelConfig
* Configuration of the model
* @param services
- * Optional collection of services to register with
+ * Collection of services to register with
* @param simulationStartDate
* Real world time date time, when the simulation starts
* @param simulationEndDate
@@ -94,19 +106,21 @@ protected trait PvAgentFundamentals
override def determineModelBaseStateData(
inputModel: InputModelContainer[PvInput],
modelConfig: PvRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ParticipantModelBaseStateData[ApparentPower, PvRelevantData, PvModel] = {
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
+ ): ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ] = {
/* Check for needed services */
- if (
- !services.exists(serviceDefinitions =>
- serviceDefinitions.map(_.getClass).containsSlice(neededServices)
- )
- )
+ if (!services.toSeq.map(_.getClass).containsSlice(neededServices))
throw new AgentInitializationException(
s"PvAgent cannot be initialized without a weather service!"
)
@@ -117,10 +131,15 @@ protected trait PvAgentFundamentals
inputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
- ParticipantModelBaseStateData[ApparentPower, PvRelevantData, PvModel](
+ ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ](
simulationStartDate,
simulationEndDate,
model,
@@ -130,18 +149,20 @@ protected trait PvAgentFundamentals
Map.empty,
requestVoltageDeviationThreshold,
ValueStore.forVoltage(
- resolution * 10,
+ resolution,
Each(
inputModel.electricalInputModel.getNode
.getvTarget()
.to(PU)
.getValue
.doubleValue
- )
+ ),
),
- ValueStore.forResult(resolution, 10),
- ValueStore(resolution * 10),
- ValueStore(resolution * 10)
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ maybeEmAgent.map(FlexControlledData(_, self.toTyped[FlexRequest])),
)
}
@@ -149,24 +170,138 @@ protected trait PvAgentFundamentals
inputModel: InputModelContainer[PvInput],
modelConfig: PvRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): PvModel = PvModel(
inputModel.electricalInputModel,
modelConfig.scaling,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
+ override protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ]
+ ): ModelState.ConstantState.type =
+ ConstantState
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ],
+ tick: Long,
+ ): PvRelevantData = {
+ /* convert current tick to a datetime */
+ implicit val startDateTime: ZonedDateTime =
+ baseStateData.startDate
+ val dateTime = tick.toDateTime
+
+ val tickInterval =
+ baseStateData.receivedSecondaryDataStore
+ .lastKnownTick(tick - 1) match {
+ case Some(dataTick) =>
+ tick - dataTick
+ case _ =>
+ /* At the first tick, we are not able to determine the tick interval from last tick
+ * (since there is none). Then we use a fall back pv stem distance. */
+ FALLBACK_WEATHER_STEM_DISTANCE
+ }
+
+ // take the last weather data, not necessarily the one for the current tick:
+ // we might receive flex control messages for irregular ticks
+ val (_, secondaryData) = baseStateData.receivedSecondaryDataStore
+ .last(tick)
+ .getOrElse(
+ throw new InconsistentStateException(
+ s"The model ${baseStateData.model} was not provided with any secondary data so far."
+ )
+ )
+
+ /* extract weather data from secondary data, which should have been requested and received before */
+ val weatherData =
+ secondaryData
+ .collectFirst {
+ // filter secondary data for weather data
+ case (_, data: WeatherData) =>
+ data
+ }
+ .getOrElse(
+ throw new InconsistentStateException(
+ s"The model ${baseStateData.model} was not provided with needed weather data."
+ )
+ )
+
+ PvRelevantData(
+ dateTime,
+ tickInterval,
+ weatherData.diffIrr,
+ weatherData.dirIrr,
+ )
+ }
+
+ /** Handle an active power change by flex control.
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ override def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ],
+ data: PvRelevantData,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, ApparentPower, FlexChangeIndicator) = {
+ /* Calculate result */
+ val voltage = getAndCheckNodalVoltage(baseStateData, tick)
+
+ val reactivePower = baseStateData.model.calculateReactivePower(
+ setPower,
+ voltage,
+ )
+ val result = ApparentPower(setPower, reactivePower)
+
+ /* Handle the request within the model */
+ val (updatedState, flexChangeIndicator) =
+ baseStateData.model.handleControlledPowerChange(data, lastState, setPower)
+ (updatedState, result, flexChangeIndicator)
+ }
+
/** Partial function, that is able to transfer
* [[ParticipantModelBaseStateData]] (holding the actual calculation model)
* into a pair of active and reactive power
*/
override val calculateModelPowerFunc: (
Long,
- ParticipantModelBaseStateData[ApparentPower, PvRelevantData, PvModel],
- Dimensionless
+ ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ],
+ ConstantState.type,
+ Dimensionless,
) => ApparentPower =
- (_, _, _) =>
+ (_, _, _, _) =>
throw new InvalidRequestException(
"Pv model cannot be run without secondary data."
)
@@ -181,8 +316,10 @@ protected trait PvAgentFundamentals
* [[edu.ie3.simona.ontology.messages.SchedulerMessage.Completion]] to
* scheduler and using update result values.
*
- * @param collectionStateData
- * State data with collected, comprehensive secondary data.
+ * @param baseStateData
+ * The base state data with collected secondary data
+ * @param lastModelState
+ * Last model state
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -191,80 +328,37 @@ protected trait PvAgentFundamentals
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPower],
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ PvRelevantData,
+ ConstantState.type,
+ PvModel,
+ ],
+ lastModelState: ConstantState.type,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] = {
- implicit val startDateTime: ZonedDateTime =
- collectionStateData.baseStateData.startDate
-
val voltage =
- getAndCheckNodalVoltage(collectionStateData.baseStateData, currentTick)
-
- val (result, relevantData) =
- collectionStateData.baseStateData match {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.model match {
- case pvModel: PvModel =>
- /* convert current tick to a datetime */
- val dateTime = currentTick.toDateTime
+ getAndCheckNodalVoltage(baseStateData, currentTick)
- val tickInterval =
- modelBaseStateData.calcRelevantDateStore
- .last(currentTick - 1) match {
- case Some((tick, _)) =>
- currentTick - tick
- case _ =>
- /* At the first tick, we are not able to determine the tick interval from last tick
- * (since there is none). Then we use a fall back pv stem distance. */
- FALLBACK_WEATHER_STEM_DISTANCE
- }
-
- /* extract weather data from secondary data, which should have been requested and received before */
- val weatherData =
- collectionStateData.data
- .collectFirst {
- // filter secondary data for weather data
- case (_, Some(data: WeatherData)) =>
- data
- }
- .getOrElse(
- throw new InconsistentStateException(
- s"The model ${modelBaseStateData.model} was not provided with needed weather data."
- )
- )
-
- val relevantData =
- PvRelevantData(
- dateTime,
- tickInterval,
- weatherData.diffIrr,
- weatherData.dirIrr
- )
-
- val power = pvModel.calculatePower(
- currentTick,
- voltage,
- relevantData
- )
+ val relevantData =
+ createCalcRelevantData(
+ baseStateData,
+ currentTick,
+ )
- (power, relevantData)
- case unsupportedModel =>
- throw new InconsistentStateException(
- s"Wrong model: $unsupportedModel!"
- )
- }
- case _ =>
- throw new InconsistentStateException(
- "Cannot find a model for model calculation."
- )
- }
+ val result = baseStateData.model.calculatePower(
+ currentTick,
+ voltage,
+ ConstantState,
+ relevantData,
+ )
updateValueStoresInformListenersAndGoToIdleWithUpdatedBaseStateData(
scheduler,
- collectionStateData.baseStateData,
+ baseStateData,
AccompaniedSimulationResult(result),
- relevantData
+ relevantData,
)
}
@@ -287,14 +381,14 @@ protected trait PvAgentFundamentals
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ] = None
+ ] = None,
): ApparentPower =
ParticipantAgentFundamentals.averageApparentPower(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
/** Determines the correct result.
@@ -311,12 +405,36 @@ protected trait PvAgentFundamentals
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPower
+ result: ApparentPower,
): SystemParticipantResult =
new PvResult(
dateTime,
uuid,
result.p.toMegawatts.asMegaWatt,
- result.q.toMegavars.asMegaVar
+ result.q.toMegavars.asMegaVar,
)
+
+ /** Update the last known model state with the given external, relevant data
+ *
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
+ */
+ override protected def updateState(
+ tick: Long,
+ modelState: ModelState.ConstantState.type,
+ calcRelevantData: PvRelevantData,
+ nodalVoltage: squants.Dimensionless,
+ model: PvModel,
+ ): ModelState.ConstantState.type = modelState
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala b/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala
index a625778779..96758a2d16 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/statedata/BaseStateData.scala
@@ -6,13 +6,23 @@
package edu.ie3.simona.agent.participant.statedata
-import org.apache.pekko.actor.ActorRef
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.PrimaryDataWithApparentPower
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.event.notifier.NotifierConfig
-import edu.ie3.simona.model.participant.{CalcRelevantData, SystemParticipant}
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ ModelState,
+ SystemParticipant,
+}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
+ ProvideFlexOptions,
+}
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.{ActorRef => ClassicActorRef}
import squants.Dimensionless
import java.time.ZonedDateTime
@@ -51,7 +61,7 @@ trait BaseStateData[+PD <: PrimaryDataWithApparentPower[PD]]
/** A mapping from service reference to it's foreseen next availability of
* data
*/
- val foreseenDataTicks: Map[ActorRef, Option[Long]]
+ val foreseenDataTicks: Map[ClassicActorRef, Option[Long]]
/** A store, holding a map from tick to active / reactive power
*/
@@ -89,7 +99,8 @@ object BaseStateData {
trait ModelBaseStateData[
+PD <: PrimaryDataWithApparentPower[PD],
CD <: CalcRelevantData,
- +M <: SystemParticipant[_ <: CalcRelevantData, PD]
+ MS <: ModelState,
+ +M <: SystemParticipant[_ <: CalcRelevantData, PD, MS],
] extends BaseStateData[PD] {
/** The physical system model
@@ -98,11 +109,19 @@ object BaseStateData {
/** The services, the physical model depends on
*/
- val services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]]
+ val services: Iterable[SecondaryDataService[_ <: SecondaryData]]
/** Stores all data that are relevant to model calculation
*/
- val calcRelevantDateStore: ValueStore[CalcRelevantData]
+ val receivedSecondaryDataStore: ValueStore[
+ Map[ClassicActorRef, _ <: SecondaryData]
+ ]
+
+ val stateDataStore: ValueStore[MS]
+
+ val flexStateData: Option[FlexControlledData]
+
+ def isEmManaged: Boolean = flexStateData.nonEmpty
}
/** Basic state data, when the agent is supposed to only provide external data
@@ -134,21 +153,22 @@ object BaseStateData {
*/
final case class FromOutsideBaseStateData[M <: SystemParticipant[
_ <: CalcRelevantData,
- P
+ P,
+ _,
], +P <: PrimaryDataWithApparentPower[P]](
model: M,
override val startDate: ZonedDateTime,
override val endDate: ZonedDateTime,
override val outputConfig: NotifierConfig,
override val additionalActivationTicks: SortedSet[Long],
- override val foreseenDataTicks: Map[ActorRef, Option[Long]],
+ override val foreseenDataTicks: Map[ClassicActorRef, Option[Long]],
fillUpReactivePowerWithModelFunc: Boolean = false,
requestVoltageDeviationThreshold: Double,
override val voltageValueStore: ValueStore[
Dimensionless
],
override val resultValueStore: ValueStore[P],
- override val requestValueStore: ValueStore[P]
+ override val requestValueStore: ValueStore[P],
) extends BaseStateData[P] {
override val modelUuid: UUID = model.getUuid
}
@@ -188,31 +208,50 @@ object BaseStateData {
final case class ParticipantModelBaseStateData[
+PD <: PrimaryDataWithApparentPower[PD],
CD <: CalcRelevantData,
- +M <: SystemParticipant[_ <: CalcRelevantData, PD]
+ MS <: ModelState,
+ M <: SystemParticipant[_ <: CalcRelevantData, PD, MS],
](
override val startDate: ZonedDateTime,
override val endDate: ZonedDateTime,
override val model: M,
- override val services: Option[
- Vector[SecondaryDataService[_ <: SecondaryData]]
- ],
+ override val services: Iterable[SecondaryDataService[_ <: SecondaryData]],
override val outputConfig: NotifierConfig,
override val additionalActivationTicks: SortedSet[Long],
- override val foreseenDataTicks: Map[ActorRef, Option[Long]],
+ override val foreseenDataTicks: Map[ClassicActorRef, Option[Long]],
requestVoltageDeviationThreshold: Double,
override val voltageValueStore: ValueStore[
Dimensionless
],
override val resultValueStore: ValueStore[PD],
override val requestValueStore: ValueStore[PD],
- override val calcRelevantDateStore: ValueStore[CD]
- ) extends ModelBaseStateData[PD, CD, M] {
+ override val receivedSecondaryDataStore: ValueStore[
+ Map[ClassicActorRef, _ <: SecondaryData]
+ ],
+ override val stateDataStore: ValueStore[MS],
+ override val flexStateData: Option[FlexControlledData],
+ ) extends ModelBaseStateData[PD, CD, MS, M] {
/** Unique identifier of the simulation model
*/
override val modelUuid: UUID = model.getUuid
}
+ /** The existence of this data object indicates that the corresponding agent
+ * is EM-controlled (by [[emAgent]]).
+ *
+ * @param emAgent
+ * The parent EmAgent that is controlling this agent.
+ * @param flexAdapter
+ * The flex adapter handling [[FlexRequest]] messages
+ * @param lastFlexOptions
+ * Last flex options that have been calculated for this agent.
+ */
+ final case class FlexControlledData(
+ emAgent: ActorRef[FlexResponse],
+ flexAdapter: ActorRef[FlexRequest],
+ lastFlexOptions: Option[ProvideFlexOptions] = None,
+ )
+
/** Updates the base state data with the given value stores
*
* @param baseStateData
@@ -226,7 +265,7 @@ object BaseStateData {
* @param updatedAdditionalActivationTicks
* Additional activation ticks
* @param updatedForeseenTicks
- * Mapping from [[ActorRef]] to foreseen ticks
+ * Mapping from [[ClassicActorRef]] to foreseen ticks
* @tparam PD
* Type of primary data, that is result of model calculation
* @return
@@ -238,7 +277,7 @@ object BaseStateData {
updatedRequestValueStore: ValueStore[PD],
updatedVoltageValueStore: ValueStore[Dimensionless],
updatedAdditionalActivationTicks: SortedSet[Long],
- updatedForeseenTicks: Map[ActorRef, Option[Long]]
+ updatedForeseenTicks: Map[ClassicActorRef, Option[Long]],
): BaseStateData[PD] = {
baseStateData match {
case external: FromOutsideBaseStateData[_, PD] =>
@@ -247,15 +286,15 @@ object BaseStateData {
requestValueStore = updatedRequestValueStore,
voltageValueStore = updatedVoltageValueStore,
additionalActivationTicks = updatedAdditionalActivationTicks,
- foreseenDataTicks = updatedForeseenTicks
+ foreseenDataTicks = updatedForeseenTicks,
)
- case model: ParticipantModelBaseStateData[PD, _, _] =>
+ case model: ParticipantModelBaseStateData[PD, _, _, _] =>
model.copy(
resultValueStore = updatedResultValueStore,
requestValueStore = updatedRequestValueStore,
voltageValueStore = updatedVoltageValueStore,
additionalActivationTicks = updatedAdditionalActivationTicks,
- foreseenDataTicks = updatedForeseenTicks
+ foreseenDataTicks = updatedForeseenTicks,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/statedata/DataCollectionStateData.scala b/src/main/scala/edu/ie3/simona/agent/participant/statedata/DataCollectionStateData.scala
index d9b7f63de2..1ce8cf9352 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/statedata/DataCollectionStateData.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/statedata/DataCollectionStateData.scala
@@ -32,7 +32,7 @@ final case class DataCollectionStateData[+PD <: PrimaryDataWithApparentPower[
]](
baseStateData: BaseStateData[PD],
data: Map[ActorRef, Option[_ <: Data]],
- yetTriggered: Boolean
+ yetTriggered: Boolean,
) extends ParticipantStateData[PD] {
/** Extract the given type of [[Data]] from the list of secondary data
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/statedata/InitializeStateData.scala b/src/main/scala/edu/ie3/simona/agent/participant/statedata/InitializeStateData.scala
index 83651c6c61..de01009850 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/statedata/InitializeStateData.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/statedata/InitializeStateData.scala
@@ -20,13 +20,14 @@ trait InitializeStateData[+PD <: PrimaryData] extends ParticipantStateData[PD] {
val outputConfig: NotifierConfig
}
-case object InitializeStateData {
+object InitializeStateData {
final case class TrivialInitializeStateData[+PD <: PrimaryData](
resultEventEmitter: String
) extends InitializeStateData[PD] {
val outputConfig: NotifierConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = true
+ powerRequestReply = true,
+ flexResult = false,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala b/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala
index d52896da0c..95d8df3fcf 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/statedata/ParticipantStateData.scala
@@ -6,14 +6,16 @@
package edu.ie3.simona.agent.participant.statedata
-import org.apache.pekko.actor.ActorRef
import edu.ie3.datamodel.models.input.container.ThermalGrid
import edu.ie3.datamodel.models.input.system.SystemParticipantInput
-import edu.ie3.simona.agent.participant.data.Data.{PrimaryData, SecondaryData}
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.PrimaryDataWithApparentPower
+import edu.ie3.simona.agent.participant.data.Data.{PrimaryData, SecondaryData}
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.event.notifier.NotifierConfig
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.FlexResponse
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.{ActorRef => ClassicActorRef}
import java.time.ZonedDateTime
@@ -53,6 +55,8 @@ object ParticipantStateData {
* power requests for the same tick are considered to be different
* @param outputConfig
* Config for the output behaviour of simulation results
+ * @param maybeEmAgent
+ * The EmAgent if this participant is em-controlled
* @tparam I
* Type of input model to carry
* @tparam C
@@ -63,18 +67,17 @@ object ParticipantStateData {
final case class ParticipantInitializingStateData[
I <: SystemParticipantInput,
C <: SimonaConfig.BaseRuntimeConfig,
- PD <: PrimaryData
+ PD <: PrimaryData,
](
inputModel: InputModelContainer[I],
modelConfig: C,
- secondaryDataServices: Option[
- Vector[SecondaryDataService[_ <: SecondaryData]]
- ],
+ secondaryDataServices: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[ActorRef[FlexResponse]],
) extends ParticipantStateData[PD]
/** State data to use, when initializing the participant agent
@@ -96,6 +99,8 @@ object ParticipantStateData {
* power requests for the same tick are considered to be different
* @param outputConfig
* Config for the output behaviour of simulation results
+ * @param maybeEmAgent
+ * The EmAgent if this participant is em-controlled
* @tparam I
* Type of input model to carry
* @tparam C
@@ -106,38 +111,68 @@ object ParticipantStateData {
final case class ParticipantInitializeStateData[
I <: SystemParticipantInput,
C <: SimonaConfig.BaseRuntimeConfig,
- PD <: PrimaryData
+ PD <: PrimaryData,
](
inputModel: InputModelContainer[I],
modelConfig: C,
- primaryServiceProxy: ActorRef,
- secondaryDataServices: Option[
- Vector[SecondaryDataService[_ <: SecondaryData]]
- ],
+ primaryServiceProxy: ClassicActorRef,
+ secondaryDataServices: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[ActorRef[FlexResponse]] = None,
) extends InitializeStateData[PD]
object ParticipantInitializeStateData {
+
+ def apply[
+ I <: SystemParticipantInput,
+ C <: SimonaConfig.BaseRuntimeConfig,
+ PD <: PrimaryData,
+ ](
+ inputModel: I,
+ modelConfig: C,
+ primaryServiceProxy: ClassicActorRef,
+ secondaryDataServices: Iterable[
+ SecondaryDataService[_ <: SecondaryData]
+ ],
+ simulationStartDate: ZonedDateTime,
+ simulationEndDate: ZonedDateTime,
+ resolution: Long,
+ requestVoltageDeviationThreshold: Double,
+ outputConfig: NotifierConfig,
+ ): ParticipantInitializeStateData[I, C, PD] =
+ new ParticipantInitializeStateData[I, C, PD](
+ SimpleInputContainer(inputModel),
+ modelConfig,
+ primaryServiceProxy,
+ secondaryDataServices,
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ maybeEmAgent = None,
+ )
def apply[
I <: SystemParticipantInput,
C <: SimonaConfig.BaseRuntimeConfig,
- PD <: PrimaryData
+ PD <: PrimaryData,
](
inputModel: I,
modelConfig: C,
- primaryServiceProxy: ActorRef,
- secondaryDataServices: Option[
- Vector[SecondaryDataService[_ <: SecondaryData]]
+ primaryServiceProxy: ClassicActorRef,
+ secondaryDataServices: Iterable[
+ SecondaryDataService[_ <: SecondaryData]
],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[ActorRef[FlexResponse]],
): ParticipantInitializeStateData[I, C, PD] =
new ParticipantInitializeStateData[I, C, PD](
SimpleInputContainer(inputModel),
@@ -148,26 +183,59 @@ object ParticipantStateData {
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
+ )
+
+ def apply[
+ I <: SystemParticipantInput,
+ C <: SimonaConfig.BaseRuntimeConfig,
+ PD <: PrimaryData,
+ ](
+ inputModel: I,
+ thermalGrid: ThermalGrid,
+ modelConfig: C,
+ primaryServiceProxy: ClassicActorRef,
+ secondaryDataServices: Iterable[
+ SecondaryDataService[_ <: SecondaryData]
+ ],
+ simulationStartDate: ZonedDateTime,
+ simulationEndDate: ZonedDateTime,
+ resolution: Long,
+ requestVoltageDeviationThreshold: Double,
+ outputConfig: NotifierConfig,
+ ): ParticipantInitializeStateData[I, C, PD] =
+ new ParticipantInitializeStateData[I, C, PD](
+ WithHeatInputContainer(inputModel, thermalGrid),
+ modelConfig,
+ primaryServiceProxy,
+ secondaryDataServices,
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ maybeEmAgent = None,
)
def apply[
I <: SystemParticipantInput,
C <: SimonaConfig.BaseRuntimeConfig,
- PD <: PrimaryData
+ PD <: PrimaryData,
](
inputModel: I,
thermalGrid: ThermalGrid,
modelConfig: C,
- primaryServiceProxy: ActorRef,
- secondaryDataServices: Option[
- Vector[SecondaryDataService[_ <: SecondaryData]]
+ primaryServiceProxy: ClassicActorRef,
+ secondaryDataServices: Iterable[
+ SecondaryDataService[_ <: SecondaryData]
],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[ActorRef[FlexResponse]],
): ParticipantInitializeStateData[I, C, PD] =
new ParticipantInitializeStateData[I, C, PD](
WithHeatInputContainer(inputModel, thermalGrid),
@@ -178,7 +246,8 @@ object ParticipantStateData {
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
)
}
@@ -199,8 +268,8 @@ object ParticipantStateData {
+PD <: PrimaryDataWithApparentPower[PD]
](
baseStateData: BaseStateData[PD],
- pendingResponses: Vector[ActorRef],
- foreseenNextDataTicks: Map[ActorRef, Long] = Map.empty
+ pendingResponses: Iterable[ClassicActorRef],
+ foreseenNextDataTicks: Map[ClassicActorRef, Long] = Map.empty,
) extends ParticipantStateData[PD]
sealed trait InputModelContainer[+I <: SystemParticipantInput] {
@@ -213,6 +282,6 @@ object ParticipantStateData {
final case class WithHeatInputContainer[+I <: SystemParticipantInput](
override val electricalInputModel: I,
- thermalGrid: ThermalGrid
+ thermalGrid: ThermalGrid,
) extends InputModelContainer[I]
}
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgent.scala b/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgent.scala
index 94a5c490a0..957d48a28f 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgent.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgent.scala
@@ -6,17 +6,18 @@
package edu.ie3.simona.agent.participant.wec
-import org.apache.pekko.actor.{ActorRef, Props}
import edu.ie3.datamodel.models.input.system.WecInput
-import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.agent.participant.ParticipantAgent
+import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorWeatherService
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData
import edu.ie3.simona.config.SimonaConfig.WecRuntimeConfig
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.WecModel
import edu.ie3.simona.model.participant.WecModel._
+import org.apache.pekko.actor.{ActorRef, Props}
object WecAgent {
def props(
@@ -24,15 +25,15 @@ object WecAgent {
initStateData: ParticipantInitializeStateData[
WecInput,
WecRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- listener: Iterable[ActorRef]
+ listener: Iterable[ActorRef],
): Props =
Props(
new WecAgent(
scheduler,
initStateData,
- listener
+ listener,
)
)
@@ -53,16 +54,17 @@ class WecAgent(
initStateData: ParticipantInitializeStateData[
WecInput,
WecRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef]
+ override val listener: Iterable[ActorRef],
) extends ParticipantAgent[
ApparentPower,
WecRelevantData,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
WecInput,
WecRuntimeConfig,
- WecModel
+ WecModel,
](scheduler, initStateData)
with WecAgentFundamentals {
diff --git a/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgentFundamentals.scala b/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgentFundamentals.scala
index bbb9e55c6c..955131bafc 100644
--- a/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgentFundamentals.scala
+++ b/src/main/scala/edu/ie3/simona/agent/participant/wec/WecAgentFundamentals.scala
@@ -6,27 +6,23 @@
package edu.ie3.simona.agent.participant.wec
-import org.apache.pekko.actor.{ActorRef, FSM}
import edu.ie3.datamodel.models.input.system.WecInput
import edu.ie3.datamodel.models.result.system.{
SystemParticipantResult,
- WecResult
+ WecResult,
}
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.ParticipantAgent._
import edu.ie3.simona.agent.participant.ParticipantAgentFundamentals
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- ZERO_POWER
+ ZERO_POWER,
}
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.statedata.BaseStateData._
+import edu.ie3.simona.agent.participant.statedata.ParticipantStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.InputModelContainer
-import edu.ie3.simona.agent.participant.statedata.{
- DataCollectionStateData,
- ParticipantStateData
-}
import edu.ie3.simona.agent.participant.wec.WecAgent.neededServices
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
@@ -35,15 +31,27 @@ import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.{
AgentInitializationException,
InconsistentStateException,
- InvalidRequestException
+ InvalidRequestException,
}
import edu.ie3.simona.io.result.AccompaniedSimulationResult
-import edu.ie3.simona.model.participant.WecModel
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.WecModel.WecRelevantData
+import edu.ie3.simona.model.participant.{
+ FlexChangeIndicator,
+ ModelState,
+ WecModel,
+}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ FlexRequest,
+ FlexResponse,
+}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
import edu.ie3.util.quantities.PowerSystemUnits._
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
+import org.apache.pekko.actor.{ActorRef, FSM}
import squants.{Dimensionless, Each, Power}
import java.time.ZonedDateTime
@@ -55,10 +63,11 @@ protected trait WecAgentFundamentals
extends ParticipantAgentFundamentals[
ApparentPower,
WecRelevantData,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
WecInput,
WecRuntimeConfig,
- WecModel
+ WecModel,
] {
this: WecAgent =>
override protected val pdClassTag: ClassTag[ApparentPower] =
@@ -73,7 +82,7 @@ protected trait WecAgentFundamentals
* @param modelConfig
* Configuration of the model
* @param services
- * Optional collection of services to register with
+ * Collection of services to register with
* @param simulationStartDate
* Real world time date time, when the simulation starts
* @param simulationEndDate
@@ -92,19 +101,21 @@ protected trait WecAgentFundamentals
override def determineModelBaseStateData(
inputModel: InputModelContainer[WecInput],
modelConfig: WecRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
- ): ParticipantModelBaseStateData[ApparentPower, WecRelevantData, WecModel] = {
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
+ ): ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ] = {
/* Check for needed services */
- if (
- !services.exists(serviceDefinitions =>
- serviceDefinitions.map(_.getClass).containsSlice(neededServices)
- )
- )
+ if (!services.toSeq.map(_.getClass).containsSlice(neededServices))
throw new AgentInitializationException(
s"$actorName cannot be initialized without a weather service!"
)
@@ -115,10 +126,15 @@ protected trait WecAgentFundamentals
inputModel,
modelConfig,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
- ParticipantModelBaseStateData[ApparentPower, WecRelevantData, WecModel](
+ ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ](
simulationStartDate,
simulationEndDate,
model,
@@ -128,18 +144,20 @@ protected trait WecAgentFundamentals
Map.empty,
requestVoltageDeviationThreshold,
ValueStore.forVoltage(
- resolution * 10,
+ resolution,
Each(
inputModel.electricalInputModel.getNode
.getvTarget()
.to(PU)
.getValue
- .doubleValue()
- )
+ .doubleValue
+ ),
),
- ValueStore.forResult(resolution, 10),
- ValueStore(resolution * 10),
- ValueStore(resolution * 10)
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ ValueStore(resolution),
+ maybeEmAgent.map(FlexControlledData(_, self.toTyped[FlexRequest])),
)
}
@@ -147,31 +165,128 @@ protected trait WecAgentFundamentals
inputModel: InputModelContainer[WecInput],
modelConfig: WecRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): WecModel = WecModel(
inputModel.electricalInputModel,
modelConfig.scaling,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
+ override protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ]
+ ): ModelState.ConstantState.type =
+ ConstantState
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ],
+ tick: Long,
+ ): WecRelevantData = {
+ // take the last weather data, not necessarily the one for the current tick:
+ // we might receive flex control messages for irregular ticks
+ val (_, secondaryData) = baseStateData.receivedSecondaryDataStore
+ .last(tick)
+ .getOrElse(
+ throw new InconsistentStateException(
+ s"The model ${baseStateData.model} was not provided with any secondary data so far."
+ )
+ )
+
+ val weatherData =
+ secondaryData
+ .collectFirst {
+ // filter secondary data for weather data
+ case (_, data: WeatherData) => data
+ }
+ .getOrElse(
+ throw new InconsistentStateException(
+ s"The model ${baseStateData.model} was not provided with needed weather data."
+ )
+ )
+
+ WecRelevantData(
+ weatherData.windVel,
+ weatherData.temp,
+ None,
+ )
+ }
+
+ /** Handle an active power change by flex control.
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ],
+ data: WecRelevantData,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, ApparentPower, FlexChangeIndicator) = {
+ /* Calculate result */
+ val voltage = getAndCheckNodalVoltage(baseStateData, tick)
+
+ val reactivePower = baseStateData.model.calculateReactivePower(
+ setPower,
+ voltage,
+ )
+ val result = ApparentPower(setPower, reactivePower)
+
+ /* Handle the request within the model */
+ val (updatedState, flexChangeIndicator) =
+ baseStateData.model.handleControlledPowerChange(data, lastState, setPower)
+ (updatedState, result, flexChangeIndicator)
+ }
+
/** Partial function, that is able to transfer
* [[ParticipantModelBaseStateData]] (holding the actual calculation model)
* into a pair of active and reactive power
*/
override val calculateModelPowerFunc: (
Long,
- ParticipantModelBaseStateData[ApparentPower, WecRelevantData, WecModel],
- Dimensionless
+ ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ],
+ ConstantState.type,
+ Dimensionless,
) => ApparentPower =
(
_: Long,
_: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
],
- _: Dimensionless
+ _,
+ _: Dimensionless,
) =>
throw new InvalidRequestException(
"WEC model cannot be run without secondary data."
@@ -187,8 +302,10 @@ protected trait WecAgentFundamentals
* [[edu.ie3.simona.ontology.messages.SchedulerMessage.Completion]] to
* scheduler and using update result values.
*
- * @param collectionStateData
- * State data with collected, comprehensive secondary data.
+ * @param baseStateData
+ * The base state data with collected secondary data
+ * @param lastModelState
+ * Optional last model state
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -197,64 +314,37 @@ protected trait WecAgentFundamentals
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPower],
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ WecRelevantData,
+ ConstantState.type,
+ WecModel,
+ ],
+ lastModelState: ConstantState.type,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] = {
- implicit val startDateTime: ZonedDateTime =
- collectionStateData.baseStateData.startDate
-
val voltage =
- getAndCheckNodalVoltage(collectionStateData.baseStateData, currentTick)
+ getAndCheckNodalVoltage(baseStateData, currentTick)
- val (result, relevantData) =
- collectionStateData.baseStateData match {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.model match {
- case wecModel: WecModel =>
- /* extract weather data from secondary data, which should have been requested and received before */
- val weatherData =
- collectionStateData.data
- .collectFirst {
- // filter secondary data for weather data
- case (_, Some(data: WeatherData)) => data
- }
- .getOrElse(
- throw new InconsistentStateException(
- s"The model ${modelBaseStateData.model} was not provided with needed weather data."
- )
- )
-
- val relevantData =
- WecRelevantData(
- weatherData.windVel,
- weatherData.temp,
- None // weather data does not support air pressure
- )
-
- val power = wecModel.calculatePower(
- currentTick,
- voltage,
- relevantData
- )
+ val relevantData =
+ createCalcRelevantData(
+ baseStateData,
+ currentTick,
+ )
- (power, relevantData)
- case unsupportedModel =>
- throw new InconsistentStateException(
- s"Wrong model: $unsupportedModel!"
- )
- }
- case _ =>
- throw new InconsistentStateException(
- "Cannot find a model for model calculation."
- )
- }
+ val result = baseStateData.model.calculatePower(
+ currentTick,
+ voltage,
+ ConstantState,
+ relevantData,
+ )
updateValueStoresInformListenersAndGoToIdleWithUpdatedBaseStateData(
scheduler,
- collectionStateData.baseStateData,
+ baseStateData,
AccompaniedSimulationResult(result),
- relevantData
+ relevantData,
)
}
@@ -277,14 +367,14 @@ protected trait WecAgentFundamentals
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
Power => ReactivePower
- ] = None
+ ] = None,
): ApparentPower =
ParticipantAgentFundamentals.averageApparentPower(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
/** Determines the correct result.
@@ -301,12 +391,36 @@ protected trait WecAgentFundamentals
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPower
+ result: ApparentPower,
): SystemParticipantResult =
new WecResult(
dateTime,
uuid,
result.p.toMegawatts.asMegaWatt,
- result.q.toMegavars.asMegaVar
+ result.q.toMegavars.asMegaVar,
)
+
+ /** Update the last known model state with the given external, relevant data
+ *
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
+ */
+ override protected def updateState(
+ tick: Long,
+ modelState: ModelState.ConstantState.type,
+ calcRelevantData: WecRelevantData,
+ nodalVoltage: squants.Dimensionless,
+ model: WecModel,
+ ): ModelState.ConstantState.type = modelState
}
diff --git a/src/main/scala/edu/ie3/simona/agent/state/AgentState.scala b/src/main/scala/edu/ie3/simona/agent/state/AgentState.scala
index 0261a7a210..fc4497b77b 100644
--- a/src/main/scala/edu/ie3/simona/agent/state/AgentState.scala
+++ b/src/main/scala/edu/ie3/simona/agent/state/AgentState.scala
@@ -19,7 +19,7 @@ object AgentState {
final case class TerminatedPrematurelyEvent(
actorRef: ActorRef,
reason: FSM.Reason,
- tick: Option[Int]
+ tick: Option[Int],
)
case object Uninitialized extends AgentState
diff --git a/src/main/scala/edu/ie3/simona/agent/state/GridAgentState.scala b/src/main/scala/edu/ie3/simona/agent/state/GridAgentState.scala
deleted file mode 100644
index f602b4dabe..0000000000
--- a/src/main/scala/edu/ie3/simona/agent/state/GridAgentState.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * © 2020. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.agent.state
-
-sealed trait GridAgentState extends AgentState
-
-object GridAgentState {
-
- case object Initializing extends GridAgentState
-
- case object SimulateGrid extends GridAgentState
-
- case object CheckPowerDifferences extends GridAgentState
-
- case object HandlePowerFlowCalculations extends GridAgentState
-
-}
diff --git a/src/main/scala/edu/ie3/simona/api/ExtSimAdapter.scala b/src/main/scala/edu/ie3/simona/api/ExtSimAdapter.scala
index bce029ea33..b71adf4a30 100644
--- a/src/main/scala/edu/ie3/simona/api/ExtSimAdapter.scala
+++ b/src/main/scala/edu/ie3/simona/api/ExtSimAdapter.scala
@@ -8,22 +8,22 @@ package edu.ie3.simona.api
import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
import org.apache.pekko.actor.{Actor, ActorRef, PoisonPill, Props}
-import edu.ie3.simona.api.ExtSimAdapter.{Create, ExtSimAdapterStateData}
+import edu.ie3.simona.api.ExtSimAdapter.{Create, ExtSimAdapterStateData, Stop}
import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.api.simulation.ontology.{
ActivationMessage,
TerminationCompleted,
TerminationMessage,
- CompletionMessage => ExtCompletionMessage
+ CompletionMessage => ExtCompletionMessage,
}
import edu.ie3.simona.logging.SimonaActorLogging
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.ScheduleServiceActivation
-import edu.ie3.simona.ontology.messages.{Activation, StopMessage}
+import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
@@ -45,9 +45,11 @@ object ExtSimAdapter {
*/
final case class Create(extSimData: ExtSimAdapterData, unlockKey: ScheduleKey)
+ final case class Stop(simulationSuccessful: Boolean)
+
final case class ExtSimAdapterStateData(
extSimData: ExtSimAdapterData,
- currentTick: Option[Long] = None
+ currentTick: Option[Long] = None,
)
}
@@ -59,7 +61,7 @@ final case class ExtSimAdapter(scheduler: ActorRef)
scheduler ! ScheduleActivation(
self.toTyped,
INIT_SIM_TICK,
- Some(unlockKey)
+ Some(unlockKey),
)
context become receiveIdle(
ExtSimAdapterStateData(extSimAdapterData)
@@ -75,7 +77,7 @@ final case class ExtSimAdapter(scheduler: ActorRef)
)
log.debug(
"Tick {} has been activated in external simulation",
- tick
+ tick,
)
context become receiveIdle(
@@ -91,7 +93,7 @@ final case class ExtSimAdapter(scheduler: ActorRef)
scheduler ! Completion(self.toTyped, newTick)
log.debug(
"Tick {} has been completed in external simulation",
- stateData.currentTick
+ stateData.currentTick,
)
context become receiveIdle(stateData.copy(currentTick = None))
@@ -104,10 +106,10 @@ final case class ExtSimAdapter(scheduler: ActorRef)
scheduleDataService.getDataService ! ScheduleServiceActivation(
tick,
- key
+ key,
)
- case StopMessage(simulationSuccessful) =>
+ case Stop(simulationSuccessful) =>
// let external sim know that we have terminated
stateData.extSimData.queueExtMsg(
new TerminationMessage(simulationSuccessful)
diff --git a/src/main/scala/edu/ie3/simona/config/ArgsParser.scala b/src/main/scala/edu/ie3/simona/config/ArgsParser.scala
index 65c563bd80..183aa8149c 100644
--- a/src/main/scala/edu/ie3/simona/config/ArgsParser.scala
+++ b/src/main/scala/edu/ie3/simona/config/ArgsParser.scala
@@ -30,7 +30,7 @@ object ArgsParser extends LazyLogging {
nodePort: Option[String] = None,
seedAddress: Option[String] = None,
useLocalWorker: Option[Boolean] = None,
- tArgs: Map[String, String] = Map.empty
+ tArgs: Map[String, String] = Map.empty,
) {
val useCluster: Boolean = clusterType.isDefined
}
@@ -42,18 +42,13 @@ object ArgsParser extends LazyLogging {
.action((value, args) => {
args.copy(
config = Some(parseTypesafeConfig(value)),
- configLocation = Option(value)
+ configLocation = Option(value),
)
})
.validate(value =>
if (value.trim.isEmpty) failure("config location cannot be empty")
else success
)
- .validate(value =>
- if (value.contains("\\"))
- failure("wrong config path, expected: /, found: \\")
- else success
- )
.text("Location of the simona config file")
.minOccurs(1)
opt[Map[String, String]]("tArgs")
@@ -127,7 +122,7 @@ object ArgsParser extends LazyLogging {
private def parse(
parser: scoptOptionParser[Arguments],
- args: Array[String]
+ args: Array[String],
): Option[Arguments] =
parser.parse(args, init = Arguments(args))
@@ -174,32 +169,28 @@ object ArgsParser extends LazyLogging {
def parseListenerConfigOption(
listenerConfigOption: Option[List[SimonaConfig.Simona.Event.Listener$Elm]]
): Map[SimonaListenerCompanion, Option[List[String]]] = {
- val clusterSingletonsWithEvents
- : Map[SimonaListenerCompanion, Option[List[String]]] =
- listenerConfigOption match {
- case Some(listenerElems) =>
- listenerElems.foldLeft(
- Map.empty[SimonaListenerCompanion, Option[List[String]]]
- )((listenerMap, listenerElem) =>
- ReflectionTools
- .resolveClassNameToCompanion(listenerElem.fullClassPath) match {
- case Some(listener: SimonaListenerCompanion) =>
- listenerMap + (listener -> listenerElem.eventsToProcess)
- case nonListenerCompanion =>
- logger.warn(
- s"Invalid value ${nonListenerCompanion.getClass} for 'event.listener' config parameter!"
- )
- listenerMap
- }
- )
- case None =>
- logger.info(
- "No listener assigned in configuration value 'event.listener'. No event are going to be processed!"
- )
+ listenerConfigOption match {
+ case Some(listenerElems) =>
+ listenerElems.foldLeft(
Map.empty[SimonaListenerCompanion, Option[List[String]]]
- }
-
- clusterSingletonsWithEvents
+ )((listenerMap, listenerElem) =>
+ ReflectionTools
+ .resolveClassNameToCompanion(listenerElem.fullClassPath) match {
+ case Some(listener: SimonaListenerCompanion) =>
+ listenerMap + (listener -> listenerElem.eventsToProcess)
+ case nonListenerCompanion =>
+ logger.warn(
+ s"Invalid value ${nonListenerCompanion.getClass} for 'event.listener' config parameter!"
+ )
+ listenerMap
+ }
+ )
+ case None =>
+ logger.info(
+ "No listener assigned in configuration value 'event.listener'. No event are going to be processed!"
+ )
+ Map.empty[SimonaListenerCompanion, Option[List[String]]]
+ }
}
/** Prepare the config by parsing the provided program arguments
@@ -232,7 +223,7 @@ object ArgsParser extends LazyLogging {
val argsConfig =
ConfigFactory.parseString(
- s"""config = ${parsedArgs.configLocation.get}
+ s"""config = "${parsedArgs.configLocation.get.replace("\\", "\\\\")}"
|simona.runtime_configuration {
| selected_subnets = [${parsedArgs.selectedSubnets.getOrElse("")}]
| selected_volt_lvls = [${parsedArgs.selectedVoltLvls
diff --git a/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala b/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala
index ab3b3895a9..09f2bf62ae 100644
--- a/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala
+++ b/src/main/scala/edu/ie3/simona/config/ConfigFailFast.scala
@@ -12,7 +12,9 @@ import edu.ie3.simona.config.SimonaConfig.Simona.Output.Sink.InfluxDb1x
import edu.ie3.simona.config.SimonaConfig.{
BaseOutputConfig,
RefSystemConfig,
- ResultKafkaParams
+ ResultKafkaParams,
+ Simona,
+ TransformerControlGroup,
}
import edu.ie3.simona.exceptions.InvalidConfigParameterException
import edu.ie3.simona.io.result.ResultSinkType
@@ -22,7 +24,7 @@ import edu.ie3.simona.service.weather.WeatherSource
import edu.ie3.simona.util.CollectionUtils
import edu.ie3.simona.util.ConfigUtil.DatabaseConfigUtil.{
checkInfluxDb1xParams,
- checkKafkaParams
+ checkKafkaParams,
}
import edu.ie3.simona.util.ConfigUtil.{CsvConfigUtil, NotifierIdentifier}
import edu.ie3.util.scala.ReflectionTools
@@ -74,7 +76,7 @@ case object ConfigFailFast extends LazyLogging {
case Failure(exception) =>
throw new InvalidConfigParameterException(
"Checking of pekko config failed due to unhandled error.",
- exception
+ exception,
)
case Success(_) =>
}
@@ -94,7 +96,7 @@ case object ConfigFailFast extends LazyLogging {
case Failure(exception) =>
throw new InvalidConfigParameterException(
"Checking of pekko logging config failed due to unhandled error.",
- exception
+ exception,
)
case _ =>
}
@@ -143,6 +145,9 @@ case object ConfigFailFast extends LazyLogging {
/* Check power flow resolution configuration */
checkPowerFlowResolutionConfiguration(simonaConfig.simona.powerflow)
+
+ /* Check control scheme definitions */
+ simonaConfig.simona.control.foreach(checkControlSchemes)
}
/** Checks for valid sink configuration
@@ -191,7 +196,7 @@ case object ConfigFailFast extends LazyLogging {
checkInfluxDb1xParams(
"Sink",
ResultSinkType.buildInfluxDb1xUrl(influxDb1x),
- influxDb1x.database
+ influxDb1x.database,
)
case Some(Some(kafka: ResultKafkaParams)) =>
checkKafkaParams(kafka, Seq(kafka.topicNodeRes))
@@ -235,7 +240,7 @@ case object ConfigFailFast extends LazyLogging {
throw new InvalidConfigParameterException(
s"Invalid dateTimeString: $dateTimeString." +
s"Please ensure that your date/time parameter match the following pattern: 'yyyy-MM-dd HH:mm:ss'",
- e
+ e,
)
}
}
@@ -256,27 +261,27 @@ case object ConfigFailFast extends LazyLogging {
/* Check basic model configuration parameters common to each participant */
checkBaseRuntimeConfigs(
subConfig.load.defaultConfig,
- subConfig.load.individualConfigs
+ subConfig.load.individualConfigs,
)
checkBaseRuntimeConfigs(
subConfig.fixedFeedIn.defaultConfig,
- subConfig.fixedFeedIn.individualConfigs
+ subConfig.fixedFeedIn.individualConfigs,
)
checkBaseRuntimeConfigs(
subConfig.evcs.defaultConfig,
- subConfig.evcs.individualConfigs
+ subConfig.evcs.individualConfigs,
)
checkBaseRuntimeConfigs(
subConfig.pv.defaultConfig,
- subConfig.pv.individualConfigs
+ subConfig.pv.individualConfigs,
)
checkBaseRuntimeConfigs(
subConfig.wec.defaultConfig,
- subConfig.wec.individualConfigs
+ subConfig.wec.individualConfigs,
)
/* check model configuration parameters specific to participants */
@@ -304,7 +309,7 @@ case object ConfigFailFast extends LazyLogging {
private def checkBaseRuntimeConfigs(
defaultConfig: SimonaConfig.BaseRuntimeConfig,
individualConfigs: List[SimonaConfig.BaseRuntimeConfig],
- defaultString: String = "default"
+ defaultString: String = "default",
): Unit = {
// special default config check
val uuidString = defaultConfig.uuids.mkString(",")
@@ -346,7 +351,7 @@ case object ConfigFailFast extends LazyLogging {
case e: IllegalArgumentException =>
throw new InvalidConfigParameterException(
s"The UUID '$uuid' cannot be parsed as it is invalid.",
- e
+ e,
)
}
)
@@ -369,7 +374,7 @@ case object ConfigFailFast extends LazyLogging {
*/
private def checkSingleString(
singleString: String,
- uuids: List[String]
+ uuids: List[String],
): Unit = {
if (uuids.toVector.size != 1)
throw new InvalidConfigParameterException(
@@ -388,7 +393,7 @@ case object ConfigFailFast extends LazyLogging {
case e: IllegalArgumentException =>
throw new InvalidConfigParameterException(
s"Found invalid UUID '$singleEntry' it was meant to be the string '$singleString' or a valid UUID.",
- e
+ e,
)
}
case None =>
@@ -449,7 +454,7 @@ case object ConfigFailFast extends LazyLogging {
case Failure(exception) =>
throw new InvalidConfigParameterException(
s"The given nominal voltage '${voltLvl.vNom}' cannot be parsed to a quantity. Did you provide the volt level with it's unit (e.g. \"20 kV\")?",
- exception
+ exception,
)
}
}
@@ -548,7 +553,7 @@ case object ConfigFailFast extends LazyLogging {
checkDefaultBaseOutputConfig(
subConfig.defaultConfig,
- defaultString = "default"
+ defaultString = "default",
)
checkIndividualParticipantsOutputConfigs(subConfig.individualConfigs)
}
@@ -583,6 +588,61 @@ case object ConfigFailFast extends LazyLogging {
}
}
+ /** Check the validity of control scheme definitions
+ *
+ * @param control
+ * Control scheme definitions
+ */
+ private def checkControlSchemes(control: Simona.Control): Unit = {
+ control.transformer.foreach(checkTransformerControl)
+ }
+
+ /** Check the suitability of transformer control group definition.
+ *
+ * One important check cannot be performed at this place, as input data is
+ * not available, yet: Do the measurements belong to a region, that can be
+ * influenced by the transformer? This is partly addressed in
+ * [[edu.ie3.simona.agent.grid.GridAgentFailFast]]
+ *
+ * @param transformerControlGroup
+ * Transformer control group definition
+ */
+ private def checkTransformerControl(
+ transformerControlGroup: TransformerControlGroup
+ ): Unit = {
+ val lowerBoundary = 0.8
+ val upperBoundary = 1.2
+ transformerControlGroup match {
+ case TransformerControlGroup(measurements, transformers, vMax, vMin) =>
+ if (measurements.isEmpty)
+ throw new InvalidConfigParameterException(
+ s"A transformer control group (${transformerControlGroup.toString}) cannot have no measurements assigned."
+ )
+ if (transformers.isEmpty)
+ throw new InvalidConfigParameterException(
+ s"A transformer control group (${transformerControlGroup.toString}) cannot have no transformers assigned."
+ )
+ if (vMin < 0)
+ throw new InvalidConfigParameterException(
+ "The minimum permissible voltage magnitude of a transformer control group has to be positive."
+ )
+ if (vMax < vMin)
+ throw new InvalidConfigParameterException(
+ s"The minimum permissible voltage magnitude of a transformer control group (${transformerControlGroup.toString}) must be smaller than the maximum permissible voltage magnitude."
+ )
+ if (vMin < lowerBoundary)
+ throw new InvalidConfigParameterException(
+ s"A control group (${transformerControlGroup.toString}) which control boundaries exceed the limit of +- 20% of nominal voltage! This may be caused " +
+ s"by invalid parametrization of one control groups where vMin is lower than the lower boundary (0.8 of nominal Voltage)!"
+ )
+ if (vMax > upperBoundary)
+ throw new InvalidConfigParameterException(
+ s"A control group (${transformerControlGroup.toString}) which control boundaries exceed the limit of +- 20% of nominal voltage! This may be caused " +
+ s"by invalid parametrization of one control groups where vMax is higher than the upper boundary (1.2 of nominal Voltage)!"
+ )
+ }
+ }
+
/** Check the default config
*
* @param config
@@ -592,7 +652,7 @@ case object ConfigFailFast extends LazyLogging {
*/
private def checkDefaultBaseOutputConfig(
config: SimonaConfig.BaseOutputConfig,
- defaultString: String
+ defaultString: String,
): Unit = {
if (
StringUtils
@@ -649,7 +709,7 @@ case object ConfigFailFast extends LazyLogging {
case e: NoSuchElementException =>
throw new InvalidConfigParameterException(
s"The identifier '$id' you provided is not valid. Valid input: ${NotifierIdentifier.values.map(_.toString).mkString(",")}",
- e
+ e,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/config/RefSystemParser.scala b/src/main/scala/edu/ie3/simona/config/RefSystemParser.scala
index 66bf1100c1..d299dc16d7 100644
--- a/src/main/scala/edu/ie3/simona/config/RefSystemParser.scala
+++ b/src/main/scala/edu/ie3/simona/config/RefSystemParser.scala
@@ -18,7 +18,7 @@ object RefSystemParser {
final case class ConfigRefSystems(
private val gridIdRefSystems: Map[Int, RefSystem],
- private val voltLvLRefSystems: Map[VoltageLevel, RefSystem]
+ private val voltLvLRefSystems: Map[VoltageLevel, RefSystem],
) {
/** Returns a [[RefSystem]] based on the provided gridId or the voltLvl as
@@ -33,7 +33,7 @@ object RefSystemParser {
*/
def find(
gridId: Int,
- voltLvl: Option[VoltageLevel] = None
+ voltLvl: Option[VoltageLevel] = None,
): Option[RefSystem] =
gridIdRefSystems
.get(gridId)
diff --git a/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala b/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala
index 5072923c7c..f122d05d39 100644
--- a/src/main/scala/edu/ie3/simona/config/SimonaConfig.scala
+++ b/src/main/scala/edu/ie3/simona/config/SimonaConfig.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
*/
@@ -13,26 +13,26 @@ object SimonaConfig {
final case class BaseCsvParams(
override val csvSep: java.lang.String,
override val directoryPath: java.lang.String,
- override val isHierarchic: scala.Boolean
+ override val isHierarchic: scala.Boolean,
) extends CsvParams(csvSep, directoryPath, isHierarchic)
object BaseCsvParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.BaseCsvParams = {
SimonaConfig.BaseCsvParams(
csvSep = $_reqStr(parentPath, c, "csvSep", $tsCfgValidator),
directoryPath =
$_reqStr(parentPath, c, "directoryPath", $tsCfgValidator),
- isHierarchic = $_reqBln(parentPath, c, "isHierarchic", $tsCfgValidator)
+ isHierarchic = $_reqBln(parentPath, c, "isHierarchic", $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -48,7 +48,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -64,52 +64,61 @@ object SimonaConfig {
sealed abstract class BaseOutputConfig(
val notifier: java.lang.String,
- val simulationResult: scala.Boolean
+ val simulationResult: scala.Boolean,
)
sealed abstract class BaseRuntimeConfig(
val calculateMissingReactivePowerWithModel: scala.Boolean,
val scaling: scala.Double,
- val uuids: scala.List[java.lang.String]
+ val uuids: scala.List[java.lang.String],
) extends java.io.Serializable
sealed abstract class CsvParams(
val csvSep: java.lang.String,
val directoryPath: java.lang.String,
- val isHierarchic: scala.Boolean
+ val isHierarchic: scala.Boolean,
)
final case class EvcsRuntimeConfig(
override val calculateMissingReactivePowerWithModel: scala.Boolean,
override val scaling: scala.Double,
- override val uuids: scala.List[java.lang.String]
+ override val uuids: scala.List[java.lang.String],
+ chargingStrategy: java.lang.String,
+ lowestEvSoc: scala.Double,
) extends BaseRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
object EvcsRuntimeConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.EvcsRuntimeConfig = {
SimonaConfig.EvcsRuntimeConfig(
+ chargingStrategy =
+ if (c.hasPathOrNull("chargingStrategy"))
+ c.getString("chargingStrategy")
+ else "maxPower",
+ lowestEvSoc =
+ if (c.hasPathOrNull("lowestEvSoc")) c.getDouble("lowestEvSoc")
+ else 0.2,
calculateMissingReactivePowerWithModel = $_reqBln(
parentPath,
c,
"calculateMissingReactivePowerWithModel",
- $tsCfgValidator
+ $tsCfgValidator,
),
scaling = $_reqDbl(parentPath, c, "scaling", $tsCfgValidator),
- uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator)
+ uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -125,7 +134,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -142,34 +151,34 @@ object SimonaConfig {
final case class FixedFeedInRuntimeConfig(
override val calculateMissingReactivePowerWithModel: scala.Boolean,
override val scaling: scala.Double,
- override val uuids: scala.List[java.lang.String]
+ override val uuids: scala.List[java.lang.String],
) extends BaseRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
object FixedFeedInRuntimeConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.FixedFeedInRuntimeConfig = {
SimonaConfig.FixedFeedInRuntimeConfig(
calculateMissingReactivePowerWithModel = $_reqBln(
parentPath,
c,
"calculateMissingReactivePowerWithModel",
- $tsCfgValidator
+ $tsCfgValidator,
),
scaling = $_reqDbl(parentPath, c, "scaling", $tsCfgValidator),
- uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator)
+ uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -185,7 +194,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -205,13 +214,13 @@ object SimonaConfig {
notifier: java.lang.String,
switches: scala.Boolean,
transformers2w: scala.Boolean,
- transformers3w: scala.Boolean
+ transformers3w: scala.Boolean,
)
object GridOutputConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.GridOutputConfig = {
SimonaConfig.GridOutputConfig(
lines = c.hasPathOrNull("lines") && c.getBoolean("lines"),
@@ -221,14 +230,14 @@ object SimonaConfig {
transformers2w =
c.hasPathOrNull("transformers2w") && c.getBoolean("transformers2w"),
transformers3w =
- c.hasPathOrNull("transformers3w") && c.getBoolean("transformers3w")
+ c.hasPathOrNull("transformers3w") && c.getBoolean("transformers3w"),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -245,34 +254,34 @@ object SimonaConfig {
final case class HpRuntimeConfig(
override val calculateMissingReactivePowerWithModel: scala.Boolean,
override val scaling: scala.Double,
- override val uuids: scala.List[java.lang.String]
+ override val uuids: scala.List[java.lang.String],
) extends BaseRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
object HpRuntimeConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.HpRuntimeConfig = {
SimonaConfig.HpRuntimeConfig(
calculateMissingReactivePowerWithModel = $_reqBln(
parentPath,
c,
"calculateMissingReactivePowerWithModel",
- $tsCfgValidator
+ $tsCfgValidator,
),
scaling = $_reqDbl(parentPath, c, "scaling", $tsCfgValidator),
- uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator)
+ uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -288,7 +297,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -306,7 +315,7 @@ object SimonaConfig {
val bootstrapServers: java.lang.String,
val linger: scala.Int,
val runId: java.lang.String,
- val schemaRegistryUrl: java.lang.String
+ val schemaRegistryUrl: java.lang.String,
)
final case class LoadRuntimeConfig(
@@ -314,17 +323,17 @@ object SimonaConfig {
override val scaling: scala.Double,
override val uuids: scala.List[java.lang.String],
modelBehaviour: java.lang.String,
- reference: java.lang.String
+ reference: java.lang.String,
) extends BaseRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
object LoadRuntimeConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.LoadRuntimeConfig = {
SimonaConfig.LoadRuntimeConfig(
modelBehaviour =
@@ -334,17 +343,17 @@ object SimonaConfig {
parentPath,
c,
"calculateMissingReactivePowerWithModel",
- $tsCfgValidator
+ $tsCfgValidator,
),
scaling = $_reqDbl(parentPath, c, "scaling", $tsCfgValidator),
- uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator)
+ uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -360,7 +369,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -376,7 +385,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -393,27 +402,30 @@ object SimonaConfig {
final case class ParticipantBaseOutputConfig(
override val notifier: java.lang.String,
override val simulationResult: scala.Boolean,
- powerRequestReply: scala.Boolean
+ flexResult: scala.Boolean,
+ powerRequestReply: scala.Boolean,
) extends BaseOutputConfig(notifier, simulationResult)
object ParticipantBaseOutputConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.ParticipantBaseOutputConfig = {
SimonaConfig.ParticipantBaseOutputConfig(
+ flexResult =
+ c.hasPathOrNull("flexResult") && c.getBoolean("flexResult"),
powerRequestReply =
$_reqBln(parentPath, c, "powerRequestReply", $tsCfgValidator),
notifier = $_reqStr(parentPath, c, "notifier", $tsCfgValidator),
simulationResult =
- $_reqBln(parentPath, c, "simulationResult", $tsCfgValidator)
+ $_reqBln(parentPath, c, "simulationResult", $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -429,7 +441,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -447,13 +459,13 @@ object SimonaConfig {
override val csvSep: java.lang.String,
override val directoryPath: java.lang.String,
override val isHierarchic: scala.Boolean,
- timePattern: java.lang.String
+ timePattern: java.lang.String,
) extends CsvParams(csvSep, directoryPath, isHierarchic)
object PrimaryDataCsvParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.PrimaryDataCsvParams = {
SimonaConfig.PrimaryDataCsvParams(
timePattern =
@@ -462,14 +474,14 @@ object SimonaConfig {
csvSep = $_reqStr(parentPath, c, "csvSep", $tsCfgValidator),
directoryPath =
$_reqStr(parentPath, c, "directoryPath", $tsCfgValidator),
- isHierarchic = $_reqBln(parentPath, c, "isHierarchic", $tsCfgValidator)
+ isHierarchic = $_reqBln(parentPath, c, "isHierarchic", $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -485,7 +497,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -502,34 +514,34 @@ object SimonaConfig {
final case class PvRuntimeConfig(
override val calculateMissingReactivePowerWithModel: scala.Boolean,
override val scaling: scala.Double,
- override val uuids: scala.List[java.lang.String]
+ override val uuids: scala.List[java.lang.String],
) extends BaseRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
object PvRuntimeConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.PvRuntimeConfig = {
SimonaConfig.PvRuntimeConfig(
calculateMissingReactivePowerWithModel = $_reqBln(
parentPath,
c,
"calculateMissingReactivePowerWithModel",
- $tsCfgValidator
+ $tsCfgValidator,
),
scaling = $_reqDbl(parentPath, c, "scaling", $tsCfgValidator),
- uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator)
+ uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -545,7 +557,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -563,13 +575,13 @@ object SimonaConfig {
gridIds: scala.Option[scala.List[java.lang.String]],
sNom: java.lang.String,
vNom: java.lang.String,
- voltLvls: scala.Option[scala.List[SimonaConfig.VoltLvlConfig]]
+ voltLvls: scala.Option[scala.List[SimonaConfig.VoltLvlConfig]],
)
object RefSystemConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.RefSystemConfig = {
SimonaConfig.RefSystemConfig(
gridIds =
@@ -586,16 +598,16 @@ object SimonaConfig {
$_LSimonaConfig_VoltLvlConfig(
c.getList("voltLvls"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
- else None
+ else None,
)
}
private def $_LSimonaConfig_VoltLvlConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.VoltLvlConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -603,7 +615,7 @@ object SimonaConfig {
SimonaConfig.VoltLvlConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -612,7 +624,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -631,13 +643,13 @@ object SimonaConfig {
override val linger: scala.Int,
override val runId: java.lang.String,
override val schemaRegistryUrl: java.lang.String,
- topicNodeRes: java.lang.String
+ topicNodeRes: java.lang.String,
) extends KafkaParams(bootstrapServers, linger, runId, schemaRegistryUrl)
object ResultKafkaParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.ResultKafkaParams = {
SimonaConfig.ResultKafkaParams(
topicNodeRes = $_reqStr(parentPath, c, "topicNodeRes", $tsCfgValidator),
@@ -646,14 +658,14 @@ object SimonaConfig {
linger = $_reqInt(parentPath, c, "linger", $tsCfgValidator),
runId = $_reqStr(parentPath, c, "runId", $tsCfgValidator),
schemaRegistryUrl =
- $_reqStr(parentPath, c, "schemaRegistryUrl", $tsCfgValidator)
+ $_reqStr(parentPath, c, "schemaRegistryUrl", $tsCfgValidator),
)
}
private def $_reqInt(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Int = {
if (c == null) 0
else
@@ -669,7 +681,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -688,13 +700,13 @@ object SimonaConfig {
override val linger: scala.Int,
override val runId: java.lang.String,
override val schemaRegistryUrl: java.lang.String,
- topic: java.lang.String
+ topic: java.lang.String,
) extends KafkaParams(bootstrapServers, linger, runId, schemaRegistryUrl)
object RuntimeKafkaParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.RuntimeKafkaParams = {
SimonaConfig.RuntimeKafkaParams(
topic = $_reqStr(parentPath, c, "topic", $tsCfgValidator),
@@ -703,14 +715,14 @@ object SimonaConfig {
linger = $_reqInt(parentPath, c, "linger", $tsCfgValidator),
runId = $_reqStr(parentPath, c, "runId", $tsCfgValidator),
schemaRegistryUrl =
- $_reqStr(parentPath, c, "schemaRegistryUrl", $tsCfgValidator)
+ $_reqStr(parentPath, c, "schemaRegistryUrl", $tsCfgValidator),
)
}
private def $_reqInt(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Int = {
if (c == null) 0
else
@@ -726,7 +738,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -742,25 +754,25 @@ object SimonaConfig {
final case class SimpleOutputConfig(
override val notifier: java.lang.String,
- override val simulationResult: scala.Boolean
+ override val simulationResult: scala.Boolean,
) extends BaseOutputConfig(notifier, simulationResult)
object SimpleOutputConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.SimpleOutputConfig = {
SimonaConfig.SimpleOutputConfig(
notifier = $_reqStr(parentPath, c, "notifier", $tsCfgValidator),
simulationResult =
- $_reqBln(parentPath, c, "simulationResult", $tsCfgValidator)
+ $_reqBln(parentPath, c, "simulationResult", $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -776,7 +788,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -790,26 +802,65 @@ object SimonaConfig {
}
+ final case class TransformerControlGroup(
+ measurements: scala.List[java.lang.String],
+ transformers: scala.List[java.lang.String],
+ vMax: scala.Double,
+ vMin: scala.Double,
+ )
+ object TransformerControlGroup {
+ def apply(
+ c: com.typesafe.config.Config,
+ parentPath: java.lang.String,
+ $tsCfgValidator: $TsCfgValidator,
+ ): SimonaConfig.TransformerControlGroup = {
+ SimonaConfig.TransformerControlGroup(
+ measurements =
+ $_L$_str(c.getList("measurements"), parentPath, $tsCfgValidator),
+ transformers =
+ $_L$_str(c.getList("transformers"), parentPath, $tsCfgValidator),
+ vMax = $_reqDbl(parentPath, c, "vMax", $tsCfgValidator),
+ vMin = $_reqDbl(parentPath, c, "vMin", $tsCfgValidator),
+ )
+ }
+ private def $_reqDbl(
+ parentPath: java.lang.String,
+ c: com.typesafe.config.Config,
+ path: java.lang.String,
+ $tsCfgValidator: $TsCfgValidator,
+ ): scala.Double = {
+ if (c == null) 0
+ else
+ try c.getDouble(path)
+ catch {
+ case e: com.typesafe.config.ConfigException =>
+ $tsCfgValidator.addBadPath(parentPath + path, e)
+ 0
+ }
+ }
+
+ }
+
final case class VoltLvlConfig(
id: java.lang.String,
- vNom: java.lang.String
+ vNom: java.lang.String,
)
object VoltLvlConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.VoltLvlConfig = {
SimonaConfig.VoltLvlConfig(
id = $_reqStr(parentPath, c, "id", $tsCfgValidator),
- vNom = $_reqStr(parentPath, c, "vNom", $tsCfgValidator)
+ vNom = $_reqStr(parentPath, c, "vNom", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -826,34 +877,34 @@ object SimonaConfig {
final case class WecRuntimeConfig(
override val calculateMissingReactivePowerWithModel: scala.Boolean,
override val scaling: scala.Double,
- override val uuids: scala.List[java.lang.String]
+ override val uuids: scala.List[java.lang.String],
) extends BaseRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
object WecRuntimeConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.WecRuntimeConfig = {
SimonaConfig.WecRuntimeConfig(
calculateMissingReactivePowerWithModel = $_reqBln(
parentPath,
c,
"calculateMissingReactivePowerWithModel",
- $tsCfgValidator
+ $tsCfgValidator,
),
scaling = $_reqDbl(parentPath, c, "scaling", $tsCfgValidator),
- uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator)
+ uuids = $_L$_str(c.getList("uuids"), parentPath, $tsCfgValidator),
)
}
private def $_reqBln(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Boolean = {
if (c == null) false
else
@@ -869,7 +920,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -884,6 +935,7 @@ object SimonaConfig {
}
final case class Simona(
+ control: scala.Option[SimonaConfig.Simona.Control],
event: SimonaConfig.Simona.Event,
gridConfig: SimonaConfig.Simona.GridConfig,
input: SimonaConfig.Simona.Input,
@@ -891,9 +943,44 @@ object SimonaConfig {
powerflow: SimonaConfig.Simona.Powerflow,
runtime: SimonaConfig.Simona.Runtime,
simulationName: java.lang.String,
- time: SimonaConfig.Simona.Time
+ time: SimonaConfig.Simona.Time,
)
object Simona {
+ final case class Control(
+ transformer: scala.List[SimonaConfig.TransformerControlGroup]
+ )
+ object Control {
+ def apply(
+ c: com.typesafe.config.Config,
+ parentPath: java.lang.String,
+ $tsCfgValidator: $TsCfgValidator,
+ ): SimonaConfig.Simona.Control = {
+ SimonaConfig.Simona.Control(
+ transformer = $_LSimonaConfig_TransformerControlGroup(
+ c.getList("transformer"),
+ parentPath,
+ $tsCfgValidator,
+ )
+ )
+ }
+ private def $_LSimonaConfig_TransformerControlGroup(
+ cl: com.typesafe.config.ConfigList,
+ parentPath: java.lang.String,
+ $tsCfgValidator: $TsCfgValidator,
+ ): scala.List[SimonaConfig.TransformerControlGroup] = {
+ import scala.jdk.CollectionConverters._
+ cl.asScala
+ .map(cv =>
+ SimonaConfig.TransformerControlGroup(
+ cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
+ parentPath,
+ $tsCfgValidator,
+ )
+ )
+ .toList
+ }
+ }
+
final case class Event(
listener: scala.Option[
scala.List[SimonaConfig.Simona.Event.Listener$Elm]
@@ -902,13 +989,13 @@ object SimonaConfig {
object Event {
final case class Listener$Elm(
eventsToProcess: scala.Option[scala.List[java.lang.String]],
- fullClassPath: java.lang.String
+ fullClassPath: java.lang.String,
)
object Listener$Elm {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Event.Listener$Elm = {
SimonaConfig.Simona.Event.Listener$Elm(
eventsToProcess =
@@ -917,19 +1004,19 @@ object SimonaConfig {
$_L$_str(
c.getList("eventsToProcess"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
fullClassPath =
- $_reqStr(parentPath, c, "fullClassPath", $tsCfgValidator)
+ $_reqStr(parentPath, c, "fullClassPath", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -946,7 +1033,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Event = {
SimonaConfig.Simona.Event(
listener =
@@ -955,7 +1042,7 @@ object SimonaConfig {
$_LSimonaConfig_Simona_Event_Listener$Elm(
c.getList("listener"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None
@@ -964,7 +1051,7 @@ object SimonaConfig {
private def $_LSimonaConfig_Simona_Event_Listener$Elm(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.Simona.Event.Listener$Elm] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -972,7 +1059,7 @@ object SimonaConfig {
SimonaConfig.Simona.Event.Listener$Elm(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -986,20 +1073,20 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.GridConfig = {
SimonaConfig.Simona.GridConfig(
refSystems = $_LSimonaConfig_RefSystemConfig(
c.getList("refSystems"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
}
private def $_LSimonaConfig_RefSystemConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.RefSystemConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -1007,7 +1094,7 @@ object SimonaConfig {
SimonaConfig.RefSystemConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -1017,7 +1104,7 @@ object SimonaConfig {
final case class Input(
grid: SimonaConfig.Simona.Input.Grid,
primary: SimonaConfig.Simona.Input.Primary,
- weather: SimonaConfig.Simona.Input.Weather
+ weather: SimonaConfig.Simona.Input.Weather,
)
object Input {
final case class Grid(
@@ -1026,13 +1113,13 @@ object SimonaConfig {
object Grid {
final case class Datasource(
csvParams: scala.Option[SimonaConfig.BaseCsvParams],
- id: java.lang.String
+ id: java.lang.String,
)
object Datasource {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Grid.Datasource = {
SimonaConfig.Simona.Input.Grid.Datasource(
csvParams =
@@ -1041,18 +1128,18 @@ object SimonaConfig {
SimonaConfig.BaseCsvParams(
c.getConfig("csvParams"),
parentPath + "csvParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
- id = $_reqStr(parentPath, c, "id", $tsCfgValidator)
+ id = $_reqStr(parentPath, c, "id", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1069,7 +1156,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Grid = {
SimonaConfig.Simona.Input.Grid(
datasource = SimonaConfig.Simona.Input.Grid.Datasource(
@@ -1077,7 +1164,7 @@ object SimonaConfig {
else
com.typesafe.config.ConfigFactory.parseString("datasource{}"),
parentPath + "datasource.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
}
@@ -1091,7 +1178,7 @@ object SimonaConfig {
influxDb1xParams: scala.Option[
SimonaConfig.Simona.Input.Primary.InfluxDb1xParams
],
- sqlParams: scala.Option[SimonaConfig.Simona.Input.Primary.SqlParams]
+ sqlParams: scala.Option[SimonaConfig.Simona.Input.Primary.SqlParams],
)
object Primary {
final case class CouchbaseParams(
@@ -1101,13 +1188,13 @@ object SimonaConfig {
password: java.lang.String,
timePattern: java.lang.String,
url: java.lang.String,
- userName: java.lang.String
+ userName: java.lang.String,
)
object CouchbaseParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Primary.CouchbaseParams = {
SimonaConfig.Simona.Input.Primary.CouchbaseParams(
bucketName =
@@ -1116,7 +1203,7 @@ object SimonaConfig {
parentPath,
c,
"coordinateColumnName",
- $tsCfgValidator
+ $tsCfgValidator,
),
keyPrefix = $_reqStr(parentPath, c, "keyPrefix", $tsCfgValidator),
password = $_reqStr(parentPath, c, "password", $tsCfgValidator),
@@ -1124,14 +1211,14 @@ object SimonaConfig {
if (c.hasPathOrNull("timePattern")) c.getString("timePattern")
else "yyyy-MM-dd'T'HH:mm:ss[.S[S][S]]'Z'",
url = $_reqStr(parentPath, c, "url", $tsCfgValidator),
- userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator)
+ userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1149,13 +1236,13 @@ object SimonaConfig {
database: java.lang.String,
port: scala.Int,
timePattern: java.lang.String,
- url: java.lang.String
+ url: java.lang.String,
)
object InfluxDb1xParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Primary.InfluxDb1xParams = {
SimonaConfig.Simona.Input.Primary.InfluxDb1xParams(
database = $_reqStr(parentPath, c, "database", $tsCfgValidator),
@@ -1163,14 +1250,14 @@ object SimonaConfig {
timePattern =
if (c.hasPathOrNull("timePattern")) c.getString("timePattern")
else "yyyy-MM-dd'T'HH:mm:ss[.S[S][S]]'Z'",
- url = $_reqStr(parentPath, c, "url", $tsCfgValidator)
+ url = $_reqStr(parentPath, c, "url", $tsCfgValidator),
)
}
private def $_reqInt(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Int = {
if (c == null) 0
else
@@ -1186,7 +1273,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1205,13 +1292,13 @@ object SimonaConfig {
password: java.lang.String,
schemaName: java.lang.String,
timePattern: java.lang.String,
- userName: java.lang.String
+ userName: java.lang.String,
)
object SqlParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Primary.SqlParams = {
SimonaConfig.Simona.Input.Primary.SqlParams(
jdbcUrl = $_reqStr(parentPath, c, "jdbcUrl", $tsCfgValidator),
@@ -1222,14 +1309,14 @@ object SimonaConfig {
timePattern =
if (c.hasPathOrNull("timePattern")) c.getString("timePattern")
else "yyyy-MM-dd'T'HH:mm:ss[.S[S][S]]'Z'",
- userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator)
+ userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1246,7 +1333,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Primary = {
SimonaConfig.Simona.Input.Primary(
couchbaseParams =
@@ -1255,7 +1342,7 @@ object SimonaConfig {
SimonaConfig.Simona.Input.Primary.CouchbaseParams(
c.getConfig("couchbaseParams"),
parentPath + "couchbaseParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1265,7 +1352,7 @@ object SimonaConfig {
SimonaConfig.PrimaryDataCsvParams(
c.getConfig("csvParams"),
parentPath + "csvParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1275,7 +1362,7 @@ object SimonaConfig {
SimonaConfig.Simona.Input.Primary.InfluxDb1xParams(
c.getConfig("influxDb1xParams"),
parentPath + "influxDb1xParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1285,10 +1372,10 @@ object SimonaConfig {
SimonaConfig.Simona.Input.Primary.SqlParams(
c.getConfig("sqlParams"),
parentPath + "sqlParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
- else None
+ else None,
)
}
}
@@ -1315,7 +1402,7 @@ object SimonaConfig {
sqlParams: scala.Option[
SimonaConfig.Simona.Input.Weather.Datasource.SqlParams
],
- timestampPattern: scala.Option[java.lang.String]
+ timestampPattern: scala.Option[java.lang.String],
)
object Datasource {
final case class CoordinateSource(
@@ -1326,7 +1413,7 @@ object SimonaConfig {
],
sqlParams: scala.Option[
SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource.SqlParams
- ]
+ ],
)
object CoordinateSource {
final case class SampleParams(
@@ -1336,7 +1423,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource.SampleParams = {
SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource
.SampleParams(
@@ -1350,13 +1437,13 @@ object SimonaConfig {
password: java.lang.String,
schemaName: java.lang.String,
tableName: java.lang.String,
- userName: java.lang.String
+ userName: java.lang.String,
)
object SqlParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource.SqlParams = {
SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource
.SqlParams(
@@ -1371,14 +1458,14 @@ object SimonaConfig {
tableName =
$_reqStr(parentPath, c, "tableName", $tsCfgValidator),
userName =
- $_reqStr(parentPath, c, "userName", $tsCfgValidator)
+ $_reqStr(parentPath, c, "userName", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1395,7 +1482,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource = {
SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource(
csvParams =
@@ -1404,7 +1491,7 @@ object SimonaConfig {
SimonaConfig.BaseCsvParams(
c.getConfig("csvParams"),
parentPath + "csvParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1418,7 +1505,7 @@ object SimonaConfig {
.SampleParams(
c.getConfig("sampleParams"),
parentPath + "sampleParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1429,10 +1516,10 @@ object SimonaConfig {
.SqlParams(
c.getConfig("sqlParams"),
parentPath + "sqlParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
- else None
+ else None,
)
}
}
@@ -1443,13 +1530,13 @@ object SimonaConfig {
keyPrefix: java.lang.String,
password: java.lang.String,
url: java.lang.String,
- userName: java.lang.String
+ userName: java.lang.String,
)
object CouchbaseParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.CouchbaseParams = {
SimonaConfig.Simona.Input.Weather.Datasource.CouchbaseParams(
bucketName =
@@ -1458,20 +1545,20 @@ object SimonaConfig {
parentPath,
c,
"coordinateColumnName",
- $tsCfgValidator
+ $tsCfgValidator,
),
keyPrefix =
$_reqStr(parentPath, c, "keyPrefix", $tsCfgValidator),
password = $_reqStr(parentPath, c, "password", $tsCfgValidator),
url = $_reqStr(parentPath, c, "url", $tsCfgValidator),
- userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator)
+ userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1488,25 +1575,25 @@ object SimonaConfig {
final case class InfluxDb1xParams(
database: java.lang.String,
port: scala.Int,
- url: java.lang.String
+ url: java.lang.String,
)
object InfluxDb1xParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.InfluxDb1xParams = {
SimonaConfig.Simona.Input.Weather.Datasource.InfluxDb1xParams(
database = $_reqStr(parentPath, c, "database", $tsCfgValidator),
port = $_reqInt(parentPath, c, "port", $tsCfgValidator),
- url = $_reqStr(parentPath, c, "url", $tsCfgValidator)
+ url = $_reqStr(parentPath, c, "url", $tsCfgValidator),
)
}
private def $_reqInt(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Int = {
if (c == null) 0
else
@@ -1522,7 +1609,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1543,7 +1630,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.SampleParams = {
SimonaConfig.Simona.Input.Weather.Datasource.SampleParams(
use = !c.hasPathOrNull("use") || c.getBoolean("use")
@@ -1556,13 +1643,13 @@ object SimonaConfig {
password: java.lang.String,
schemaName: java.lang.String,
tableName: java.lang.String,
- userName: java.lang.String
+ userName: java.lang.String,
)
object SqlParams {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource.SqlParams = {
SimonaConfig.Simona.Input.Weather.Datasource.SqlParams(
jdbcUrl = $_reqStr(parentPath, c, "jdbcUrl", $tsCfgValidator),
@@ -1572,14 +1659,14 @@ object SimonaConfig {
else "public",
tableName =
$_reqStr(parentPath, c, "tableName", $tsCfgValidator),
- userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator)
+ userName = $_reqStr(parentPath, c, "userName", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1596,7 +1683,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather.Datasource = {
SimonaConfig.Simona.Input.Weather.Datasource(
coordinateSource =
@@ -1607,7 +1694,7 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("coordinateSource{}"),
parentPath + "coordinateSource.",
- $tsCfgValidator
+ $tsCfgValidator,
),
couchbaseParams =
if (c.hasPathOrNull("couchbaseParams"))
@@ -1616,7 +1703,7 @@ object SimonaConfig {
.CouchbaseParams(
c.getConfig("couchbaseParams"),
parentPath + "couchbaseParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1626,7 +1713,7 @@ object SimonaConfig {
SimonaConfig.BaseCsvParams(
c.getConfig("csvParams"),
parentPath + "csvParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1637,7 +1724,7 @@ object SimonaConfig {
.InfluxDb1xParams(
c.getConfig("influxDb1xParams"),
parentPath + "influxDb1xParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1655,7 +1742,7 @@ object SimonaConfig {
SimonaConfig.Simona.Input.Weather.Datasource.SampleParams(
c.getConfig("sampleParams"),
parentPath + "sampleParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1668,14 +1755,14 @@ object SimonaConfig {
SimonaConfig.Simona.Input.Weather.Datasource.SqlParams(
c.getConfig("sqlParams"),
parentPath + "sqlParams.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
timestampPattern =
if (c.hasPathOrNull("timestampPattern"))
Some(c.getString("timestampPattern"))
- else None
+ else None,
)
}
}
@@ -1683,7 +1770,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input.Weather = {
SimonaConfig.Simona.Input.Weather(
datasource = SimonaConfig.Simona.Input.Weather.Datasource(
@@ -1691,7 +1778,7 @@ object SimonaConfig {
else
com.typesafe.config.ConfigFactory.parseString("datasource{}"),
parentPath + "datasource.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
}
@@ -1700,61 +1787,62 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Input = {
SimonaConfig.Simona.Input(
grid = SimonaConfig.Simona.Input.Grid(
if (c.hasPathOrNull("grid")) c.getConfig("grid")
else com.typesafe.config.ConfigFactory.parseString("grid{}"),
parentPath + "grid.",
- $tsCfgValidator
+ $tsCfgValidator,
),
primary = SimonaConfig.Simona.Input.Primary(
if (c.hasPathOrNull("primary")) c.getConfig("primary")
else com.typesafe.config.ConfigFactory.parseString("primary{}"),
parentPath + "primary.",
- $tsCfgValidator
+ $tsCfgValidator,
),
weather = SimonaConfig.Simona.Input.Weather(
if (c.hasPathOrNull("weather")) c.getConfig("weather")
else com.typesafe.config.ConfigFactory.parseString("weather{}"),
parentPath + "weather.",
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
}
final case class Output(
base: SimonaConfig.Simona.Output.Base,
+ flex: scala.Boolean,
grid: SimonaConfig.GridOutputConfig,
participant: SimonaConfig.Simona.Output.Participant,
sink: SimonaConfig.Simona.Output.Sink,
- thermal: SimonaConfig.Simona.Output.Thermal
+ thermal: SimonaConfig.Simona.Output.Thermal,
)
object Output {
final case class Base(
addTimestampToOutputDir: scala.Boolean,
- dir: java.lang.String
+ dir: java.lang.String,
)
object Base {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output.Base = {
SimonaConfig.Simona.Output.Base(
addTimestampToOutputDir = !c.hasPathOrNull(
"addTimestampToOutputDir"
) || c.getBoolean("addTimestampToOutputDir"),
- dir = $_reqStr(parentPath, c, "dir", $tsCfgValidator)
+ dir = $_reqStr(parentPath, c, "dir", $tsCfgValidator),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1772,13 +1860,13 @@ object SimonaConfig {
defaultConfig: SimonaConfig.ParticipantBaseOutputConfig,
individualConfigs: scala.List[
SimonaConfig.ParticipantBaseOutputConfig
- ]
+ ],
)
object Participant {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output.Participant = {
SimonaConfig.Simona.Output.Participant(
defaultConfig = SimonaConfig.ParticipantBaseOutputConfig(
@@ -1787,19 +1875,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_ParticipantBaseOutputConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_ParticipantBaseOutputConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.ParticipantBaseOutputConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -1807,7 +1895,7 @@ object SimonaConfig {
SimonaConfig.ParticipantBaseOutputConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -1817,20 +1905,20 @@ object SimonaConfig {
final case class Sink(
csv: scala.Option[SimonaConfig.Simona.Output.Sink.Csv],
influxDb1x: scala.Option[SimonaConfig.Simona.Output.Sink.InfluxDb1x],
- kafka: scala.Option[SimonaConfig.ResultKafkaParams]
+ kafka: scala.Option[SimonaConfig.ResultKafkaParams],
)
object Sink {
final case class Csv(
fileFormat: java.lang.String,
filePrefix: java.lang.String,
fileSuffix: java.lang.String,
- isHierarchic: scala.Boolean
+ isHierarchic: scala.Boolean,
)
object Csv {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output.Sink.Csv = {
SimonaConfig.Simona.Output.Sink.Csv(
fileFormat =
@@ -1843,7 +1931,7 @@ object SimonaConfig {
if (c.hasPathOrNull("fileSuffix")) c.getString("fileSuffix")
else "",
isHierarchic =
- c.hasPathOrNull("isHierarchic") && c.getBoolean("isHierarchic")
+ c.hasPathOrNull("isHierarchic") && c.getBoolean("isHierarchic"),
)
}
}
@@ -1851,25 +1939,25 @@ object SimonaConfig {
final case class InfluxDb1x(
database: java.lang.String,
port: scala.Int,
- url: java.lang.String
+ url: java.lang.String,
)
object InfluxDb1x {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output.Sink.InfluxDb1x = {
SimonaConfig.Simona.Output.Sink.InfluxDb1x(
database = $_reqStr(parentPath, c, "database", $tsCfgValidator),
port = $_reqInt(parentPath, c, "port", $tsCfgValidator),
- url = $_reqStr(parentPath, c, "url", $tsCfgValidator)
+ url = $_reqStr(parentPath, c, "url", $tsCfgValidator),
)
}
private def $_reqInt(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Int = {
if (c == null) 0
else
@@ -1885,7 +1973,7 @@ object SimonaConfig {
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -1902,7 +1990,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output.Sink = {
SimonaConfig.Simona.Output.Sink(
csv =
@@ -1911,7 +1999,7 @@ object SimonaConfig {
SimonaConfig.Simona.Output.Sink.Csv(
c.getConfig("csv"),
parentPath + "csv.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1921,7 +2009,7 @@ object SimonaConfig {
SimonaConfig.Simona.Output.Sink.InfluxDb1x(
c.getConfig("influxDb1x"),
parentPath + "influxDb1x.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -1931,23 +2019,23 @@ object SimonaConfig {
SimonaConfig.ResultKafkaParams(
c.getConfig("kafka"),
parentPath + "kafka.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
- else None
+ else None,
)
}
}
final case class Thermal(
defaultConfig: SimonaConfig.SimpleOutputConfig,
- individualConfigs: scala.List[SimonaConfig.SimpleOutputConfig]
+ individualConfigs: scala.List[SimonaConfig.SimpleOutputConfig],
)
object Thermal {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output.Thermal = {
SimonaConfig.Simona.Output.Thermal(
defaultConfig = SimonaConfig.SimpleOutputConfig(
@@ -1956,19 +2044,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_SimpleOutputConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_SimpleOutputConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.SimpleOutputConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -1976,7 +2064,7 @@ object SimonaConfig {
SimonaConfig.SimpleOutputConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -1986,39 +2074,40 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Output = {
SimonaConfig.Simona.Output(
base = SimonaConfig.Simona.Output.Base(
if (c.hasPathOrNull("base")) c.getConfig("base")
else com.typesafe.config.ConfigFactory.parseString("base{}"),
parentPath + "base.",
- $tsCfgValidator
+ $tsCfgValidator,
),
+ flex = c.hasPathOrNull("flex") && c.getBoolean("flex"),
grid = SimonaConfig.GridOutputConfig(
if (c.hasPathOrNull("grid")) c.getConfig("grid")
else com.typesafe.config.ConfigFactory.parseString("grid{}"),
parentPath + "grid.",
- $tsCfgValidator
+ $tsCfgValidator,
),
participant = SimonaConfig.Simona.Output.Participant(
if (c.hasPathOrNull("participant")) c.getConfig("participant")
else com.typesafe.config.ConfigFactory.parseString("participant{}"),
parentPath + "participant.",
- $tsCfgValidator
+ $tsCfgValidator,
),
sink = SimonaConfig.Simona.Output.Sink(
if (c.hasPathOrNull("sink")) c.getConfig("sink")
else com.typesafe.config.ConfigFactory.parseString("sink{}"),
parentPath + "sink.",
- $tsCfgValidator
+ $tsCfgValidator,
),
thermal = SimonaConfig.Simona.Output.Thermal(
if (c.hasPathOrNull("thermal")) c.getConfig("thermal")
else com.typesafe.config.ConfigFactory.parseString("thermal{}"),
parentPath + "thermal.",
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
}
@@ -2028,30 +2117,30 @@ object SimonaConfig {
newtonraphson: SimonaConfig.Simona.Powerflow.Newtonraphson,
resolution: java.time.Duration,
stopOnFailure: scala.Boolean,
- sweepTimeout: java.time.Duration
+ sweepTimeout: java.time.Duration,
)
object Powerflow {
final case class Newtonraphson(
epsilon: scala.List[scala.Double],
- iterations: scala.Int
+ iterations: scala.Int,
)
object Newtonraphson {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Powerflow.Newtonraphson = {
SimonaConfig.Simona.Powerflow.Newtonraphson(
epsilon =
$_L$_dbl(c.getList("epsilon"), parentPath, $tsCfgValidator),
- iterations = $_reqInt(parentPath, c, "iterations", $tsCfgValidator)
+ iterations = $_reqInt(parentPath, c, "iterations", $tsCfgValidator),
)
}
private def $_reqInt(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Int = {
if (c == null) 0
else
@@ -2068,7 +2157,7 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Powerflow = {
SimonaConfig.Simona.Powerflow(
maxSweepPowerDeviation =
@@ -2078,7 +2167,7 @@ object SimonaConfig {
else
com.typesafe.config.ConfigFactory.parseString("newtonraphson{}"),
parentPath + "newtonraphson.",
- $tsCfgValidator
+ $tsCfgValidator,
),
resolution =
if (c.hasPathOrNull("resolution")) c.getDuration("resolution")
@@ -2087,14 +2176,14 @@ object SimonaConfig {
c.hasPathOrNull("stopOnFailure") && c.getBoolean("stopOnFailure"),
sweepTimeout =
if (c.hasPathOrNull("sweepTimeout")) c.getDuration("sweepTimeout")
- else java.time.Duration.parse("PT30S")
+ else java.time.Duration.parse("PT30S"),
)
}
private def $_reqDbl(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.Double = {
if (c == null) 0
else
@@ -2112,18 +2201,18 @@ object SimonaConfig {
listener: SimonaConfig.Simona.Runtime.Listener,
participant: SimonaConfig.Simona.Runtime.Participant,
selected_subgrids: scala.Option[scala.List[scala.Int]],
- selected_volt_lvls: scala.Option[scala.List[SimonaConfig.VoltLvlConfig]]
+ selected_volt_lvls: scala.Option[scala.List[SimonaConfig.VoltLvlConfig]],
)
object Runtime {
final case class Listener(
eventsToProcess: scala.Option[scala.List[java.lang.String]],
- kafka: scala.Option[SimonaConfig.RuntimeKafkaParams]
+ kafka: scala.Option[SimonaConfig.RuntimeKafkaParams],
)
object Listener {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Listener = {
SimonaConfig.Simona.Runtime.Listener(
eventsToProcess =
@@ -2132,7 +2221,7 @@ object SimonaConfig {
$_L$_str(
c.getList("eventsToProcess"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -2142,10 +2231,10 @@ object SimonaConfig {
SimonaConfig.RuntimeKafkaParams(
c.getConfig("kafka"),
parentPath + "kafka.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
- else None
+ else None,
)
}
}
@@ -2157,18 +2246,18 @@ object SimonaConfig {
load: SimonaConfig.Simona.Runtime.Participant.Load,
pv: SimonaConfig.Simona.Runtime.Participant.Pv,
requestVoltageDeviationThreshold: scala.Double,
- wec: SimonaConfig.Simona.Runtime.Participant.Wec
+ wec: SimonaConfig.Simona.Runtime.Participant.Wec,
)
object Participant {
final case class Evcs(
defaultConfig: SimonaConfig.EvcsRuntimeConfig,
- individualConfigs: scala.List[SimonaConfig.EvcsRuntimeConfig]
+ individualConfigs: scala.List[SimonaConfig.EvcsRuntimeConfig],
)
object Evcs {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant.Evcs = {
SimonaConfig.Simona.Runtime.Participant.Evcs(
defaultConfig = SimonaConfig.EvcsRuntimeConfig(
@@ -2178,19 +2267,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_EvcsRuntimeConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_EvcsRuntimeConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.EvcsRuntimeConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2198,7 +2287,7 @@ object SimonaConfig {
SimonaConfig.EvcsRuntimeConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2207,13 +2296,13 @@ object SimonaConfig {
final case class FixedFeedIn(
defaultConfig: SimonaConfig.FixedFeedInRuntimeConfig,
- individualConfigs: scala.List[SimonaConfig.FixedFeedInRuntimeConfig]
+ individualConfigs: scala.List[SimonaConfig.FixedFeedInRuntimeConfig],
)
object FixedFeedIn {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant.FixedFeedIn = {
SimonaConfig.Simona.Runtime.Participant.FixedFeedIn(
defaultConfig = SimonaConfig.FixedFeedInRuntimeConfig(
@@ -2223,19 +2312,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_FixedFeedInRuntimeConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_FixedFeedInRuntimeConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.FixedFeedInRuntimeConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2243,7 +2332,7 @@ object SimonaConfig {
SimonaConfig.FixedFeedInRuntimeConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2252,13 +2341,13 @@ object SimonaConfig {
final case class Hp(
defaultConfig: SimonaConfig.HpRuntimeConfig,
- individualConfigs: scala.List[SimonaConfig.HpRuntimeConfig]
+ individualConfigs: scala.List[SimonaConfig.HpRuntimeConfig],
)
object Hp {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant.Hp = {
SimonaConfig.Simona.Runtime.Participant.Hp(
defaultConfig = SimonaConfig.HpRuntimeConfig(
@@ -2268,19 +2357,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_HpRuntimeConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_HpRuntimeConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.HpRuntimeConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2288,7 +2377,7 @@ object SimonaConfig {
SimonaConfig.HpRuntimeConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2297,13 +2386,13 @@ object SimonaConfig {
final case class Load(
defaultConfig: SimonaConfig.LoadRuntimeConfig,
- individualConfigs: scala.List[SimonaConfig.LoadRuntimeConfig]
+ individualConfigs: scala.List[SimonaConfig.LoadRuntimeConfig],
)
object Load {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant.Load = {
SimonaConfig.Simona.Runtime.Participant.Load(
defaultConfig = SimonaConfig.LoadRuntimeConfig(
@@ -2313,19 +2402,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_LoadRuntimeConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_LoadRuntimeConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.LoadRuntimeConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2333,7 +2422,7 @@ object SimonaConfig {
SimonaConfig.LoadRuntimeConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2342,13 +2431,13 @@ object SimonaConfig {
final case class Pv(
defaultConfig: SimonaConfig.PvRuntimeConfig,
- individualConfigs: scala.List[SimonaConfig.PvRuntimeConfig]
+ individualConfigs: scala.List[SimonaConfig.PvRuntimeConfig],
)
object Pv {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant.Pv = {
SimonaConfig.Simona.Runtime.Participant.Pv(
defaultConfig = SimonaConfig.PvRuntimeConfig(
@@ -2358,19 +2447,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_PvRuntimeConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_PvRuntimeConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.PvRuntimeConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2378,7 +2467,7 @@ object SimonaConfig {
SimonaConfig.PvRuntimeConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2387,13 +2476,13 @@ object SimonaConfig {
final case class Wec(
defaultConfig: SimonaConfig.WecRuntimeConfig,
- individualConfigs: scala.List[SimonaConfig.WecRuntimeConfig]
+ individualConfigs: scala.List[SimonaConfig.WecRuntimeConfig],
)
object Wec {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant.Wec = {
SimonaConfig.Simona.Runtime.Participant.Wec(
defaultConfig = SimonaConfig.WecRuntimeConfig(
@@ -2403,19 +2492,19 @@ object SimonaConfig {
com.typesafe.config.ConfigFactory
.parseString("defaultConfig{}"),
parentPath + "defaultConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
individualConfigs = $_LSimonaConfig_WecRuntimeConfig(
c.getList("individualConfigs"),
parentPath,
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_LSimonaConfig_WecRuntimeConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.WecRuntimeConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2423,7 +2512,7 @@ object SimonaConfig {
SimonaConfig.WecRuntimeConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2433,39 +2522,39 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime.Participant = {
SimonaConfig.Simona.Runtime.Participant(
evcs = SimonaConfig.Simona.Runtime.Participant.Evcs(
if (c.hasPathOrNull("evcs")) c.getConfig("evcs")
else com.typesafe.config.ConfigFactory.parseString("evcs{}"),
parentPath + "evcs.",
- $tsCfgValidator
+ $tsCfgValidator,
),
fixedFeedIn = SimonaConfig.Simona.Runtime.Participant.FixedFeedIn(
if (c.hasPathOrNull("fixedFeedIn")) c.getConfig("fixedFeedIn")
else
com.typesafe.config.ConfigFactory.parseString("fixedFeedIn{}"),
parentPath + "fixedFeedIn.",
- $tsCfgValidator
+ $tsCfgValidator,
),
hp = SimonaConfig.Simona.Runtime.Participant.Hp(
if (c.hasPathOrNull("hp")) c.getConfig("hp")
else com.typesafe.config.ConfigFactory.parseString("hp{}"),
parentPath + "hp.",
- $tsCfgValidator
+ $tsCfgValidator,
),
load = SimonaConfig.Simona.Runtime.Participant.Load(
if (c.hasPathOrNull("load")) c.getConfig("load")
else com.typesafe.config.ConfigFactory.parseString("load{}"),
parentPath + "load.",
- $tsCfgValidator
+ $tsCfgValidator,
),
pv = SimonaConfig.Simona.Runtime.Participant.Pv(
if (c.hasPathOrNull("pv")) c.getConfig("pv")
else com.typesafe.config.ConfigFactory.parseString("pv{}"),
parentPath + "pv.",
- $tsCfgValidator
+ $tsCfgValidator,
),
requestVoltageDeviationThreshold =
if (c.hasPathOrNull("requestVoltageDeviationThreshold"))
@@ -2475,8 +2564,8 @@ object SimonaConfig {
if (c.hasPathOrNull("wec")) c.getConfig("wec")
else com.typesafe.config.ConfigFactory.parseString("wec{}"),
parentPath + "wec.",
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
}
@@ -2484,20 +2573,20 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Runtime = {
SimonaConfig.Simona.Runtime(
listener = SimonaConfig.Simona.Runtime.Listener(
if (c.hasPathOrNull("listener")) c.getConfig("listener")
else com.typesafe.config.ConfigFactory.parseString("listener{}"),
parentPath + "listener.",
- $tsCfgValidator
+ $tsCfgValidator,
),
participant = SimonaConfig.Simona.Runtime.Participant(
if (c.hasPathOrNull("participant")) c.getConfig("participant")
else com.typesafe.config.ConfigFactory.parseString("participant{}"),
parentPath + "participant.",
- $tsCfgValidator
+ $tsCfgValidator,
),
selected_subgrids =
if (c.hasPathOrNull("selected_subgrids"))
@@ -2505,7 +2594,7 @@ object SimonaConfig {
$_L$_int(
c.getList("selected_subgrids"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
else None,
@@ -2515,16 +2604,16 @@ object SimonaConfig {
$_LSimonaConfig_VoltLvlConfig(
c.getList("selected_volt_lvls"),
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
- else None
+ else None,
)
}
private def $_LSimonaConfig_VoltLvlConfig(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[SimonaConfig.VoltLvlConfig] = {
import scala.jdk.CollectionConverters._
cl.asScala
@@ -2532,7 +2621,7 @@ object SimonaConfig {
SimonaConfig.VoltLvlConfig(
cv.asInstanceOf[com.typesafe.config.ConfigObject].toConfig,
parentPath,
- $tsCfgValidator
+ $tsCfgValidator,
)
)
.toList
@@ -2542,13 +2631,13 @@ object SimonaConfig {
final case class Time(
endDateTime: java.lang.String,
schedulerReadyCheckWindow: scala.Option[scala.Int],
- startDateTime: java.lang.String
+ startDateTime: java.lang.String,
)
object Time {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona.Time = {
SimonaConfig.Simona.Time(
endDateTime =
@@ -2560,7 +2649,7 @@ object SimonaConfig {
else None,
startDateTime =
if (c.hasPathOrNull("startDateTime")) c.getString("startDateTime")
- else "2011-05-01 00:00:00"
+ else "2011-05-01 00:00:00",
)
}
}
@@ -2568,44 +2657,54 @@ object SimonaConfig {
def apply(
c: com.typesafe.config.Config,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): SimonaConfig.Simona = {
SimonaConfig.Simona(
+ control =
+ if (c.hasPathOrNull("control"))
+ scala.Some(
+ SimonaConfig.Simona.Control(
+ c.getConfig("control"),
+ parentPath + "control.",
+ $tsCfgValidator,
+ )
+ )
+ else None,
event = SimonaConfig.Simona.Event(
if (c.hasPathOrNull("event")) c.getConfig("event")
else com.typesafe.config.ConfigFactory.parseString("event{}"),
parentPath + "event.",
- $tsCfgValidator
+ $tsCfgValidator,
),
gridConfig = SimonaConfig.Simona.GridConfig(
if (c.hasPathOrNull("gridConfig")) c.getConfig("gridConfig")
else com.typesafe.config.ConfigFactory.parseString("gridConfig{}"),
parentPath + "gridConfig.",
- $tsCfgValidator
+ $tsCfgValidator,
),
input = SimonaConfig.Simona.Input(
if (c.hasPathOrNull("input")) c.getConfig("input")
else com.typesafe.config.ConfigFactory.parseString("input{}"),
parentPath + "input.",
- $tsCfgValidator
+ $tsCfgValidator,
),
output = SimonaConfig.Simona.Output(
if (c.hasPathOrNull("output")) c.getConfig("output")
else com.typesafe.config.ConfigFactory.parseString("output{}"),
parentPath + "output.",
- $tsCfgValidator
+ $tsCfgValidator,
),
powerflow = SimonaConfig.Simona.Powerflow(
if (c.hasPathOrNull("powerflow")) c.getConfig("powerflow")
else com.typesafe.config.ConfigFactory.parseString("powerflow{}"),
parentPath + "powerflow.",
- $tsCfgValidator
+ $tsCfgValidator,
),
runtime = SimonaConfig.Simona.Runtime(
if (c.hasPathOrNull("runtime")) c.getConfig("runtime")
else com.typesafe.config.ConfigFactory.parseString("runtime{}"),
parentPath + "runtime.",
- $tsCfgValidator
+ $tsCfgValidator,
),
simulationName =
$_reqStr(parentPath, c, "simulationName", $tsCfgValidator),
@@ -2613,15 +2712,15 @@ object SimonaConfig {
if (c.hasPathOrNull("time")) c.getConfig("time")
else com.typesafe.config.ConfigFactory.parseString("time{}"),
parentPath + "time.",
- $tsCfgValidator
- )
+ $tsCfgValidator,
+ ),
)
}
private def $_reqStr(
parentPath: java.lang.String,
c: com.typesafe.config.Config,
path: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): java.lang.String = {
if (c == null) null
else
@@ -2643,7 +2742,7 @@ object SimonaConfig {
if (c.hasPathOrNull("simona")) c.getConfig("simona")
else com.typesafe.config.ConfigFactory.parseString("simona{}"),
parentPath + "simona.",
- $tsCfgValidator
+ $tsCfgValidator,
)
)
$tsCfgValidator.validate()
@@ -2653,7 +2752,7 @@ object SimonaConfig {
private def $_L$_dbl(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[scala.Double] = {
import scala.jdk.CollectionConverters._
cl.asScala.map(cv => $_dbl(cv)).toList
@@ -2661,7 +2760,7 @@ object SimonaConfig {
private def $_L$_int(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[scala.Int] = {
import scala.jdk.CollectionConverters._
cl.asScala.map(cv => $_int(cv)).toList
@@ -2669,7 +2768,7 @@ object SimonaConfig {
private def $_L$_str(
cl: com.typesafe.config.ConfigList,
parentPath: java.lang.String,
- $tsCfgValidator: $TsCfgValidator
+ $tsCfgValidator: $TsCfgValidator,
): scala.List[java.lang.String] = {
import scala.jdk.CollectionConverters._
cl.asScala.map(cv => $_str(cv)).toList
@@ -2685,7 +2784,7 @@ object SimonaConfig {
private def $_expE(
cv: com.typesafe.config.ConfigValue,
- exp: java.lang.String
+ exp: java.lang.String,
) = {
val u: Any = cv.unwrapped
new java.lang.RuntimeException(
@@ -2714,7 +2813,7 @@ object SimonaConfig {
def addBadPath(
path: java.lang.String,
- e: com.typesafe.config.ConfigException
+ e: com.typesafe.config.ConfigException,
): Unit = {
badPaths += s"'$path': ${e.getClass.getName}(${e.getMessage})"
}
@@ -2722,7 +2821,7 @@ object SimonaConfig {
def addInvalidEnumValue(
path: java.lang.String,
value: java.lang.String,
- enumName: java.lang.String
+ enumName: java.lang.String,
): Unit = {
badPaths += s"'$path': invalid value $value for enumeration $enumName"
}
diff --git a/src/main/scala/edu/ie3/simona/config/VoltLvlParser.scala b/src/main/scala/edu/ie3/simona/config/VoltLvlParser.scala
index 3d3ce38920..885adae81e 100644
--- a/src/main/scala/edu/ie3/simona/config/VoltLvlParser.scala
+++ b/src/main/scala/edu/ie3/simona/config/VoltLvlParser.scala
@@ -10,7 +10,7 @@ import edu.ie3.datamodel.exceptions.VoltageLevelException
import edu.ie3.datamodel.models.voltagelevels.{
CommonVoltageLevel,
GermanVoltageLevelUtils,
- VoltageLevel
+ VoltageLevel,
}
import edu.ie3.simona.config.SimonaConfig.VoltLvlConfig
import edu.ie3.simona.exceptions.InvalidConfigParameterException
@@ -46,7 +46,7 @@ object VoltLvlParser {
*/
private def parse(
id: String,
- vNominal: ComparableQuantity[ElectricPotential]
+ vNominal: ComparableQuantity[ElectricPotential],
): CommonVoltageLevel =
try {
GermanVoltageLevelUtils.parse(id, vNominal)
@@ -54,7 +54,7 @@ object VoltLvlParser {
case vle: VoltageLevelException =>
throw new InvalidConfigParameterException(
s"Cannot find a common voltage level with id $id and nominal voltage $vNominal",
- vle
+ vle,
)
}
@@ -76,12 +76,12 @@ object VoltLvlParser {
case iae: IllegalArgumentException =>
throw new InvalidConfigParameterException(
s"Cannot parse the nominal voltage $quantString",
- iae
+ iae,
)
case cce: ClassCastException =>
throw new InvalidConfigParameterException(
s"Cannot parse $quantString to nominal voltage, as it is no voltage.",
- cce
+ cce,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/event/ResultEvent.scala b/src/main/scala/edu/ie3/simona/event/ResultEvent.scala
index 7244d941f5..d81242c608 100644
--- a/src/main/scala/edu/ie3/simona/event/ResultEvent.scala
+++ b/src/main/scala/edu/ie3/simona/event/ResultEvent.scala
@@ -10,14 +10,17 @@ import edu.ie3.datamodel.models.result.NodeResult
import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
- Transformer2WResult
+ Transformer2WResult,
+}
+import edu.ie3.datamodel.models.result.system.{
+ FlexOptionsResult,
+ SystemParticipantResult,
}
-import edu.ie3.datamodel.models.result.system.SystemParticipantResult
import edu.ie3.datamodel.models.result.thermal.ThermalUnitResult
import edu.ie3.simona.agent.grid.GridResultsSupport.PartialTransformer3wResult
-import edu.ie3.simona.event.listener.ResultEventListener.ResultMessage
+import edu.ie3.simona.event.listener.ResultEventListener
-sealed trait ResultEvent extends ResultMessage
+sealed trait ResultEvent extends Event with ResultEventListener.Request
/** Calculation result events
*/
@@ -62,7 +65,17 @@ object ResultEvent {
switchResults: Iterable[SwitchResult],
lineResults: Iterable[LineResult],
transformer2wResults: Iterable[Transformer2WResult],
- transformer3wResults: Iterable[PartialTransformer3wResult]
+ transformer3wResults: Iterable[PartialTransformer3wResult],
+ ) extends ResultEvent
+
+ /** Event that holds the flexibility options result of a
+ * [[edu.ie3.simona.model.participant.SystemParticipant]]
+ *
+ * @param flexOptionsResult
+ * the flex options result
+ */
+ final case class FlexOptionsResultEvent(
+ flexOptionsResult: FlexOptionsResult
) extends ResultEvent
}
diff --git a/src/main/scala/edu/ie3/simona/event/RuntimeEvent.scala b/src/main/scala/edu/ie3/simona/event/RuntimeEvent.scala
index 23a3352e19..da02b9c258 100644
--- a/src/main/scala/edu/ie3/simona/event/RuntimeEvent.scala
+++ b/src/main/scala/edu/ie3/simona/event/RuntimeEvent.scala
@@ -6,8 +6,10 @@
package edu.ie3.simona.event
+import edu.ie3.simona.event.listener.RuntimeEventListener.Request
+
/** Event type for simulation control */
-sealed trait RuntimeEvent extends Event
+sealed trait RuntimeEvent extends Event with Request
object RuntimeEvent {
@@ -80,7 +82,7 @@ object RuntimeEvent {
final case class Done(
tick: Long,
duration: Long,
- errorInSim: Boolean
+ errorInSim: Boolean,
) extends RuntimeEvent
/** Indicates that a power flow calculation has failed. This event is not
diff --git a/src/main/scala/edu/ie3/simona/event/listener/DelayedStopHelper.scala b/src/main/scala/edu/ie3/simona/event/listener/DelayedStopHelper.scala
new file mode 100644
index 0000000000..c6d8cea4d5
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/event/listener/DelayedStopHelper.scala
@@ -0,0 +1,52 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.event.listener
+
+import org.apache.pekko.actor.typed.Behavior
+import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
+
+import scala.concurrent.duration.DurationInt
+
+/** Helper that provides functionality for delayed stopping of actors, i.e. upon
+ * receiving [[FlushAndStop]], the actor is stopped after a fixed amount of
+ * time after the last message has been received
+ */
+object DelayedStopHelper {
+
+ /** Note: Needs to extend be message traits for actors that want to use this
+ * functionality
+ */
+ sealed trait StoppingMsg
+ extends ResultEventListener.Request
+ with RuntimeEventListener.Request
+
+ /** Message indicating that [[RuntimeEventListener]] should stop. Instead of
+ * using [[org.apache.pekko.actor.typed.scaladsl.ActorContext.stop]], this
+ * way of stopping allows the current mailbox to be processed, plus more
+ * messages that are pending to be received.
+ */
+ case object FlushAndStop extends StoppingMsg
+
+ private case object StopTimeout extends StoppingMsg
+
+ def handleMsg[T >: StoppingMsg]
+ : PartialFunction[(ActorContext[T], StoppingMsg), Behavior[T]] = {
+
+ case (ctx, FlushAndStop) =>
+ ctx.log.debug(
+ s"Received FlushAndStop message, shutting down once no message has been received for 5 seconds."
+ )
+ ctx.setReceiveTimeout(5.seconds, StopTimeout)
+ Behaviors.same
+
+ case (ctx, StopTimeout) =>
+ // there have been no messages for 5 seconds, let's end this
+ ctx.log.debug(s"${getClass.getSimpleName} is now stopped.")
+ Behaviors.stopped
+ }
+
+}
diff --git a/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala b/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala
index 38539b2baa..d5acf17912 100644
--- a/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala
+++ b/src/main/scala/edu/ie3/simona/event/listener/ResultEventListener.scala
@@ -11,18 +11,17 @@ import org.apache.pekko.actor.typed.{Behavior, PostStop}
import edu.ie3.datamodel.io.processor.result.ResultEntityProcessor
import edu.ie3.datamodel.models.result.{NodeResult, ResultEntity}
import edu.ie3.simona.agent.grid.GridResultsSupport.PartialTransformer3wResult
-import edu.ie3.simona.event.Event
import edu.ie3.simona.event.ResultEvent.{
+ FlexOptionsResultEvent,
ParticipantResultEvent,
PowerFlowResultEvent,
- ThermalResultEvent
+ ThermalResultEvent,
}
import edu.ie3.simona.exceptions.{
FileHierarchyException,
- ProcessResultEventException
+ ProcessResultEventException,
}
import edu.ie3.simona.io.result._
-import edu.ie3.simona.ontology.messages.StopMessage
import edu.ie3.simona.util.ResultFileHierarchy
import org.slf4j.Logger
@@ -33,15 +32,13 @@ import scala.util.{Failure, Success, Try}
object ResultEventListener extends Transformer3wResultSupport {
- trait ResultMessage extends Event
+ trait Request
private final case class SinkResponse(
response: Map[Class[_], ResultEntitySink]
- ) extends ResultMessage
+ ) extends Request
- private final case class Failed(ex: Exception) extends ResultMessage
-
- private final case object StopTimeout extends ResultMessage
+ private final case class InitFailed(ex: Exception) extends Request
/** [[ResultEventListener]] base data containing all information the listener
* needs
@@ -54,8 +51,8 @@ object ResultEventListener extends Transformer3wResultSupport {
classToSink: Map[Class[_], ResultEntitySink],
threeWindingResults: Map[
Transformer3wKey,
- AggregatedTransformer3wResult
- ] = Map.empty
+ AggregatedTransformer3wResult,
+ ] = Map.empty,
)
/** Initialize the sinks for this listener based on the provided collection
@@ -93,8 +90,8 @@ object ResultEventListener extends Transformer3wResultSupport {
ResultEntityCsvSink(
fileName.replace(".gz", ""),
new ResultEntityProcessor(resultClass),
- fileName.endsWith(".gz")
- )
+ fileName.endsWith(".gz"),
+ ),
)
}
} else {
@@ -120,7 +117,7 @@ object ResultEventListener extends Transformer3wResultSupport {
runId,
bootstrapServers,
schemaRegistryUrl,
- linger
+ linger,
) =>
val classes: Iterable[Class[_ <: ResultEntity]] = Set(
classOf[NodeResult] // currently, only NodeResults are sent out
@@ -134,8 +131,8 @@ object ResultEventListener extends Transformer3wResultSupport {
runId,
bootstrapServers,
schemaRegistryUrl,
- linger
- )
+ linger,
+ ),
)
)
)
@@ -154,7 +151,7 @@ object ResultEventListener extends Transformer3wResultSupport {
private def handleResult(
resultEntity: ResultEntity,
baseData: BaseData,
- log: Logger
+ log: Logger,
): BaseData = {
handOverToSink(resultEntity, baseData.classToSink, log)
baseData
@@ -174,14 +171,14 @@ object ResultEventListener extends Transformer3wResultSupport {
private def handlePartialTransformer3wResult(
result: PartialTransformer3wResult,
baseData: BaseData,
- log: Logger
+ log: Logger,
): BaseData = {
val key = Transformer3wKey(result.input, result.time)
// retrieve existing partial result or use empty one
val partialResult =
baseData.threeWindingResults.getOrElse(
key,
- AggregatedTransformer3wResult.EMPTY
+ AggregatedTransformer3wResult.EMPTY,
)
// add partial result
val updatedResults = partialResult.add(result).map { updatedResult =>
@@ -201,7 +198,7 @@ object ResultEventListener extends Transformer3wResultSupport {
case Failure(exception) =>
log.warn(
"Failure when handling partial Transformer3w result",
- exception
+ exception,
)
// on failure, we just continue with previous results
baseData.threeWindingResults
@@ -221,7 +218,7 @@ object ResultEventListener extends Transformer3wResultSupport {
private def handOverToSink(
resultEntity: ResultEntity,
classToSink: Map[Class[_], ResultEntitySink],
- log: Logger
+ log: Logger,
): Unit =
Try {
classToSink
@@ -233,7 +230,7 @@ object ResultEventListener extends Transformer3wResultSupport {
def apply(
resultFileHierarchy: ResultFileHierarchy
- ): Behavior[ResultMessage] = Behaviors.setup[ResultMessage] { ctx =>
+ ): Behavior[Request] = Behaviors.setup[Request] { ctx =>
ctx.log.debug("Starting initialization!")
resultFileHierarchy.resultSinkType match {
case _: ResultSinkType.Kafka =>
@@ -243,7 +240,7 @@ object ResultEventListener extends Transformer3wResultSupport {
s"Events that will be processed: {}",
resultFileHierarchy.resultEntitiesToConsider
.map(_.getSimpleName)
- .mkString(",")
+ .mkString(","),
)
}
@@ -252,33 +249,32 @@ object ResultEventListener extends Transformer3wResultSupport {
ResultEventListener.initializeSinks(resultFileHierarchy)
)
) {
- case Failure(exception: Exception) => Failed(exception)
+ case Failure(exception: Exception) => InitFailed(exception)
case Success(result) => SinkResponse(result.toMap)
}
init()
}
- private def init(): Behavior[ResultMessage] = Behaviors.withStash(200) {
- buffer =>
- Behaviors.receive[ResultMessage] {
- case (ctx, SinkResponse(response)) =>
- ctx.log.debug("Initialization complete!")
- buffer.unstashAll(idle(BaseData(response)))
+ private def init(): Behavior[Request] = Behaviors.withStash(200) { buffer =>
+ Behaviors.receive[Request] {
+ case (ctx, SinkResponse(response)) =>
+ ctx.log.debug("Initialization complete!")
+ buffer.unstashAll(idle(BaseData(response)))
- case (ctx, Failed(ex)) =>
- ctx.log.error("Unable to setup ResultEventListener.", ex)
- Behaviors.stopped
+ case (ctx, InitFailed(ex)) =>
+ ctx.log.error("Unable to setup ResultEventListener.", ex)
+ Behaviors.stopped
- case (_, msg) =>
- // stash all messages
- buffer.stash(msg)
- Behaviors.same
- }
+ case (_, msg) =>
+ // stash all messages
+ buffer.stash(msg)
+ Behaviors.same
+ }
}
- private def idle(baseData: BaseData): Behavior[ResultMessage] = Behaviors
- .receive[ResultMessage] {
+ private def idle(baseData: BaseData): Behavior[Request] = Behaviors
+ .receivePartial[Request] {
case (ctx, ParticipantResultEvent(participantResult)) =>
val updatedBaseData = handleResult(participantResult, baseData, ctx.log)
idle(updatedBaseData)
@@ -294,8 +290,8 @@ object ResultEventListener extends Transformer3wResultSupport {
switchResults,
lineResults,
transformer2wResults,
- transformer3wResults
- )
+ transformer3wResults,
+ ),
) =>
val updatedBaseData =
(nodeResults ++ switchResults ++ lineResults ++ transformer2wResults ++ transformer3wResults)
@@ -304,27 +300,22 @@ object ResultEventListener extends Transformer3wResultSupport {
handleResult(resultEntity, currentBaseData, ctx.log)
case (
currentBaseData,
- partialTransformerResult: PartialTransformer3wResult
+ partialTransformerResult: PartialTransformer3wResult,
) =>
handlePartialTransformer3wResult(
partialTransformerResult,
currentBaseData,
- ctx.log
+ ctx.log,
)
}
idle(updatedBaseData)
- case (ctx, _: StopMessage) =>
- ctx.log.debug(
- s"${getClass.getSimpleName} received Stop message, shutting down when no message has been received in 5 seconds."
- )
- ctx.setReceiveTimeout(5.seconds, StopTimeout)
- Behaviors.same
+ case (ctx, FlexOptionsResultEvent(flexOptionsResult)) =>
+ val updatedBaseData = handleResult(flexOptionsResult, baseData, ctx.log)
+ idle(updatedBaseData)
- case (ctx, StopTimeout) =>
- // there have been no messages for 5 seconds, let's end this
- ctx.log.debug(s"${getClass.getSimpleName} is now stopped.")
- Behaviors.stopped
+ case (ctx, msg: DelayedStopHelper.StoppingMsg) =>
+ DelayedStopHelper.handleMsg((ctx, msg))
}
.receiveSignal { case (ctx, PostStop) =>
@@ -336,7 +327,7 @@ object ResultEventListener extends Transformer3wResultSupport {
.map { case Transformer3wKey(model, zdt) =>
s"model '$model' at $zdt"
}
- .mkString("\n\t\t")
+ .mkString("\n\t\t"),
)
// close sinks concurrently to speed up closing (closing calls might be blocking)
@@ -348,7 +339,7 @@ object ResultEventListener extends Transformer3wResultSupport {
}
)
),
- 5.minutes
+ 5.minutes,
)
ctx.log.debug("Result I/O completed.")
diff --git a/src/main/scala/edu/ie3/simona/event/listener/RuntimeEventListener.scala b/src/main/scala/edu/ie3/simona/event/listener/RuntimeEventListener.scala
index a45fdd5f86..bb97242e13 100644
--- a/src/main/scala/edu/ie3/simona/event/listener/RuntimeEventListener.scala
+++ b/src/main/scala/edu/ie3/simona/event/listener/RuntimeEventListener.scala
@@ -6,8 +6,6 @@
package edu.ie3.simona.event.listener
-import org.apache.pekko.actor.typed.{Behavior, PostStop}
-import org.apache.pekko.actor.typed.scaladsl.Behaviors
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.event.RuntimeEvent.PowerFlowFailed
@@ -16,10 +14,11 @@ import edu.ie3.simona.io.runtime.{
RuntimeEventKafkaSink,
RuntimeEventLogSink,
RuntimeEventQueueSink,
- RuntimeEventSink
+ RuntimeEventSink,
}
import edu.ie3.util.TimeUtil
-import org.slf4j.Logger
+import org.apache.pekko.actor.typed.scaladsl.Behaviors
+import org.apache.pekko.actor.typed.{Behavior, PostStop}
import java.util.concurrent.BlockingQueue
@@ -29,7 +28,10 @@ import java.util.concurrent.BlockingQueue
*/
object RuntimeEventListener {
+ trait Request
+
/** Creates a runtime event listener behavior with given configuration.
+ *
* @param listenerConf
* configuration that determines additional sinks and event filters
* @param queue
@@ -42,46 +44,52 @@ object RuntimeEventListener {
def apply(
listenerConf: SimonaConfig.Simona.Runtime.Listener,
queue: Option[BlockingQueue[RuntimeEvent]],
- startDateTimeString: String
- ): Behavior[RuntimeEvent] = {
+ startDateTimeString: String,
+ ): Behavior[Request] = Behaviors.setup { ctx =>
val listeners = Iterable(
Some(
RuntimeEventLogSink(
- TimeUtil.withDefaults.toZonedDateTime(startDateTimeString)
+ TimeUtil.withDefaults.toZonedDateTime(startDateTimeString),
+ ctx.log,
)
),
queue.map(qu => RuntimeEventQueueSink(qu)),
- listenerConf.kafka.map(kafkaConf => RuntimeEventKafkaSink(kafkaConf))
+ listenerConf.kafka.map(kafkaConf =>
+ RuntimeEventKafkaSink(kafkaConf, ctx.log)
+ ),
).flatten
- RuntimeEventListener(
+ apply(
listeners,
- listenerConf.eventsToProcess
+ listenerConf.eventsToProcess,
)
}
- def apply(
+ private def apply(
listeners: Iterable[RuntimeEventSink],
eventsToProcess: Option[List[String]] = None,
- runtimeStats: RuntimeStats = RuntimeStats()
- ): Behavior[RuntimeEvent] = Behaviors
- .receive[RuntimeEvent] {
+ runtimeStats: RuntimeStats = RuntimeStats(),
+ ): Behavior[Request] = Behaviors
+ .receive[Request] {
case (_, PowerFlowFailed) =>
val updatedRuntimeData = runtimeStats
.copy(failedPowerFlows = runtimeStats.failedPowerFlows + 1)
RuntimeEventListener(listeners, eventsToProcess, updatedRuntimeData)
- case (ctx, event) =>
+ case (ctx, event: RuntimeEvent) =>
val process = eventsToProcess.forall(_.contains(event.id))
if (process)
- processEvent(listeners, event, runtimeStats, ctx.log)
+ processEvent(listeners, event, runtimeStats)
else
ctx.log.debug(
"Skipping event {} as it is not in the list of events to process.",
- event.id
+ event.id,
)
Behaviors.same
+
+ case (ctx, msg: DelayedStopHelper.StoppingMsg) =>
+ DelayedStopHelper.handleMsg((ctx, msg))
}
.receiveSignal { case (_, PostStop) =>
listeners.foreach(_.close())
@@ -92,8 +100,7 @@ object RuntimeEventListener {
listeners: Iterable[RuntimeEventSink],
event: RuntimeEvent,
runtimeStats: RuntimeStats,
- log: Logger
): Unit =
- listeners.foreach(_.handleRuntimeEvent(event, runtimeStats, log))
+ listeners.foreach(_.handleRuntimeEvent(event, runtimeStats))
}
diff --git a/src/main/scala/edu/ie3/simona/event/listener/SimonaListenerWithFilter.scala b/src/main/scala/edu/ie3/simona/event/listener/SimonaListenerWithFilter.scala
index 8d09e63c0a..666120e80e 100644
--- a/src/main/scala/edu/ie3/simona/event/listener/SimonaListenerWithFilter.scala
+++ b/src/main/scala/edu/ie3/simona/event/listener/SimonaListenerWithFilter.scala
@@ -49,7 +49,7 @@ abstract class SimonaListenerWithFilter(eventsToProcess: Option[List[String]])
case _ =>
log.debug(
"Skipping event {} as it is not in the list of events to process.",
- event.id
+ event.id,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/event/listener/Transformer3wResultSupport.scala b/src/main/scala/edu/ie3/simona/event/listener/Transformer3wResultSupport.scala
index 5eb68a5653..97e443a2b0 100644
--- a/src/main/scala/edu/ie3/simona/event/listener/Transformer3wResultSupport.scala
+++ b/src/main/scala/edu/ie3/simona/event/listener/Transformer3wResultSupport.scala
@@ -27,7 +27,7 @@ private[listener] trait Transformer3wResultSupport {
*/
final case class Transformer3wKey(
model: UUID,
- zdt: ZonedDateTime
+ zdt: ZonedDateTime,
)
/** Holding the result values of all three ports of a transformer
@@ -42,7 +42,7 @@ private[listener] trait Transformer3wResultSupport {
final case class AggregatedTransformer3wResult(
a: Option[PartialTransformer3wResult.PortA],
b: Option[PartialTransformer3wResult.PortB],
- c: Option[PartialTransformer3wResult.PortC]
+ c: Option[PartialTransformer3wResult.PortC],
) {
/** Check, if the results can be consolidated
@@ -65,29 +65,29 @@ private[listener] trait Transformer3wResultSupport {
aResult.input,
Quantities.getQuantity(
aResult.currentMagnitude.toAmperes,
- Units.AMPERE
+ Units.AMPERE,
),
Quantities.getQuantity(
aResult.currentAngle.toDegrees,
- PowerSystemUnits.DEGREE_GEOM
+ PowerSystemUnits.DEGREE_GEOM,
),
Quantities.getQuantity(
bResult.currentMagnitude.toAmperes,
- Units.AMPERE
+ Units.AMPERE,
),
Quantities.getQuantity(
bResult.currentAngle.toDegrees,
- PowerSystemUnits.DEGREE_GEOM
+ PowerSystemUnits.DEGREE_GEOM,
),
Quantities.getQuantity(
cResult.currentMagnitude.toAmperes,
- Units.AMPERE
+ Units.AMPERE,
),
Quantities.getQuantity(
cResult.currentAngle.toDegrees,
- PowerSystemUnits.DEGREE_GEOM
+ PowerSystemUnits.DEGREE_GEOM,
),
- aResult.tapPos
+ aResult.tapPos,
)
)
case _ =>
diff --git a/src/main/scala/edu/ie3/simona/event/notifier/Notifier.scala b/src/main/scala/edu/ie3/simona/event/notifier/Notifier.scala
index 916a97f9cb..b271a913e9 100644
--- a/src/main/scala/edu/ie3/simona/event/notifier/Notifier.scala
+++ b/src/main/scala/edu/ie3/simona/event/notifier/Notifier.scala
@@ -6,10 +6,10 @@
package edu.ie3.simona.event.notifier
-import org.apache.pekko.actor.{Actor, ActorRef}
import edu.ie3.simona.event.Event
+import org.apache.pekko.actor.ActorRef
-trait Notifier extends Actor {
+trait Notifier {
def listener: Iterable[ActorRef]
diff --git a/src/main/scala/edu/ie3/simona/event/notifier/NotifierConfig.scala b/src/main/scala/edu/ie3/simona/event/notifier/NotifierConfig.scala
index 3db8b438c6..bacd0be34d 100644
--- a/src/main/scala/edu/ie3/simona/event/notifier/NotifierConfig.scala
+++ b/src/main/scala/edu/ie3/simona/event/notifier/NotifierConfig.scala
@@ -14,5 +14,6 @@ package edu.ie3.simona.event.notifier
*/
final case class NotifierConfig(
simulationResultInfo: Boolean,
- powerRequestReply: Boolean
+ powerRequestReply: Boolean,
+ flexResult: Boolean,
)
diff --git a/src/main/scala/edu/ie3/simona/exceptions/CriticalFailureException.scala b/src/main/scala/edu/ie3/simona/exceptions/CriticalFailureException.scala
new file mode 100644
index 0000000000..6ab75e8af6
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/exceptions/CriticalFailureException.scala
@@ -0,0 +1,19 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.exceptions
+
+/** Error that should cause an actor to fail, which might terminate the whole
+ * simulation
+ * @param message
+ * The error message
+ */
+class CriticalFailureException(message: String) extends Exception(message) {
+ def this(message: String, cause: Throwable) = {
+ this(message)
+ initCause(cause)
+ }
+}
diff --git a/src/main/scala/edu/ie3/simona/exceptions/WeatherServiceException.scala b/src/main/scala/edu/ie3/simona/exceptions/WeatherServiceException.scala
index 5dcdaab211..08d109a2a6 100644
--- a/src/main/scala/edu/ie3/simona/exceptions/WeatherServiceException.scala
+++ b/src/main/scala/edu/ie3/simona/exceptions/WeatherServiceException.scala
@@ -15,7 +15,7 @@ package edu.ie3.simona.exceptions
*/
abstract class WeatherServiceException(
private val msg: String = "",
- private val cause: Throwable = None.orNull
+ private val cause: Throwable = None.orNull,
) extends Exception(msg, cause)
object WeatherServiceException {
@@ -30,7 +30,7 @@ object WeatherServiceException {
*/
final case class WeatherServiceInitializationException(
private val msg: String = "",
- private val cause: Throwable = None.orNull
+ private val cause: Throwable = None.orNull,
) extends WeatherServiceException(msg, cause)
/** Exception to be thrown, if looking up of weather fails
@@ -42,7 +42,7 @@ object WeatherServiceException {
*/
final case class WeatherLookupException(
private val msg: String = "",
- private val cause: Throwable = None.orNull
+ private val cause: Throwable = None.orNull,
) extends WeatherServiceException(msg, cause)
/** Exception to be thrown if the registration of the an agent fails
@@ -53,7 +53,7 @@ object WeatherServiceException {
*/
final case class InvalidRegistrationRequestException(
private val msg: String = "",
- private val cause: Throwable = None.orNull
+ private val cause: Throwable = None.orNull,
) extends WeatherServiceException(msg, cause)
}
diff --git a/src/main/scala/edu/ie3/simona/io/grid/CsvGridSource.scala b/src/main/scala/edu/ie3/simona/io/grid/CsvGridSource.scala
index fa7016a09a..7726640c1f 100644
--- a/src/main/scala/edu/ie3/simona/io/grid/CsvGridSource.scala
+++ b/src/main/scala/edu/ie3/simona/io/grid/CsvGridSource.scala
@@ -13,7 +13,7 @@ import edu.ie3.datamodel.models.input.container._
import edu.ie3.datamodel.models.input.thermal.{
ThermalBusInput,
ThermalHouseInput,
- ThermalStorageInput
+ ThermalStorageInput,
}
import java.nio.file.Path
@@ -23,7 +23,7 @@ object CsvGridSource {
def readThermalGrids(
csvSep: String,
baseFolder: Path,
- fileNamingStrategy: FileNamingStrategy
+ fileNamingStrategy: FileNamingStrategy,
): Map[ThermalBusInput, ThermalGrid] = {
val csvDataSource =
new CsvDataSource(csvSep, baseFolder, fileNamingStrategy)
@@ -56,7 +56,7 @@ object CsvGridSource {
bus -> new ThermalGrid(
bus,
h,
- s
+ s,
)
}.toMap
}
diff --git a/src/main/scala/edu/ie3/simona/io/grid/GridProvider.scala b/src/main/scala/edu/ie3/simona/io/grid/GridProvider.scala
index 4818b95fb1..24277fcd7c 100644
--- a/src/main/scala/edu/ie3/simona/io/grid/GridProvider.scala
+++ b/src/main/scala/edu/ie3/simona/io/grid/GridProvider.scala
@@ -11,7 +11,7 @@ import edu.ie3.datamodel.io.naming.FileNamingStrategy
import edu.ie3.datamodel.io.source.csv.CsvJointGridContainerSource
import edu.ie3.datamodel.models.input.container.{
JointGridContainer,
- ThermalGrid
+ ThermalGrid,
}
import edu.ie3.datamodel.models.input.thermal.ThermalBusInput
import edu.ie3.datamodel.utils.validation.ValidationUtils
@@ -31,7 +31,7 @@ object GridProvider extends LazyLogging {
def gridFromConfig(
simulationName: String,
- gridDataSource: SimonaConfig.Simona.Input.Grid.Datasource
+ gridDataSource: SimonaConfig.Simona.Input.Grid.Datasource,
): JointGridContainer = {
GridSourceType(gridDataSource.id.toLowerCase) match {
case GridSourceType.CSV =>
@@ -41,14 +41,14 @@ object GridProvider extends LazyLogging {
simulationName,
params.csvSep,
Path.of(params.directoryPath),
- params.isHierarchic
+ params.isHierarchic,
)
Try(ValidationUtils.check(jointGridContainer)) match {
case Failure(exception) =>
logger.warn(
s"Validation of grid ${jointGridContainer.getGridName} failed: \n\t{}",
- exception.getMessage
+ exception.getMessage,
)
case Success(_) =>
logger.debug(
@@ -85,7 +85,7 @@ object GridProvider extends LazyLogging {
.readThermalGrids(
params.csvSep,
Path.of(params.directoryPath),
- new FileNamingStrategy()
+ new FileNamingStrategy(),
)
case None =>
throw new RuntimeException(
diff --git a/src/main/scala/edu/ie3/simona/io/result/ResultEntityCsvSink.scala b/src/main/scala/edu/ie3/simona/io/result/ResultEntityCsvSink.scala
index 304dfde6a6..88e6d5a080 100644
--- a/src/main/scala/edu/ie3/simona/io/result/ResultEntityCsvSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/result/ResultEntityCsvSink.scala
@@ -17,6 +17,7 @@ import edu.ie3.util.io.FileIOUtils
import java.io.{BufferedWriter, File, FileWriter, Writer}
import java.lang
+import java.nio.file.Path
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
import scala.concurrent.{Await, Future}
@@ -40,7 +41,7 @@ final case class ResultEntityCsvSink private (
fileWriter: Writer,
resultEntityProcessor: ResultEntityProcessor,
compressOutputFiles: Boolean,
- delimiter: String
+ delimiter: String,
) extends ResultEntitySink
with LazyLogging {
@@ -85,7 +86,7 @@ final case class ResultEntityCsvSink private (
}
}
- /** Creat the initial the .csv-file and write the header in the first row
+ /** Create the initial the .csv-file and write the header in the first row
*
* @return
* a future with information on the I/O operation
@@ -109,17 +110,20 @@ final case class ResultEntityCsvSink private (
)
)
- FileIOUtils.gzip(outfileName).asScala.andThen {
- case Success(_) =>
- logger.debug(logPrefix(s"Compressed $outfileName."))
- FileIOUtils.deleteFileIfExists(outFileName).asScala
- case Failure(_) =>
- Future.failed[IOResult](
- new ProcessResultEventException(
- s"Failed to zip file $outFileName!"
+ FileIOUtils
+ .compressFile(Path.of(outFileName), Path.of(outFileName + ".gz"))
+ .asScala
+ .andThen {
+ case Success(_) =>
+ logger.debug(logPrefix(s"Compressed $outfileName."))
+ FileIOUtils.deleteFileIfExists(outFileName).asScala
+ case Failure(_) =>
+ Future.failed[IOResult](
+ new ProcessResultEventException(
+ s"Failed to zip file $outFileName!"
+ )
)
- )
- }
+ }
}
/** Contains all cleanup operations before closing this sink
@@ -155,7 +159,7 @@ object ResultEntityCsvSink {
outfileName: String,
resultEntityProcessor: ResultEntityProcessor,
compressOutputFiles: Boolean,
- delimiter: String = ","
+ delimiter: String = ",",
): ResultEntityCsvSink = {
val file = new File(outfileName)
@@ -168,7 +172,7 @@ object ResultEntityCsvSink {
writer,
resultEntityProcessor,
compressOutputFiles,
- delimiter
+ delimiter,
)
if (!existedBefore)
diff --git a/src/main/scala/edu/ie3/simona/io/result/ResultEntityInfluxDbSink.scala b/src/main/scala/edu/ie3/simona/io/result/ResultEntityInfluxDbSink.scala
index 3b101064b5..a8d0ec4a5d 100644
--- a/src/main/scala/edu/ie3/simona/io/result/ResultEntityInfluxDbSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/result/ResultEntityInfluxDbSink.scala
@@ -48,7 +48,7 @@ object ResultEntityInfluxDbSink {
def apply(
databaseUrl: String,
databaseName: String,
- scenarioName: String
+ scenarioName: String,
): Future[ResultEntityInfluxDbSink] =
Future.successful(
new ResultEntityInfluxDbSink(
diff --git a/src/main/scala/edu/ie3/simona/io/result/ResultEntityKafkaSink.scala b/src/main/scala/edu/ie3/simona/io/result/ResultEntityKafkaSink.scala
index b9d8f1f272..3effd9dd2d 100644
--- a/src/main/scala/edu/ie3/simona/io/result/ResultEntityKafkaSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/result/ResultEntityKafkaSink.scala
@@ -16,7 +16,7 @@ import io.confluent.kafka.serializers.AbstractKafkaSchemaSerDeConfig.SCHEMA_REGI
import org.apache.kafka.clients.producer.{
KafkaProducer,
ProducerConfig,
- ProducerRecord
+ ProducerRecord,
}
import org.apache.kafka.common.serialization.{Serdes, Serializer}
@@ -26,11 +26,11 @@ import scala.reflect.ClassTag
final case class ResultEntityKafkaSink[
V <: ResultEntity,
- P <: PlainResult
+ P <: PlainResult,
] private (
producer: KafkaProducer[String, P],
plainWriter: PlainWriter[V, P],
- topic: String
+ topic: String,
) extends ResultEntitySink {
override def handleResultEntity(resultEntity: ResultEntity): Unit = {
@@ -53,7 +53,7 @@ object ResultEntityKafkaSink {
simRunId: UUID,
bootstrapServers: String,
schemaRegistryUrl: String,
- linger: Int
+ linger: Int,
)(implicit
tag: ClassTag[R]
): ResultEntityKafkaSink[_ <: ResultEntity, _ <: PlainResult] = {
@@ -62,7 +62,7 @@ object ResultEntityKafkaSink {
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers)
props.put(
ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG,
- true
+ true,
) // exactly once delivery
val NodeResClass = classOf[NodeResult]
@@ -79,24 +79,24 @@ object ResultEntityKafkaSink {
schemaRegistryUrl: String,
props: Properties,
topic: String,
- writer: PlainWriter[F, P]
+ writer: PlainWriter[F, P],
): ResultEntityKafkaSink[F, P] = {
val keySerializer = Serdes.String().serializer()
val valueSerializer: Serializer[P] = reflectionSerializer4S[P]
valueSerializer.configure(
Map(SCHEMA_REGISTRY_URL_CONFIG -> schemaRegistryUrl).asJava,
- false
+ false,
)
ResultEntityKafkaSink(
new KafkaProducer[String, P](
props,
keySerializer,
- valueSerializer
+ valueSerializer,
),
writer,
- topic
+ topic,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/io/result/ResultSinkType.scala b/src/main/scala/edu/ie3/simona/io/result/ResultSinkType.scala
index d4c444fdd7..3510810062 100644
--- a/src/main/scala/edu/ie3/simona/io/result/ResultSinkType.scala
+++ b/src/main/scala/edu/ie3/simona/io/result/ResultSinkType.scala
@@ -20,7 +20,7 @@ object ResultSinkType {
final case class Csv(
fileFormat: String = ".csv",
filePrefix: String = "",
- fileSuffix: String = ""
+ fileSuffix: String = "",
) extends ResultSinkType
final case class InfluxDb1x(url: String, database: String, scenario: String)
@@ -31,12 +31,12 @@ object ResultSinkType {
runId: UUID,
bootstrapServers: String,
schemaRegistryUrl: String,
- linger: Int
+ linger: Int,
) extends ResultSinkType
def apply(
sinkConfig: SimonaConfig.Simona.Output.Sink,
- runName: String
+ runName: String,
): ResultSinkType = {
val sink: Seq[Any] =
Seq(sinkConfig.csv, sinkConfig.influxDb1x, sinkConfig.kafka).flatten
@@ -57,7 +57,7 @@ object ResultSinkType {
UUID.fromString(params.runId),
params.bootstrapServers,
params.schemaRegistryUrl,
- params.linger
+ params.linger,
)
case None =>
throw new IllegalArgumentException(
diff --git a/src/main/scala/edu/ie3/simona/io/result/plain/PlainResult.scala b/src/main/scala/edu/ie3/simona/io/result/plain/PlainResult.scala
index a590441007..19bb41283d 100644
--- a/src/main/scala/edu/ie3/simona/io/result/plain/PlainResult.scala
+++ b/src/main/scala/edu/ie3/simona/io/result/plain/PlainResult.scala
@@ -40,6 +40,6 @@ object PlainResult {
uuid: UUID,
inputModel: UUID,
vMag: Double,
- vAng: Double
+ vAng: Double,
) extends PlainResult
}
diff --git a/src/main/scala/edu/ie3/simona/io/result/plain/PlainWriter.scala b/src/main/scala/edu/ie3/simona/io/result/plain/PlainWriter.scala
index 993f43d0c9..597dff5ac1 100644
--- a/src/main/scala/edu/ie3/simona/io/result/plain/PlainWriter.scala
+++ b/src/main/scala/edu/ie3/simona/io/result/plain/PlainWriter.scala
@@ -62,7 +62,7 @@ object PlainWriter {
full.getUuid,
full.getInputModel,
full.getvMag.getValue.doubleValue(),
- full.getvAng.getValue.doubleValue()
+ full.getvAng.getValue.doubleValue(),
)
}
@@ -72,7 +72,7 @@ object PlainWriter {
ZonedDateTime.parse(plain.time, timeFormatter),
plain.inputModel,
Quantities.getQuantity(plain.vMag, PowerSystemUnits.PU),
- Quantities.getQuantity(plain.vAng, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(plain.vAng, PowerSystemUnits.DEGREE_GEOM),
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventKafkaSink.scala b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventKafkaSink.scala
index dea2b50642..4698d25215 100644
--- a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventKafkaSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventKafkaSink.scala
@@ -5,6 +5,7 @@
*/
package edu.ie3.simona.io.runtime
+
import com.sksamuel.avro4s.RecordFormat
import edu.ie3.simona.config.SimonaConfig.RuntimeKafkaParams
import edu.ie3.simona.event.RuntimeEvent
@@ -16,7 +17,7 @@ import io.confluent.kafka.serializers.AbstractKafkaSchemaSerDeConfig.SCHEMA_REGI
import org.apache.kafka.clients.producer.{
KafkaProducer,
ProducerConfig,
- ProducerRecord
+ ProducerRecord,
}
import org.apache.kafka.common.serialization.{Serdes, Serializer}
import org.slf4j.Logger
@@ -26,23 +27,26 @@ import scala.jdk.CollectionConverters._
/** Runtime event sink that sends events related to the simulation ending to a
* kafka topic.
+ *
* @param producer
* the kafka producer to use
* @param simRunId
* the id of this simulation run
* @param topic
* the topic to send the events to
+ * @param log
+ * The logger to use
*/
final case class RuntimeEventKafkaSink(
producer: KafkaProducer[String, SimonaEndMessage],
simRunId: UUID,
- topic: String
+ topic: String,
+ log: Logger,
) extends RuntimeEventSink {
override def handleRuntimeEvent(
runtimeEvent: RuntimeEvent,
runtimeStats: RuntimeStats,
- log: Logger
): Unit = {
(runtimeEvent match {
case Done(_, _, errorInSim) =>
@@ -74,7 +78,8 @@ final case class RuntimeEventKafkaSink(
object RuntimeEventKafkaSink {
def apply(
- config: RuntimeKafkaParams
+ config: RuntimeKafkaParams,
+ log: Logger,
): RuntimeEventKafkaSink = {
val simRunId = UUID.fromString(config.runId)
@@ -83,7 +88,7 @@ object RuntimeEventKafkaSink {
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.bootstrapServers)
props.put(
ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG,
- true
+ true,
) // exactly once delivery
implicit val recordFormat: RecordFormat[SimonaEndMessage] =
@@ -95,23 +100,24 @@ object RuntimeEventKafkaSink {
valueSerializer.configure(
Map(SCHEMA_REGISTRY_URL_CONFIG -> config.schemaRegistryUrl).asJava,
- false
+ false,
)
RuntimeEventKafkaSink(
new KafkaProducer[String, SimonaEndMessage](
props,
keySerializer,
- valueSerializer
+ valueSerializer,
),
simRunId,
- config.topic
+ config.topic,
+ log,
)
}
final case class SimonaEndMessage(
simRunId: UUID,
failedPowerFlows: Int,
- error: Boolean
+ error: Boolean,
)
}
diff --git a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventLogSink.scala b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventLogSink.scala
index 833b5907dd..5f6b74b906 100644
--- a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventLogSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventLogSink.scala
@@ -5,6 +5,7 @@
*/
package edu.ie3.simona.io.runtime
+
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.event.RuntimeEvent._
import edu.ie3.simona.io.runtime.RuntimeEventSink.RuntimeStats
@@ -15,18 +16,21 @@ import org.slf4j.Logger
import java.time.ZonedDateTime
/** Runtime event sink that just logs all received events.
+ *
* @param simulationStartDate
* the simulation start date time, used for calculating simulation time from
* ticks
+ * @param log
+ * The logger to use
*/
final case class RuntimeEventLogSink(
- simulationStartDate: ZonedDateTime
+ simulationStartDate: ZonedDateTime,
+ log: Logger,
) extends RuntimeEventSink {
override def handleRuntimeEvent(
runtimeEvent: RuntimeEvent,
runtimeStats: RuntimeStats,
- log: Logger
): Unit =
runtimeEvent match {
case Initializing =>
diff --git a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventQueueSink.scala b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventQueueSink.scala
index dad7a65686..b10393a030 100644
--- a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventQueueSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventQueueSink.scala
@@ -5,9 +5,9 @@
*/
package edu.ie3.simona.io.runtime
+
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.io.runtime.RuntimeEventSink.RuntimeStats
-import org.slf4j.Logger
import java.util.concurrent.BlockingQueue
@@ -21,7 +21,6 @@ final case class RuntimeEventQueueSink(queue: BlockingQueue[RuntimeEvent])
override def handleRuntimeEvent(
runtimeEvent: RuntimeEvent,
runtimeStats: RuntimeStats,
- log: Logger
): Unit =
queue.put(runtimeEvent)
diff --git a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventSink.scala b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventSink.scala
index 496a2166e3..2638e8fe00 100644
--- a/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventSink.scala
+++ b/src/main/scala/edu/ie3/simona/io/runtime/RuntimeEventSink.scala
@@ -8,7 +8,6 @@ package edu.ie3.simona.io.runtime
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.io.runtime.RuntimeEventSink.RuntimeStats
-import org.slf4j.Logger
/** Runtime event sinks are handling runtime events. More than one sink can
* exist in parallel.
@@ -22,13 +21,10 @@ trait RuntimeEventSink {
* The runtime event that should be processed
* @param runtimeStats
* The runtime statistical data
- * @param log
- * The logger to use
*/
def handleRuntimeEvent(
runtimeEvent: RuntimeEvent,
runtimeStats: RuntimeStats,
- log: Logger
): Unit
/** Contains all cleanup operations before closing this sink. Should be
diff --git a/src/main/scala/edu/ie3/simona/logging/SimonaFSMActorLogging.scala b/src/main/scala/edu/ie3/simona/logging/SimonaFSMActorLogging.scala
index cfc618bc85..9a4b58b069 100644
--- a/src/main/scala/edu/ie3/simona/logging/SimonaFSMActorLogging.scala
+++ b/src/main/scala/edu/ie3/simona/logging/SimonaFSMActorLogging.scala
@@ -17,7 +17,7 @@ trait SimonaFSMActorLogging extends ActorLogging with SimonaLogging {
context.system,
() => stateName,
this,
- actorName
+ actorName,
)
}
diff --git a/src/main/scala/edu/ie3/simona/logging/SimonaLogging.scala b/src/main/scala/edu/ie3/simona/logging/SimonaLogging.scala
index 5cb449ab3d..933eed80a7 100644
--- a/src/main/scala/edu/ie3/simona/logging/SimonaLogging.scala
+++ b/src/main/scala/edu/ie3/simona/logging/SimonaLogging.scala
@@ -11,7 +11,7 @@ import org.apache.pekko.event.{
LogSource,
LoggingAdapter,
LoggingBus,
- LoggingFilter
+ LoggingFilter,
}
import edu.ie3.simona.actor.SimonaActorNaming
@@ -33,7 +33,7 @@ object SimonaLogging {
system: ActorSystem,
state: () => S,
logSource: T,
- agentName: String
+ agentName: String,
): LoggingAdapter = {
val (str, clazz) = LogSource(logSource, system)
SimonaBusLogging(
@@ -41,14 +41,14 @@ object SimonaLogging {
str,
clazz,
logFilter(system),
- () => fsmPrefix(agentName, state)
+ () => fsmPrefix(agentName, state),
)
}
private[logging] def createAdapter[T: LogSource](
system: ActorSystem,
logSource: T,
- agentName: String
+ agentName: String,
): LoggingAdapter = {
val (str, clazz) = LogSource(logSource, system)
SimonaBusLogging(
@@ -56,7 +56,7 @@ object SimonaLogging {
str,
clazz,
logFilter(system),
- () => actorPrefix(agentName)
+ () => actorPrefix(agentName),
)
}
@@ -79,7 +79,7 @@ object SimonaLogging {
logSource: String,
logClass: Class[_],
loggingFilter: LoggingFilter,
- prefix: () => String
+ prefix: () => String,
) extends LoggingAdapter {
import org.apache.pekko.event.Logging._
diff --git a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala
index 8aa16d5174..3d236a1f41 100644
--- a/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala
+++ b/src/main/scala/edu/ie3/simona/logging/logback/LogbackConfiguration.scala
@@ -39,7 +39,7 @@ object LogbackConfiguration extends LazyLogging {
log,
"simona-default",
fileLoggerFilterList,
- loggerContext
+ loggerContext,
)
)
@@ -61,7 +61,7 @@ object LogbackConfiguration extends LazyLogging {
logPath: String,
appenderName: String,
maybeFilterList: Option[Seq[Filter[ILoggingEvent]]],
- loggerContext: LoggerContext
+ loggerContext: LoggerContext,
): FileAppender[ILoggingEvent] = {
val layoutEncoder = new PatternLayoutEncoder
diff --git a/src/main/scala/edu/ie3/simona/main/RunSimona.scala b/src/main/scala/edu/ie3/simona/main/RunSimona.scala
index 449a16df25..707da41338 100644
--- a/src/main/scala/edu/ie3/simona/main/RunSimona.scala
+++ b/src/main/scala/edu/ie3/simona/main/RunSimona.scala
@@ -6,21 +6,17 @@
package edu.ie3.simona.main
-import java.util.Locale
-
-import org.apache.pekko.actor.{ActorRef, ActorSystem}
-import org.apache.pekko.pattern.gracefulStop
-import org.apache.pekko.util.Timeout
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.simona.sim.setup.SimonaSetup
import edu.ie3.util.scala.quantities.QuantityUtil
+import org.apache.pekko.util.Timeout
-import scala.concurrent.Future
+import java.util.Locale
import scala.concurrent.duration.FiniteDuration
import scala.util.Random
/** Trait to be mixed in all implementations that should be used to run a simona
- * simulation. For a sample implemenation see [[RunSimonaStandalone]].
+ * simulation. For a sample implementation see [[RunSimonaStandalone]].
*
* @version 0.1
* @since 01.07.20
@@ -39,36 +35,33 @@ trait RunSimona[T <: SimonaSetup] extends LazyLogging {
printOpener()
- setup(args).foreach(run)
+ val simonaSetup = setup(args)
+
+ val successful = run(simonaSetup)
printGoodbye()
- Thread.sleep(
- 1000
- ) // prevents cutting of the log when having a fast simulation
- System.exit(0)
- }
+ // prevents cutting of the log when having a fast simulation
+ Thread.sleep(1000)
- def shutdownGracefully(
- simonaSim: ActorRef
- )(implicit timeout: FiniteDuration): Future[Boolean] = {
- gracefulStop(simonaSim, timeout)
+ System.exit(if (successful) 0 else 1)
}
// a fancy opener
- protected def printOpener(): Unit = {
+ private def printOpener(): Unit = {
logger.info(
s"Starting SIMONA with interface '${getClass.getSimpleName.replaceAll("\\$", "")}'.\n" + " _____ ______ _______ _ _____ \n / ___// _/ |/ / __ \\/ | / / |\n \\__ \\ / // /|_/ / / / / |/ / /| |\n ___/ // // / / / /_/ / /| / ___ |\n/____/___/_/ /_/\\____/_/ |_/_/ |_|\n "
)
}
- def printGoodbye(): Unit = {
+ private def printGoodbye(): Unit = {
val myWords = Array(
"\"Vielleicht ist heute ein besonders guter Tag zum Sterben.\" - Worf (in Star Trek: Der erste Kontakt)",
"\"Assimiliert das!\" - Worf (in Star Trek: Der erste Kontakt)",
"\"Lebe lang und erfolgreich.\" - Gruppe von Vulkanier (in Star Trek: Der erste Kontakt)",
"\"Ich bin der Anfang, das Ende, die Eine, die Viele ist. Ich bin die Borg.\" - Borg-Königin (in Star Trek: Der erste Kontakt)",
- "\"A horse! A horse! My kingdom for a horse!\" - King Richard III (in Shakespeare's Richard III, 1594)"
+ "\"A horse! A horse! My kingdom for a horse!\" - King Richard III (in Shakespeare's Richard III, 1594)",
+ "\"Und wenn du lange in einen Abgrund blickst, blickt der Abgrund auch in dich hinein\" - F. Nietzsche",
)
val rand = new Random
@@ -78,21 +71,32 @@ trait RunSimona[T <: SimonaSetup] extends LazyLogging {
}
/** Method to be implemented to setup everything that is necessary for a
- * sequence of simulations. This is by creating an instance of
- * [[SimonaSetup]] implementation
+ * simulations. This is by creating an instance of [[SimonaSetup]]
+ * implementation
*
* @param args
* arguments provided by the command line
* @return
- * the setup instances
+ * the setup instance
*/
- def setup(args: Array[String]): Seq[T]
+ def setup(args: Array[String]): T
/** Actually run the simona simulation using the provided [[SimonaSetup]]
*
* @param simonaSetup
* the setup data that should be used
+ * @return
+ * Whether the simualtion was successful or not
+ */
+ def run(simonaSetup: T): Boolean
+
+}
+
+object RunSimona {
+
+ /** Reported back from the scheduler if an error occurred during the
+ * simulation
*/
- def run(simonaSetup: T): Unit
+ final case class SimonaEnded(successful: Boolean)
}
diff --git a/src/main/scala/edu/ie3/simona/main/RunSimonaStandalone.scala b/src/main/scala/edu/ie3/simona/main/RunSimonaStandalone.scala
index 5a8bee6d2e..d0a50802b8 100644
--- a/src/main/scala/edu/ie3/simona/main/RunSimonaStandalone.scala
+++ b/src/main/scala/edu/ie3/simona/main/RunSimonaStandalone.scala
@@ -6,35 +6,26 @@
package edu.ie3.simona.main
-import org.apache.pekko.actor.ActorSystem
-
-import java.util.concurrent.TimeUnit
-import org.apache.pekko.pattern.ask
-import org.apache.pekko.util.Timeout
import edu.ie3.simona.config.{ArgsParser, ConfigFailFast, SimonaConfig}
-import edu.ie3.simona.sim.SimMessage.{
- InitSim,
- SimulationFailure,
- SimulationSuccessful,
- StartSimulation
-}
+import edu.ie3.simona.main.RunSimona._
import edu.ie3.simona.sim.SimonaSim
import edu.ie3.simona.sim.setup.SimonaStandaloneSetup
+import org.apache.pekko.actor.typed.scaladsl.AskPattern._
+import org.apache.pekko.actor.typed.{ActorSystem, Scheduler}
+import org.apache.pekko.util.Timeout
import scala.concurrent.Await
+import scala.concurrent.duration.DurationInt
/** Run a standalone simulation of simona
*
- * @version 0.1
* @since 01.07.20
*/
object RunSimonaStandalone extends RunSimona[SimonaStandaloneSetup] {
- override implicit val timeout: Timeout = Timeout(50000, TimeUnit.SECONDS)
+ override implicit val timeout: Timeout = Timeout(12.hours)
- override def setup(
- args: Array[String]
- ): Seq[SimonaStandaloneSetup] = {
+ override def setup(args: Array[String]): SimonaStandaloneSetup = {
// get the config and prepare it with the provided args
val (arguments, parsedConfig) = ArgsParser.prepareConfig(args)
@@ -42,36 +33,30 @@ object RunSimonaStandalone extends RunSimona[SimonaStandaloneSetup] {
val simonaConfig = SimonaConfig(parsedConfig)
ConfigFailFast.check(parsedConfig, simonaConfig)
- Seq(
- SimonaStandaloneSetup(
- parsedConfig,
- SimonaStandaloneSetup.buildResultFileHierarchy(parsedConfig),
- mainArgs = arguments.mainArgs
- )
+ SimonaStandaloneSetup(
+ parsedConfig,
+ SimonaStandaloneSetup.buildResultFileHierarchy(parsedConfig),
+ mainArgs = arguments.mainArgs,
)
}
- override def run(
- simonaSetup: SimonaStandaloneSetup
- ): Unit = {
- val actorSystem: ActorSystem = simonaSetup.buildActorSystem.apply()
- // build the simulation container actor
- val simonaSim = actorSystem.actorOf(
- SimonaSim.props(simonaSetup)
+ override def run(simonaSetup: SimonaStandaloneSetup): Boolean = {
+ val simonaSim = ActorSystem(
+ SimonaSim(simonaSetup),
+ name = "Simona",
+ config = simonaSetup.typeSafeConfig,
)
+ implicit val scheduler: Scheduler = simonaSim.scheduler
+
// run the simulation
- val terminated = simonaSim ? InitSim
+ val terminated = simonaSim.ask[SimonaEnded](ref => SimonaSim.Start(ref))
Await.result(terminated, timeout.duration) match {
- case SimulationFailure | SimulationSuccessful =>
- Await.ready(shutdownGracefully(simonaSim), timeout.duration)
- Await.ready(actorSystem.terminate(), timeout.duration)
+ case SimonaEnded(successful) =>
+ simonaSim.terminate()
- case unknown =>
- throw new RuntimeException(
- s"Unexpected message from SimonaSim $unknown"
- )
+ successful
}
}
diff --git a/src/main/scala/edu/ie3/simona/model/SystemComponent.scala b/src/main/scala/edu/ie3/simona/model/SystemComponent.scala
index a81c235bf9..623e5a58c3 100644
--- a/src/main/scala/edu/ie3/simona/model/SystemComponent.scala
+++ b/src/main/scala/edu/ie3/simona/model/SystemComponent.scala
@@ -13,7 +13,7 @@ import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.models.OperationTime
import edu.ie3.simona.exceptions.{
InvalidActionRequestException,
- InvalidParameterException
+ InvalidParameterException,
}
import edu.ie3.simona.util.TickUtil._
import edu.ie3.util.scala.OperationInterval
@@ -35,7 +35,7 @@ import scala.util.{Failure, Success, Try}
abstract class SystemComponent(
uuid: UUID,
id: String,
- operationInterval: OperationInterval
+ operationInterval: OperationInterval,
) extends LazyLogging {
private val elementType: String = this.getClass.getSimpleName
@@ -107,7 +107,7 @@ case object SystemComponent {
def determineOperationInterval(
startDate: ZonedDateTime,
endDate: ZonedDateTime,
- operationTime: OperationTime
+ operationTime: OperationTime,
): OperationInterval = {
val operationStartOpt = operationTime.getStartDate.toScala
val operationEndOpt = operationTime.getEndDate.toScala
diff --git a/src/main/scala/edu/ie3/simona/model/control/GridControls.scala b/src/main/scala/edu/ie3/simona/model/control/GridControls.scala
new file mode 100644
index 0000000000..b2d00cba17
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/control/GridControls.scala
@@ -0,0 +1,23 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.control
+
+/** Collection of grid-related control strategies
+ *
+ * @param transformerControlGroups
+ * Transformer control groups
+ */
+final case class GridControls(
+ transformerControlGroups: Set[TransformerControlGroupModel]
+)
+
+object GridControls {
+
+ /** Represents an empty GridControls group
+ */
+ def empty: GridControls = GridControls(Set.empty)
+}
diff --git a/src/main/scala/edu/ie3/simona/model/control/TransformerControlGroupModel.scala b/src/main/scala/edu/ie3/simona/model/control/TransformerControlGroupModel.scala
new file mode 100644
index 0000000000..2354770480
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/control/TransformerControlGroupModel.scala
@@ -0,0 +1,161 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.control
+
+import breeze.math.Complex
+import edu.ie3.datamodel.models.input.MeasurementUnitInput
+import edu.ie3.powerflow.model.NodeData.StateData
+import edu.ie3.powerflow.model.PowerFlowResult.SuccessFullPowerFlowResult
+import edu.ie3.simona.config.SimonaConfig
+import edu.ie3.simona.config.SimonaConfig.TransformerControlGroup
+import edu.ie3.simona.model.control.TransformerControlGroupModel.{
+ RegulationCriterion,
+ harmonizeRegulationNeeds,
+}
+import squants.{Dimensionless, Each}
+
+import java.util.UUID
+
+/** Business logic for a transformer control group. It's main purpose is to
+ * determine, if there is any regulation need and if yes, to what extent (here:
+ * voltage raise or reduction to achieve)
+ *
+ * @param measuredNodes
+ * The nodes (with voltage measurement) to consider
+ * @param regulationCriterion
+ * Function that determines the regulation need
+ */
+final case class TransformerControlGroupModel(
+ measuredNodes: Set[UUID],
+ regulationCriterion: RegulationCriterion,
+) {
+
+ /** Based on the given successful power flow result, determine the difference
+ * in voltage magnitude, that needs to be achieved by regulating the
+ * transformer tap position
+ *
+ * @param result
+ * Power flow result to account for
+ * @param uuidToIndex
+ * Mapping from node's uuid to nodal index
+ * @return
+ * Optional voltage magnitude, that a transformer tap regulation needs to
+ * achieve
+ */
+ def determineRegulationNeed(
+ result: SuccessFullPowerFlowResult,
+ uuidToIndex: Map[UUID, Int],
+ ): Option[Dimensionless] = {
+ val regulationNeeds = result.nodeData
+ .filter { case StateData(resultNodeIndex, _, _, _) =>
+ measuredNodes.exists { uuid =>
+ resultNodeIndex == uuidToIndex(uuid)
+ }
+ }
+ .flatMap { case StateData(_, _, voltage, _) =>
+ regulationCriterion(voltage)
+ }
+ harmonizeRegulationNeeds(regulationNeeds)
+ }
+}
+
+object TransformerControlGroupModel {
+ type RegulationCriterion =
+ Complex => Option[Dimensionless]
+
+ /** Build business models for control groups
+ *
+ * @param measurementUnitInput
+ * Set of [[MeasurementUnitInput]] s
+ * @param config
+ * List of configs for control groups
+ * @return
+ * A set of control group business models
+ */
+ def buildControlGroups(
+ measurementUnitInput: Set[MeasurementUnitInput],
+ config: Iterable[SimonaConfig.TransformerControlGroup],
+ ): Set[TransformerControlGroupModel] = config.map {
+ case TransformerControlGroup(measurements, _, vMax, vMin) =>
+ val nodeUuids =
+ determineNodeUuids(measurementUnitInput, measurements.toSet)
+ TransformerControlGroupModel(nodeUuids, regulationFunction(vMax, vMin))
+ }.toSet
+
+ /** Determine the uuids of the nodes to control
+ *
+ * @param measurementUnitInput
+ * Collection of all known [[MeasurementUnitInput]] s
+ * @param measurementConfigs
+ * Collection of all uuids, denoting which of the [[MeasurementUnitInput]]
+ * s does belong to this control group
+ * @return
+ * A set of relevant nodal uuids
+ */
+ private def determineNodeUuids(
+ measurementUnitInput: Set[MeasurementUnitInput],
+ measurementConfigs: Set[String],
+ ): Set[UUID] =
+ measurementUnitInput
+ .filter(input =>
+ measurementConfigs.contains(input.getUuid.toString) && input.getVMag
+ )
+ .map(_.getNode.getUuid)
+
+ /** Determine the regulation criterion of the nodes to control
+ *
+ * @param vMax
+ * Maximum voltage limit
+ * @param vMin
+ * Minimum voltage limit
+ * @return
+ * The regulation need, if applicable
+ */
+ private def regulationFunction(
+ vMax: Double,
+ vMin: Double,
+ ): RegulationCriterion = { (voltage: Complex) =>
+ voltage.abs match {
+ case vMag if vMag > vMax =>
+ Some(vMax - vMag).map(Each(_))
+ case vMag if vMag < vMin =>
+ Some(vMin - vMag).map(Each(_))
+ case _ => None
+ }
+ }
+
+ /** Function to harmonize contrary requests for regulation
+ *
+ * @param regulationRequests:
+ * Array of all regulation requests
+ * @return
+ * None in case of contrary requests, else the highest or lowest voltage
+ * depending of the direction for regulation
+ */
+ private def harmonizeRegulationNeeds(
+ regulationRequests: Array[Dimensionless]
+ ): Option[Dimensionless] = {
+ val negativeRequests = regulationRequests.filter(_ < Each(0d))
+ val positiveRequests = regulationRequests.filter(_ > Each(0d))
+
+ (negativeRequests.nonEmpty, positiveRequests.nonEmpty) match {
+ case (true, true) =>
+ /* There are requests for higher and lower voltages at the same time => do nothing! */
+ None
+ case (true, false) =>
+ /* There are only requests for lower voltages => decide for the lowest required voltage */
+ negativeRequests.minOption
+ case (false, true) =>
+ /* There are only requests for higher voltages => decide for the highest required voltage */
+ positiveRequests.maxOption
+ case _ =>
+ None
+ }
+
+ }
+
+}
diff --git a/src/main/scala/edu/ie3/simona/model/em/EmTools.scala b/src/main/scala/edu/ie3/simona/model/em/EmTools.scala
new file mode 100644
index 0000000000..8a10b9c335
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/em/EmTools.scala
@@ -0,0 +1,78 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.em
+
+import edu.ie3.simona.exceptions.CriticalFailureException
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.{
+ IssueFlexControl,
+ IssueNoControl,
+ IssuePowerControl,
+ ProvideFlexOptions,
+}
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
+import squants.Power
+
+/** Tools used by agents that engage with energy management and flexibility
+ */
+object EmTools {
+
+ /** Determines the set point given a flex options message and a flex control
+ * message. Also validates the resulting power.
+ *
+ * @param flexOptionsMsg
+ * The flex options message
+ * @param flexCtrl
+ * The flex control message
+ * @return
+ * The resulting power set point
+ */
+ def determineFlexPower(
+ flexOptionsMsg: ProvideFlexOptions,
+ flexCtrl: IssueFlexControl,
+ ): Power =
+ flexOptionsMsg match {
+ case flexOptions: ProvideMinMaxFlexOptions =>
+ flexCtrl match {
+ case IssuePowerControl(_, setPower) =>
+ // sanity check: setPower is in range of latest flex options
+ checkSetPower(flexOptions, setPower)
+
+ setPower
+
+ case IssueNoControl(_) =>
+ // no override, take reference power
+ flexOptions.ref
+ }
+
+ case unknownFlexOpt =>
+ throw new CriticalFailureException(
+ s"Unknown/unfitting flex messages $unknownFlexOpt"
+ )
+ }
+
+ /** Checks whether given setPower fits the provided flex options, i.e. whether
+ * the set point is feasible given the flex options.
+ *
+ * @param flexOptions
+ * The flex options that the set point has to fit
+ * @param setPower
+ * The set point
+ */
+ def checkSetPower(
+ flexOptions: ProvideMinMaxFlexOptions,
+ setPower: Power,
+ ): Unit = {
+ if (setPower < flexOptions.min)
+ throw new CriticalFailureException(
+ s"The set power $setPower for ${flexOptions.modelUuid} must not be lower than the minimum power ${flexOptions.min}!"
+ )
+ else if (setPower > flexOptions.max)
+ throw new CriticalFailureException(
+ s"The set power $setPower for ${flexOptions.modelUuid} must not be greater than the maximum power ${flexOptions.max}!"
+ )
+ }
+}
diff --git a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala
index fd1ea11df2..f2eab5ff1d 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/GridModel.scala
@@ -11,13 +11,16 @@ import breeze.math.Complex
import edu.ie3.datamodel.exceptions.InvalidGridException
import edu.ie3.datamodel.models.input.connector._
import edu.ie3.datamodel.models.input.container.SubGridContainer
+import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.exceptions.GridInconsistencyException
+import edu.ie3.simona.exceptions.agent.GridAgentInitializationException
import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.control.{GridControls, TransformerControlGroupModel}
import edu.ie3.simona.model.grid.GridModel.GridComponents
import edu.ie3.simona.model.grid.Transformer3wPowerFlowCase.{
PowerFlowCaseA,
PowerFlowCaseB,
- PowerFlowCaseC
+ PowerFlowCaseC,
}
import edu.ie3.simona.util.CollectionUtils
import org.jgrapht.Graph
@@ -36,7 +39,8 @@ import scala.jdk.CollectionConverters._
final case class GridModel(
subnetNo: Int,
mainRefSystem: RefSystem,
- gridComponents: GridComponents
+ gridComponents: GridComponents,
+ gridControls: GridControls,
) {
// init nodeUuidToIndexMap
@@ -50,7 +54,7 @@ final case class GridModel(
nodeModel.uuid,
throw new InvalidGridException(
s"Requested slack node with uuid ${nodeModel.uuid} is not part of nodeToIndexMap!"
- )
+ ),
)
)
.toVector
@@ -58,16 +62,21 @@ final case class GridModel(
def nodeUuidToIndexMap: Map[UUID, Int] = _nodeUuidToIndexMap
}
-case object GridModel {
+object GridModel {
def apply(
subGridContainer: SubGridContainer,
refSystem: RefSystem,
startDate: ZonedDateTime,
- endDate: ZonedDateTime
- ): GridModel = {
- buildAndValidate(subGridContainer, refSystem, startDate, endDate)
- }
+ endDate: ZonedDateTime,
+ simonaConfig: SimonaConfig,
+ ): GridModel = buildAndValidate(
+ subGridContainer,
+ refSystem,
+ startDate,
+ endDate,
+ simonaConfig,
+ )
/** structure that represents all grid components that are needed by a grid
* model
@@ -77,7 +86,7 @@ case object GridModel {
lines: Set[LineModel],
transformers: Set[TransformerModel],
transformers3w: Set[Transformer3wModel],
- switches: Set[SwitchModel]
+ switches: Set[SwitchModel],
)
/** Checks the availability of node calculation models, that are connected by
@@ -93,7 +102,7 @@ case object GridModel {
*/
private def getConnectedNodes(
connector: ConnectorInput,
- nodes: Seq[NodeModel]
+ nodes: Seq[NodeModel],
): (NodeModel, NodeModel) = {
val nodeAOpt: Option[NodeModel] =
nodes.find(_.uuid.equals(connector.getNodeA.getUuid))
@@ -131,7 +140,7 @@ case object GridModel {
*/
private def getConnectedNodes(
transformerInput: Transformer3WInput,
- nodes: Seq[NodeModel]
+ nodes: Seq[NodeModel],
): (NodeModel, NodeModel, NodeModel) = {
val (nodeA, nodeB) =
getConnectedNodes(transformerInput.asInstanceOf[ConnectorInput], nodes)
@@ -165,7 +174,7 @@ case object GridModel {
def composeAdmittanceMatrix(
nodeUuidToIndexMap: Map[UUID, Int],
- gridComponents: GridComponents
+ gridComponents: GridComponents,
): DenseMatrix[Complex] = {
val _returnAdmittanceMatrixIfValid
@@ -176,7 +185,7 @@ case object GridModel {
{ entry: Complex =>
!entry.imag.isNaN & !entry.real.isNaN & entry.imag.isFinite & entry.real.isFinite
},
- admittanceMatrix
+ admittanceMatrix,
)
)
throw new RuntimeException(s"Admittance matrix is illegal.")
@@ -191,17 +200,17 @@ case object GridModel {
val linesAdmittanceMatrix = buildAssetAdmittanceMatrix(
nodeUuidToIndexMap,
gridComponents.lines,
- getLinesAdmittance
+ getLinesAdmittance,
)
val trafoAdmittanceMatrix = buildAssetAdmittanceMatrix(
nodeUuidToIndexMap,
gridComponents.transformers,
- getTransformerAdmittance
+ getTransformerAdmittance,
)
val trafo3wAdmittanceMatrix = buildAssetAdmittanceMatrix(
nodeUuidToIndexMap,
gridComponents.transformers3w,
- getTransformer3wAdmittance
+ getTransformer3wAdmittance,
)
_returnAdmittanceMatrixIfValid(
@@ -214,8 +223,8 @@ case object GridModel {
assets: Set[C],
getAssetAdmittance: (
Map[UUID, Int],
- C
- ) => (Int, Int, Complex, Complex, Complex)
+ C,
+ ) => (Int, Int, Complex, Complex, Complex),
): DenseMatrix[Complex] = {
val matrixDimension = nodeUuidToIndexMap.values.toSeq.distinct.size
@@ -237,20 +246,20 @@ case object GridModel {
private def getLinesAdmittance(
nodeUuidToIndexMap: Map[UUID, Int],
- line: LineModel
+ line: LineModel,
): (Int, Int, Complex, Complex, Complex) = {
val (i: Int, j: Int) =
(
nodeUuidToIndexMap.getOrElse(
line.nodeAUuid,
- throwNodeNotFoundException(line.nodeAUuid)
+ throwNodeNotFoundException(line.nodeAUuid),
),
nodeUuidToIndexMap
.getOrElse(
line.nodeBUuid,
- throwNodeNotFoundException(line.nodeBUuid)
- )
+ throwNodeNotFoundException(line.nodeBUuid),
+ ),
)
// yaa == ybb => we use yaa only
@@ -261,25 +270,25 @@ case object GridModel {
private def getTransformerAdmittance(
nodeUuidToIndexMap: Map[UUID, Int],
- trafo: TransformerModel
+ trafo: TransformerModel,
): (Int, Int, Complex, Complex, Complex) = {
val (i: Int, j: Int) =
(
nodeUuidToIndexMap.getOrElse(
trafo.hvNodeUuid,
- throwNodeNotFoundException(trafo.hvNodeUuid)
+ throwNodeNotFoundException(trafo.hvNodeUuid),
),
nodeUuidToIndexMap.getOrElse(
trafo.lvNodeUuid,
- throwNodeNotFoundException(trafo.lvNodeUuid)
- )
+ throwNodeNotFoundException(trafo.lvNodeUuid),
+ ),
)
val (yab, yaa, ybb) = (
TransformerModel.yij(trafo),
TransformerModel.y0(trafo, ConnectorPort.A),
- TransformerModel.y0(trafo, ConnectorPort.B)
+ TransformerModel.y0(trafo, ConnectorPort.B),
)
(i, j, yab, yaa, ybb)
@@ -287,7 +296,7 @@ case object GridModel {
private def getTransformer3wAdmittance(
nodeUuidToIndexMap: Map[UUID, Int],
- trafo3w: Transformer3wModel
+ trafo3w: Transformer3wModel,
): (Int, Int, Complex, Complex, Complex) = {
// start with power flow case specific parameters
@@ -298,7 +307,7 @@ case object GridModel {
trafo3w.hvNodeUuid,
trafo3w.nodeInternalUuid,
Transformer3wModel
- .y0(trafo3w, Transformer3wModel.Transformer3wPort.INTERNAL)
+ .y0(trafo3w, Transformer3wModel.Transformer3wPort.INTERNAL),
)
case PowerFlowCaseB =>
@@ -313,7 +322,7 @@ case object GridModel {
nodeUuidToIndexMap
.getOrElse(nodeAUuid, throwNodeNotFoundException(nodeAUuid)),
nodeUuidToIndexMap
- .getOrElse(nodeBUuid, throwNodeNotFoundException(nodeBUuid))
+ .getOrElse(nodeBUuid, throwNodeNotFoundException(nodeBUuid)),
)
// these parameters are the same for all cases
@@ -446,11 +455,68 @@ case object GridModel {
}
+ /** Checks all ControlGroups if a) Transformer of ControlGroup and Measurement
+ * belongs to the same sub grid. b) Measurements are measure voltage
+ * magnitude.
+ *
+ * @param subGridContainer
+ * Container of all models for this sub grid
+ * @param maybeControlConfig
+ * Config of ControlGroup
+ */
+ private def validateControlGroups(
+ subGridContainer: SubGridContainer,
+ maybeControlConfig: Option[SimonaConfig.Simona.Control],
+ ): Unit = {
+ maybeControlConfig.foreach { control =>
+ val measurementUnits =
+ subGridContainer.getRawGrid.getMeasurementUnits.asScala
+ .map(measurement => measurement.getUuid -> measurement)
+ .toMap
+
+ val transformerUnits2W =
+ subGridContainer.getRawGrid.getTransformer2Ws.asScala
+ .map(transformer2w => transformer2w.getUuid -> transformer2w)
+ .toMap
+
+ val transformerUnits3W =
+ subGridContainer.getRawGrid.getTransformer3Ws.asScala
+ .map(transformer3w => transformer3w.getUuid -> transformer3w)
+ .toMap
+
+ control.transformer.foreach { controlGroup =>
+ controlGroup.transformers.map(UUID.fromString).foreach { transformer =>
+ val transformerUnit2W = transformerUnits2W.get(transformer)
+ val transformerUnit3W = transformerUnits3W.get(transformer)
+
+ if (transformerUnit2W.isDefined || transformerUnit3W.isDefined) {
+ controlGroup.measurements
+ .map(UUID.fromString)
+ .foreach { measurement =>
+ val measurementUnit = measurementUnits.getOrElse(
+ measurement,
+ throw new GridAgentInitializationException(
+ s"${subGridContainer.getGridName} has a transformer control group (${control.transformer.toString}) with a measurement unit whose UUID does not exist in this subnet."
+ ),
+ )
+ if (!measurementUnit.getVMag)
+ throw new GridAgentInitializationException(
+ s"${subGridContainer.getGridName} has a transformer control group (${control.transformer.toString}) with a measurement unit which does not measure voltage magnitude."
+ )
+ }
+ }
+
+ }
+ }
+ }
+ }
+
private def buildAndValidate(
subGridContainer: SubGridContainer,
refSystem: RefSystem,
startDate: ZonedDateTime,
- endDate: ZonedDateTime
+ endDate: ZonedDateTime,
+ simonaConfig: SimonaConfig,
): GridModel = {
// build
@@ -479,7 +545,7 @@ case object GridModel {
transformer2wInput,
refSystem,
startDate,
- endDate
+ endDate,
)
} else {
throw new InvalidGridException(
@@ -498,7 +564,7 @@ case object GridModel {
refSystem,
subGridContainer.getSubnet,
startDate,
- endDate
+ endDate,
)
}.toSet
@@ -531,15 +597,39 @@ case object GridModel {
lines,
transformers,
transformer3ws,
- switches
+ switches,
)
- val gridModel =
- GridModel(subGridContainer.getSubnet, refSystem, gridComponents)
+ /* Build transformer control groups */
+ val transformerControlGroups = simonaConfig.simona.control
+ .map { controlConfig =>
+ TransformerControlGroupModel.buildControlGroups(
+ subGridContainer.getRawGrid.getMeasurementUnits.asScala.toSet,
+ controlConfig.transformer,
+ )
+ }
+ .getOrElse(Set.empty)
+
+ val gridModel = GridModel(
+ subGridContainer.getSubnet,
+ refSystem,
+ gridComponents,
+ GridControls(transformerControlGroups),
+ )
+
+ /** Check and validates the grid. Especially the consistency of the grid
+ * model the connectivity of the grid model if there is InitData for
+ * superior or inferior GridGates if there exits voltage measurements for
+ * transformerControlGroups
+ */
// validate
validateConsistency(gridModel)
validateConnectivity(gridModel)
+ validateControlGroups(
+ subGridContainer,
+ simonaConfig.simona.control,
+ )
// return
gridModel
@@ -574,7 +664,7 @@ case object GridModel {
case switchModel: SwitchModel =>
map ++ Map(
switchModel.nodeAUuid -> componentId,
- switchModel.nodeBUuid -> componentId
+ switchModel.nodeBUuid -> componentId,
)
case nodeModel: NodeModel =>
diff --git a/src/main/scala/edu/ie3/simona/model/grid/LineModel.scala b/src/main/scala/edu/ie3/simona/model/grid/LineModel.scala
index 8ca2b2f0a9..60a2621630 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/LineModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/LineModel.scala
@@ -63,11 +63,11 @@ final case class LineModel(
protected val r: squants.Dimensionless,
protected val x: squants.Dimensionless,
protected val g: squants.Dimensionless,
- protected val b: squants.Dimensionless
+ protected val b: squants.Dimensionless,
) extends SystemComponent(
uuid,
id,
- operationInterval
+ operationInterval,
)
with PiEquivalentCircuit {
@@ -111,7 +111,7 @@ case object LineModel extends LazyLogging {
lineInput: LineInput,
refSystem: RefSystem,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): LineModel = {
// validate the input model first
validateInputModel(lineInput)
@@ -121,7 +121,7 @@ case object LineModel extends LazyLogging {
lineInput,
refSystem,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
}
@@ -143,7 +143,7 @@ case object LineModel extends LazyLogging {
lineInput: LineInput,
refSystem: RefSystem,
startDate: ZonedDateTime,
- endDate: ZonedDateTime
+ endDate: ZonedDateTime,
): LineModel = {
val lineType = lineInput.getType
@@ -183,14 +183,14 @@ case object LineModel extends LazyLogging {
.getValue
.doubleValue()
)
- )
+ ),
)
val operationInterval =
SystemComponent.determineOperationInterval(
startDate,
endDate,
- lineInput.getOperationTime
+ lineInput.getOperationTime,
)
val lineModel = new LineModel(
@@ -206,7 +206,7 @@ case object LineModel extends LazyLogging {
r,
x,
g,
- b
+ b,
)
// if the line input model is in operation, enable the model
@@ -321,7 +321,7 @@ case object LineModel extends LazyLogging {
def y0(lineModel: LineModel): Complex = {
new Complex(
lineModel.g0().value.doubleValue(),
- lineModel.b0().value.doubleValue()
+ lineModel.b0().value.doubleValue(),
)
}
@@ -335,7 +335,7 @@ case object LineModel extends LazyLogging {
*/
def yij(lineModel: LineModel): Complex = new Complex(
lineModel.gij().value.doubleValue(),
- lineModel.bij().value.doubleValue()
+ lineModel.bij().value.doubleValue(),
)
/** Calculates the utilisation of a given line model
@@ -352,12 +352,12 @@ case object LineModel extends LazyLogging {
def utilisation(
lineModel: LineModel,
iNodeA: squants.electro.ElectricCurrent,
- iNodeB: squants.electro.ElectricCurrent
+ iNodeB: squants.electro.ElectricCurrent,
): squants.Dimensionless = {
Each(
Math.max(
iNodeA.toAmperes,
- iNodeB.toAmperes
+ iNodeB.toAmperes,
) / lineModel.iNom.toAmperes * 100 / lineModel.amount
)
}
diff --git a/src/main/scala/edu/ie3/simona/model/grid/NodeModel.scala b/src/main/scala/edu/ie3/simona/model/grid/NodeModel.scala
index 67acac7d8b..506ea804a1 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/NodeModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/NodeModel.scala
@@ -39,18 +39,18 @@ final case class NodeModel(
operationInterval: OperationInterval,
isSlack: Boolean,
vTarget: squants.Dimensionless,
- voltLvl: VoltageLevel
+ voltLvl: VoltageLevel,
) extends SystemComponent(
uuid,
id,
- operationInterval
+ operationInterval,
)
case object NodeModel {
def apply(
nodeInput: NodeInput,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): NodeModel = {
// validate the input model
@@ -60,7 +60,7 @@ case object NodeModel {
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- nodeInput.getOperationTime
+ nodeInput.getOperationTime,
)
val nodeModel = new NodeModel(
@@ -69,7 +69,7 @@ case object NodeModel {
operationInterval,
nodeInput.isSlack,
Each(nodeInput.getvTarget.to(PowerSystemUnits.PU).getValue.doubleValue()),
- nodeInput.getVoltLvl
+ nodeInput.getVoltLvl,
)
/* Checks, if the participant is in operation right from the start */
diff --git a/src/main/scala/edu/ie3/simona/model/grid/PiEquivalentCircuit.scala b/src/main/scala/edu/ie3/simona/model/grid/PiEquivalentCircuit.scala
index d1d85d7177..0583d37d74 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/PiEquivalentCircuit.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/PiEquivalentCircuit.scala
@@ -125,7 +125,7 @@ trait PiEquivalentCircuit extends LazyLogging {
r,
x,
g,
- b
+ b,
)
}
diff --git a/src/main/scala/edu/ie3/simona/model/grid/RefSystem.scala b/src/main/scala/edu/ie3/simona/model/grid/RefSystem.scala
index 53ee866930..40319e699a 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/RefSystem.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/RefSystem.scala
@@ -9,22 +9,19 @@ package edu.ie3.simona.model.grid
import breeze.math.Complex
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.quantities.{ReactivePower, Vars}
-import squants.Each
-import squants.electro.Kilovolts
+import squants.electro._
import squants.energy.{Megawatts, Watts}
+import squants.{Dimensionless, Each, Power}
import tech.units.indriya.quantity.Quantities
-import javax.measure.quantity.{ElectricPotential, Power}
-
/** Provides the values a [[GridModel]] is referenced to as well as functions to
* reference some standard parameters to the nominal impedance.
*/
-
final case class RefSystem private (
- nominalVoltage: squants.electro.ElectricPotential,
- nominalCurrent: squants.electro.ElectricCurrent,
- nominalPower: squants.Power,
- nominalImpedance: squants.electro.ElectricalResistance
+ nominalVoltage: ElectricPotential,
+ nominalCurrent: ElectricCurrent,
+ nominalPower: Power,
+ nominalImpedance: ElectricalResistance,
) {
/** Calculates the referenced resistance r (real part of impedance z) of a
@@ -36,8 +33,8 @@ final case class RefSystem private (
* referenced resistance r in p.u.
*/
def rInPu(
- r: squants.electro.ElectricalResistance
- ): squants.Dimensionless = {
+ r: ElectricalResistance
+ ): Dimensionless = {
Each(r.toOhms / nominalImpedance.toOhms)
}
@@ -50,8 +47,8 @@ final case class RefSystem private (
* referenced reactance x in p.u.
*/
def xInPu(
- x: squants.electro.ElectricalResistance
- ): squants.Dimensionless =
+ x: ElectricalResistance
+ ): Dimensionless =
rInPu(x)
/** Calculates the referenced susceptance b (imaginary part of admittance y)
@@ -63,8 +60,8 @@ final case class RefSystem private (
* referenced susceptance b in p.u.
*/
def bInPu(
- b: squants.electro.ElectricalConductance
- ): squants.Dimensionless = {
+ b: ElectricalConductance
+ ): Dimensionless = {
Each(b.toSiemens * nominalImpedance.toOhms)
}
@@ -77,8 +74,8 @@ final case class RefSystem private (
* referenced conductance g in p.u.
*/
def gInPu(
- g: squants.electro.ElectricalConductance
- ): squants.Dimensionless =
+ g: ElectricalConductance
+ ): Dimensionless =
bInPu(g)
/** Converts a provided referenced active power value from p.u. into physical
@@ -89,10 +86,10 @@ final case class RefSystem private (
* @return
* unreferenced active power value in Watt
*/
- def pInSi(pInPu: squants.Dimensionless): squants.Power =
+ def pInSi(pInPu: Dimensionless): Power =
Watts(nominalPower.toWatts * pInPu.toEach)
- def pInSi(pInPu: Double): squants.Power =
+ def pInSi(pInPu: Double): Power =
pInSi(Each(pInPu))
/** Converts a provided active power value from physical SI to referenced p.u.
@@ -102,7 +99,7 @@ final case class RefSystem private (
* @return
* referenced active power value in p.u.
*/
- def pInPu(pInSi: squants.Power): squants.Dimensionless =
+ def pInPu(pInSi: Power): Dimensionless =
Each(pInSi.toWatts / nominalPower.toWatts)
/** Converts a provided reactive power value from p.u. into physical SI value
@@ -112,7 +109,7 @@ final case class RefSystem private (
* @return
* unreferenced active power value in Var
*/
- def qInSi(qInPu: squants.Dimensionless): ReactivePower =
+ def qInSi(qInPu: Dimensionless): ReactivePower =
Vars(nominalPower.toWatts * qInPu.toEach)
def qInSi(qInPu: Double): ReactivePower =
@@ -126,7 +123,7 @@ final case class RefSystem private (
* @return
* referenced active power value in p.u.
*/
- def qInPu(qInSi: ReactivePower): squants.Dimensionless =
+ def qInPu(qInSi: ReactivePower): Dimensionless =
Each(qInSi.toVars / nominalPower.toWatts)
/** Converts a provided voltage value from p.u. into physical SI value
@@ -137,20 +134,20 @@ final case class RefSystem private (
* unreferenced voltage value in Volt
*/
def vInSi(
- vInPu: squants.Dimensionless
- ): squants.electro.ElectricPotential =
+ vInPu: Dimensionless
+ ): ElectricPotential =
Kilovolts(nominalVoltage.toKilovolts * vInPu.toEach)
- def vInSi(vInPu: Double): squants.electro.ElectricPotential =
+ def vInSi(vInPu: Double): ElectricPotential =
vInSi(Each(vInPu))
def vInSi(vInPu: Complex): (
- squants.electro.ElectricPotential,
- squants.electro.ElectricPotential
+ ElectricPotential,
+ ElectricPotential,
) =
(
vInSi(Each(vInPu.real)),
- vInSi(Each(vInPu.imag))
+ vInSi(Each(vInPu.imag)),
)
/** Converts a provided voltage value from physical SI value into p.u. value
@@ -161,29 +158,29 @@ final case class RefSystem private (
* referenced voltage value in p.u.
*/
def vInPu(
- vInSi: squants.electro.ElectricPotential
- ): squants.Dimensionless =
+ vInSi: ElectricPotential
+ ): Dimensionless =
Each(vInSi.toVolts / nominalVoltage.toVolts)
}
case object RefSystem {
def apply(
- nominalPower: squants.Power,
- nominalVoltage: squants.electro.ElectricPotential
+ nominalPower: Power,
+ nominalVoltage: ElectricPotential,
): RefSystem = {
- val nominalCurrent: squants.electro.ElectricCurrent =
+ val nominalCurrent: ElectricCurrent =
nominalPower / (nominalVoltage * Math.sqrt(3))
- val nominalImpedance: squants.electro.ElectricalResistance =
+ val nominalImpedance: ElectricalResistance =
nominalVoltage / (nominalCurrent * Math.sqrt(3))
new RefSystem(
nominalVoltage,
nominalCurrent,
nominalPower,
- nominalImpedance
+ nominalImpedance,
)
}
@@ -197,7 +194,7 @@ case object RefSystem {
val sNom = Megawatts(
Quantities
.getQuantity(nominalPower)
- .asType(classOf[Power])
+ .asType(classOf[javax.measure.quantity.Power])
.to(PowerSystemUnits.MEGAVOLTAMPERE)
.getValue
.doubleValue()
@@ -205,7 +202,7 @@ case object RefSystem {
val vNom = Kilovolts(
Quantities
.getQuantity(nominalVoltage)
- .asType(classOf[ElectricPotential])
+ .asType(classOf[javax.measure.quantity.ElectricPotential])
.to(PowerSystemUnits.KILOVOLT)
.getValue
.doubleValue()
@@ -225,10 +222,10 @@ case object RefSystem {
* Dimensionless impedance with regard the to target reference system
*/
def transferImpedance(
- impedance: squants.Dimensionless,
+ impedance: Dimensionless,
from: RefSystem,
- to: RefSystem
- ): squants.Dimensionless = {
+ to: RefSystem,
+ ): Dimensionless = {
val ratio = from.nominalImpedance.toOhms / to.nominalImpedance.toOhms
Each(impedance.toEach * ratio)
}
@@ -246,10 +243,10 @@ case object RefSystem {
* Dimensionless admittance with regard the to target reference system
*/
def transferAdmittance(
- admittance: squants.Dimensionless,
+ admittance: Dimensionless,
from: RefSystem,
- to: RefSystem
- ): squants.Dimensionless = {
+ to: RefSystem,
+ ): Dimensionless = {
val ratio = to.nominalImpedance.toOhms / from.nominalImpedance.toOhms
Each(admittance.toEach * ratio)
diff --git a/src/main/scala/edu/ie3/simona/model/grid/SwitchModel.scala b/src/main/scala/edu/ie3/simona/model/grid/SwitchModel.scala
index 49d1d73402..5a5a457a5f 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/SwitchModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/SwitchModel.scala
@@ -35,11 +35,11 @@ final case class SwitchModel(
id: String,
operationInterval: OperationInterval,
nodeAUuid: UUID,
- nodeBUuid: UUID
+ nodeBUuid: UUID,
) extends SystemComponent(
uuid,
id,
- operationInterval
+ operationInterval,
) {
private var _isClosed = true
@@ -86,7 +86,7 @@ case object SwitchModel {
def apply(
switchInput: SwitchInput,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): SwitchModel = {
// validate the input model
@@ -96,7 +96,7 @@ case object SwitchModel {
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- switchInput.getOperationTime
+ switchInput.getOperationTime,
)
val switchModel = new SwitchModel(
@@ -104,7 +104,7 @@ case object SwitchModel {
switchInput.getId,
operationInterval,
switchInput.getNodeA.getUuid,
- switchInput.getNodeB.getUuid
+ switchInput.getNodeB.getUuid,
)
if (!switchInput.isClosed)
switchModel.open()
diff --git a/src/main/scala/edu/ie3/simona/model/grid/Transformer3wModel.scala b/src/main/scala/edu/ie3/simona/model/grid/Transformer3wModel.scala
index 42f365a630..8f61c2a897 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/Transformer3wModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/Transformer3wModel.scala
@@ -16,13 +16,13 @@ import edu.ie3.datamodel.models.input.connector.Transformer3WInput
import edu.ie3.datamodel.models.input.connector.`type`.Transformer3WTypeInput
import edu.ie3.simona.exceptions.{
InvalidActionRequestException,
- InvalidParameterException
+ InvalidParameterException,
}
import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.model.grid.Transformer3wPowerFlowCase.{
PowerFlowCaseA,
PowerFlowCaseB,
- PowerFlowCaseC
+ PowerFlowCaseC,
}
import edu.ie3.simona.util.SimonaConstants
import edu.ie3.util.quantities.PowerSystemUnits._
@@ -94,11 +94,11 @@ final case class Transformer3wModel(
protected val r: squants.Dimensionless,
protected val x: squants.Dimensionless,
protected val g: squants.Dimensionless,
- protected val b: squants.Dimensionless
+ protected val b: squants.Dimensionless,
) extends SystemComponent(
uuid,
id,
- operationInterval
+ operationInterval,
)
with PiEquivalentCircuit
with TransformerTapping {
@@ -171,7 +171,7 @@ case object Transformer3wModel extends LazyLogging {
refSystem: RefSystem,
subnetNo: Int,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): Transformer3wModel = {
// validate the input model first
validateInputModel(transformer3wInput)
@@ -182,7 +182,7 @@ case object Transformer3wModel extends LazyLogging {
refSystem,
subnetNo,
simulationStartDate,
- simulationEndDate
+ simulationEndDate,
)
}
@@ -213,7 +213,7 @@ case object Transformer3wModel extends LazyLogging {
gridRefSystem: RefSystem,
subnetNo: Int,
startDate: ZonedDateTime,
- endDate: ZonedDateTime
+ endDate: ZonedDateTime,
): Transformer3wModel = {
// build the model
val trafo3wType = transformer3wInput.getType
@@ -238,7 +238,7 @@ case object Transformer3wModel extends LazyLogging {
trafo3wType.getTapMax,
trafo3wType.getTapMin,
trafo3wType.getTapNeutr,
- transformer3wInput.isAutoTap
+ transformer3wInput.isAutoTap,
)
val voltRatioNominal = powerFlowCase match {
@@ -246,15 +246,15 @@ case object Transformer3wModel extends LazyLogging {
BigDecimal.apply("1").setScale(5, RoundingMode.HALF_UP)
case PowerFlowCaseB =>
BigDecimal
- .apply(trafo3wType.getvRatedA.to(KILOVOLT).getValue.doubleValue)
+ .apply(trafo3wType.getvRatedA.to(KILOVOLT).getValue.toString)
.setScale(5, RoundingMode.HALF_UP) / BigDecimal
- .apply(trafo3wType.getvRatedB.to(KILOVOLT).getValue.doubleValue)
+ .apply(trafo3wType.getvRatedB.to(KILOVOLT).getValue.toString)
.setScale(5, RoundingMode.HALF_UP)
case PowerFlowCaseC =>
BigDecimal
- .apply(trafo3wType.getvRatedA.to(KILOVOLT).getValue.doubleValue)
+ .apply(trafo3wType.getvRatedA.to(KILOVOLT).getValue.toString)
.setScale(5, RoundingMode.HALF_UP) / BigDecimal
- .apply(trafo3wType.getvRatedC.to(KILOVOLT).getValue.doubleValue)
+ .apply(trafo3wType.getvRatedC.to(KILOVOLT).getValue.toString)
.setScale(5, RoundingMode.HALF_UP)
}
@@ -262,7 +262,7 @@ case object Transformer3wModel extends LazyLogging {
SystemComponent.determineOperationInterval(
startDate,
endDate,
- transformer3wInput.getOperationTime
+ transformer3wInput.getOperationTime,
)
val transformer3wModel = new Transformer3wModel(
@@ -280,7 +280,7 @@ case object Transformer3wModel extends LazyLogging {
r,
x,
g,
- b
+ b,
)
// if the transformer3w input model is in operation, enable the model
@@ -314,12 +314,12 @@ case object Transformer3wModel extends LazyLogging {
private def rxgbInPu(
transformerType: Transformer3WTypeInput,
refSystem: RefSystem,
- powerFlowCase: Transformer3wPowerFlowCase
+ powerFlowCase: Transformer3wPowerFlowCase,
): (
squants.Dimensionless,
squants.Dimensionless,
squants.Dimensionless,
- squants.Dimensionless
+ squants.Dimensionless,
) = {
val transformerRefSystem =
RefSystem(
@@ -328,7 +328,7 @@ case object Transformer3wModel extends LazyLogging {
),
Kilovolts(
transformerType.getvRatedA.to(KILOVOLT).getValue.doubleValue()
- )
+ ),
)
/* Get the physical equivalent circuit diagram parameters from type. They come with reference to the highest
@@ -339,7 +339,7 @@ case object Transformer3wModel extends LazyLogging {
transformerType.getrScA,
transformerType.getxScA,
transformerType.getgM,
- transformerType.getbM
+ transformerType.getbM,
)
case PowerFlowCaseB =>
val nominalRatio = transformerType
@@ -353,7 +353,7 @@ case object Transformer3wModel extends LazyLogging {
transformerType.getrScB.divide(pow(nominalRatio, 2)),
transformerType.getxScB.divide(pow(nominalRatio, 2)),
Quantities.getQuantity(0d, StandardUnits.CONDUCTANCE),
- Quantities.getQuantity(0d, StandardUnits.SUSCEPTANCE)
+ Quantities.getQuantity(0d, StandardUnits.SUSCEPTANCE),
)
case PowerFlowCaseC =>
val nominalRatio = transformerType
@@ -367,7 +367,7 @@ case object Transformer3wModel extends LazyLogging {
transformerType.getrScC.divide(pow(nominalRatio, 2)),
transformerType.getxScC.divide(pow(nominalRatio, 2)),
Quantities.getQuantity(0d, StandardUnits.CONDUCTANCE),
- Quantities.getQuantity(0d, StandardUnits.SUSCEPTANCE)
+ Quantities.getQuantity(0d, StandardUnits.SUSCEPTANCE),
)
}
@@ -380,7 +380,7 @@ case object Transformer3wModel extends LazyLogging {
/* g */
refSystem.gInPu(Siemens(gTrafo.to(SIEMENS).getValue.doubleValue())),
/* b */
- refSystem.bInPu(Siemens(bTrafo.to(SIEMENS).getValue.doubleValue()))
+ refSystem.bInPu(Siemens(bTrafo.to(SIEMENS).getValue.doubleValue())),
)
}
@@ -400,17 +400,17 @@ case object Transformer3wModel extends LazyLogging {
val (vNodeAVal, vTypeAVal) =
(
transformer3wInput.getNodeA.getVoltLvl.getNominalVoltage.getValue.doubleValue,
- trafo3wType.getvRatedA.getValue.doubleValue
+ trafo3wType.getvRatedA.getValue.doubleValue,
)
val (vNodeBVal, vTypeBVal) =
(
transformer3wInput.getNodeB.getVoltLvl.getNominalVoltage.getValue.doubleValue,
- trafo3wType.getvRatedB.getValue.doubleValue
+ trafo3wType.getvRatedB.getValue.doubleValue,
)
val (vNodeCVal, vTypeCVal) =
(
transformer3wInput.getNodeC.getVoltLvl.getNominalVoltage.getValue.doubleValue,
- trafo3wType.getvRatedC.getValue.doubleValue
+ trafo3wType.getvRatedC.getValue.doubleValue,
)
val nomVoltDevA = vNodeAVal - vTypeAVal
@@ -435,7 +435,7 @@ case object Transformer3wModel extends LazyLogging {
// check for wrong positioning by comparing node{A,B,C} voltage and transformer type v{A,B,C}
val transformerWrongPositionExceptionString: (
Quantity[ElectricPotential],
- Quantity[ElectricPotential]
+ Quantity[ElectricPotential],
) => String = { (vRatedNodeX, typeVNodeX) =>
s"The rated voltage of node A is $vRatedNodeX, " +
s"but the winding A is only rated for $typeVNodeX. Is the transformer connected correctly?"
@@ -444,21 +444,21 @@ case object Transformer3wModel extends LazyLogging {
throw new InvalidGridException(
transformerWrongPositionExceptionString(
transformer3wInput.getNodeA.getVoltLvl.getNominalVoltage,
- trafo3wType.getvRatedA
+ trafo3wType.getvRatedA,
)
)
if (vNodeBVal < vTypeBVal)
throw new InvalidGridException(
transformerWrongPositionExceptionString(
transformer3wInput.getNodeB.getVoltLvl.getNominalVoltage,
- trafo3wType.getvRatedB
+ trafo3wType.getvRatedB,
)
)
if (vNodeCVal < vTypeCVal)
throw new InvalidGridException(
transformerWrongPositionExceptionString(
transformer3wInput.getNodeC.getVoltLvl.getNominalVoltage,
- trafo3wType.getvRatedC
+ trafo3wType.getvRatedC,
)
)
@@ -481,7 +481,7 @@ case object Transformer3wModel extends LazyLogging {
trafo3wType.getsRatedA(),
trafo3wType.getsRatedB(),
trafo3wType.getsRatedC(),
- trafo3wType.getsRatedB().add(trafo3wType.getsRatedC())
+ trafo3wType.getsRatedB().add(trafo3wType.getsRatedC()),
)
}
}
@@ -503,7 +503,7 @@ case object Transformer3wModel extends LazyLogging {
transformerModel.powerFlowCase match {
case Transformer3wPowerFlowCase.PowerFlowCaseA =>
BigDecimal
- .apply(transformerModel.tapRatio)
+ .apply(transformerModel.tapRatio.toString)
.setScale(5, RoundingMode.HALF_UP)
case Transformer3wPowerFlowCase.PowerFlowCaseB |
Transformer3wPowerFlowCase.PowerFlowCaseC =>
@@ -526,7 +526,7 @@ case object Transformer3wModel extends LazyLogging {
*/
def y0(
transformer3wModel: Transformer3wModel,
- port: Transformer3wPort.Value
+ port: Transformer3wPort.Value,
): Complex = {
val amount = transformer3wModel.amount
transformer3wModel.powerFlowCase match {
@@ -537,10 +537,10 @@ case object Transformer3wModel extends LazyLogging {
val bii = transformer3wModel.b0().value.doubleValue()
amount * ((1 - 1 / transformer3wModel.tapRatio) * Complex(
gij,
- bij
+ bij,
) + Complex(
gii,
- bii
+ bii,
))
case _ => Complex.zero
}
diff --git a/src/main/scala/edu/ie3/simona/model/grid/Transformer3wPowerFlowCase.scala b/src/main/scala/edu/ie3/simona/model/grid/Transformer3wPowerFlowCase.scala
index 0b3e88252f..cad400df8f 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/Transformer3wPowerFlowCase.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/Transformer3wPowerFlowCase.scala
@@ -34,7 +34,7 @@ object Transformer3wPowerFlowCase {
def apply(
trafo3wInput: Transformer3WInput,
- subnetNo: Int
+ subnetNo: Int,
): Transformer3wPowerFlowCase = {
if (trafo3wInput.getNodeA.getSubnet == subnetNo)
PowerFlowCaseA
diff --git a/src/main/scala/edu/ie3/simona/model/grid/TransformerModel.scala b/src/main/scala/edu/ie3/simona/model/grid/TransformerModel.scala
index e7f7abf08f..7a63cbf489 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/TransformerModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/TransformerModel.scala
@@ -11,7 +11,7 @@ import breeze.numerics.pow
import edu.ie3.datamodel.exceptions.InvalidGridException
import edu.ie3.datamodel.models.input.connector.{
ConnectorPort,
- Transformer2WInput
+ Transformer2WInput,
}
import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.util.SimonaConstants
@@ -77,11 +77,11 @@ final case class TransformerModel(
protected val r: squants.Dimensionless,
protected val x: squants.Dimensionless,
protected val g: squants.Dimensionless,
- protected val b: squants.Dimensionless
+ protected val b: squants.Dimensionless,
) extends SystemComponent(
uuid,
id,
- operationInterval
+ operationInterval,
)
with PiEquivalentCircuit
with TransformerTapping {
@@ -95,7 +95,7 @@ case object TransformerModel {
transformerInput: Transformer2WInput,
refSystem: RefSystem,
startDate: ZonedDateTime,
- endDate: ZonedDateTime
+ endDate: ZonedDateTime,
): TransformerModel = {
// validate the input model first
@@ -125,15 +125,15 @@ case object TransformerModel {
transformerInput: Transformer2WInput,
gridRefSystem: RefSystem,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): TransformerModel = {
// get referenced electric values
val trafoType = transformerInput.getType
val voltRatioNominal = BigDecimal
- .apply(trafoType.getvRatedA().to(KILOVOLT).getValue.doubleValue)
+ .apply(trafoType.getvRatedA().to(KILOVOLT).getValue.toString)
.setScale(5, RoundingMode.HALF_UP) / BigDecimal
- .apply(trafoType.getvRatedB().to(KILOVOLT).getValue.doubleValue)
+ .apply(trafoType.getvRatedB().to(KILOVOLT).getValue.toString)
.setScale(5, RoundingMode.HALF_UP)
val squaredNominalVoltRatio = voltRatioNominal * voltRatioNominal
@@ -167,7 +167,7 @@ case object TransformerModel {
.multiply(squaredNominalVoltRatio)
.getValue
.doubleValue()
- )
+ ),
)
/* Transfer the dimensionless parameters into the grid reference system */
@@ -175,7 +175,7 @@ case object TransformerModel {
gridRefSystem.rInPu(rTrafo),
gridRefSystem.xInPu(xTrafo),
gridRefSystem.gInPu(gTrafo),
- gridRefSystem.bInPu(bTrafo)
+ gridRefSystem.bInPu(bTrafo),
)
// iNomHv, iNomLv
@@ -201,7 +201,7 @@ case object TransformerModel {
Kilovolts(
trafoType.getvRatedB.to(KILOVOLT).getValue.doubleValue()
)
- )
+ ),
)
// get the element port, where the transformer tap is located
@@ -216,14 +216,14 @@ case object TransformerModel {
trafoType.getTapMin,
trafoType.getTapNeutr,
transformerInput.isAutoTap,
- tapSide
+ tapSide,
)
val operationInterval =
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- transformerInput.getOperationTime
+ transformerInput.getOperationTime,
)
val transformerModel = new TransformerModel(
@@ -240,7 +240,7 @@ case object TransformerModel {
r,
x,
g,
- b
+ b,
)
// if the transformer input model is in operation, enable the model
@@ -268,7 +268,7 @@ case object TransformerModel {
*/
def validateInputModel(
transformerInput: Transformer2WInput,
- refSystem: RefSystem
+ refSystem: RefSystem,
): Unit = {
val trafoType = transformerInput.getType
@@ -371,7 +371,7 @@ case object TransformerModel {
new Complex(
transformerModel.gij().value.doubleValue(),
- transformerModel.bij().value.doubleValue()
+ transformerModel.bij().value.doubleValue(),
) * amount / tapRatio
}
@@ -390,14 +390,14 @@ case object TransformerModel {
def utilisation(
transformerModel: TransformerModel,
iNodeHv: Quantity[ElectricCurrent],
- iNodeLv: Quantity[ElectricCurrent]
+ iNodeLv: Quantity[ElectricCurrent],
): squants.Dimensionless = {
Each(
Math.max(
iNodeHv.getValue.doubleValue() / transformerModel.iNomHv.value
.doubleValue(),
iNodeLv.getValue.doubleValue() / transformerModel.iNomLv.value
- .doubleValue()
+ .doubleValue(),
) * 100
)
}
diff --git a/src/main/scala/edu/ie3/simona/model/grid/TransformerTapping.scala b/src/main/scala/edu/ie3/simona/model/grid/TransformerTapping.scala
index 5337e0c829..809e7c5f8f 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/TransformerTapping.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/TransformerTapping.scala
@@ -72,7 +72,7 @@ trait TransformerTapping {
*/
def computeDeltaTap(
vChangeRequest: Quantity[Dimensionless],
- deadBand: Quantity[Dimensionless] = Quantities.getQuantity(0.75, PU)
+ deadBand: Quantity[Dimensionless] = Quantities.getQuantity(0.75, PU),
): Int =
transformerTappingModel.computeDeltaTap(vChangeRequest, deadBand)
diff --git a/src/main/scala/edu/ie3/simona/model/grid/TransformerTappingModel.scala b/src/main/scala/edu/ie3/simona/model/grid/TransformerTappingModel.scala
index a40e4a5568..602259cec2 100644
--- a/src/main/scala/edu/ie3/simona/model/grid/TransformerTappingModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/grid/TransformerTappingModel.scala
@@ -43,7 +43,7 @@ final case class TransformerTappingModel(
tapMin: Int,
tapNeutr: Int,
autoTap: Boolean,
- tapSide: ConnectorPort = ConnectorPort.A
+ tapSide: ConnectorPort = ConnectorPort.A,
) extends LazyLogging {
private val deltaVval = deltaV.to(PU).getValue.doubleValue()
@@ -96,8 +96,7 @@ final case class TransformerTappingModel(
s"Provided tap pos $newTapPos is not between allowed tapping range of tapMin: $tapMin and tapMax: $tapMax!"
)
_currentTapPos = newTapPos
- val tapRatio: Double = 1 + (_currentTapPos - tapNeutr) * deltaVval
- tapRatio
+ 1 + (_currentTapPos - tapNeutr) * deltaVval
}
/** Determine the amount of tap positions to increase oder decrease in order
@@ -131,7 +130,7 @@ final case class TransformerTappingModel(
*/
def computeDeltaTap(
vChangeRequest: Quantity[Dimensionless],
- deadBandPerTap: Quantity[Dimensionless] = Quantities.getQuantity(0.75, PU)
+ deadBandPerTap: Quantity[Dimensionless] = Quantities.getQuantity(0.75, PU),
): Int = {
/* Determine the tap change, that has to be done in any case, as well as the remainder to fully
* fulfill the voltage change request */
@@ -170,7 +169,7 @@ case object TransformerTappingModel {
tapMin: Int,
tapNeutr: Int,
autoTap: Boolean,
- elementPort: ConnectorPort = ConnectorPort.A
+ elementPort: ConnectorPort = ConnectorPort.A,
): TransformerTappingModel = {
val tapModel =
new TransformerTappingModel(
@@ -180,7 +179,7 @@ case object TransformerTappingModel {
tapMin,
tapNeutr,
autoTap,
- elementPort
+ elementPort,
)
// update internal state variables
diff --git a/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatParticipant.scala b/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatParticipant.scala
index 9fe21e87ac..205704bf41 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatParticipant.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatParticipant.scala
@@ -10,17 +10,22 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPowerAndHe
import squants.energy.Megawatts
import squants.{Dimensionless, Power}
-trait ApparentPowerAndHeatParticipant[CD <: CalcRelevantData] {
- this: SystemParticipant[CD, ApparentPowerAndHeat] =>
+trait ApparentPowerAndHeatParticipant[
+ CD <: CalcRelevantData,
+ MS <: ModelState,
+] {
+ this: SystemParticipant[CD, ApparentPowerAndHeat, MS] =>
override def calculatePower(
tick: Long,
voltage: Dimensionless,
- data: CD
+ modelState: MS,
+ data: CD,
): ApparentPowerAndHeat = {
- val apparentPower = calculateApparentPower(tick, voltage, data)
+ val apparentPower =
+ calculateApparentPower(tick, voltage, modelState, data)
val heat =
if (isInOperation(tick))
- calculateHeat(tick, data)
+ calculateHeat(tick, modelState, data) * scalingFactor
else
Megawatts(0d)
@@ -31,10 +36,16 @@ trait ApparentPowerAndHeatParticipant[CD <: CalcRelevantData] {
* are understood as consumption and negative as production
* @param tick
* Current instant in simulation time
+ * @param modelState
+ * Current state of the model
* @param data
* Needed calculation relevant data
* @return
* Heat production or consumption of the asset
*/
- def calculateHeat(tick: Long, data: CD): Power
+ def calculateHeat(
+ tick: Long,
+ modelState: MS,
+ data: CD,
+ ): Power
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerParticipant.scala b/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerParticipant.scala
index 51353fe437..d1d71a58ac 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerParticipant.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/ApparentPowerParticipant.scala
@@ -9,11 +9,13 @@ package edu.ie3.simona.model.participant
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import squants.Dimensionless
-trait ApparentPowerParticipant[CD <: CalcRelevantData] {
- this: SystemParticipant[CD, ApparentPower] =>
+trait ApparentPowerParticipant[CD <: CalcRelevantData, MS <: ModelState] {
+ this: SystemParticipant[CD, ApparentPower, MS] =>
override def calculatePower(
tick: Long,
voltage: Dimensionless,
- data: CD
- ): ApparentPower = calculateApparentPower(tick, voltage, data)
+ modelState: MS,
+ data: CD,
+ ): ApparentPower =
+ calculateApparentPower(tick, voltage, modelState, data)
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/BMModel.scala b/src/main/scala/edu/ie3/simona/model/participant/BMModel.scala
index c8594f3e3c..40b149bf95 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/BMModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/BMModel.scala
@@ -8,10 +8,13 @@ package edu.ie3.simona.model.participant
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.model.participant.BMModel.BMCalcRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.EnergyPrice
-import squants.energy.Megawatts
+import squants.energy.{Kilowatts, Megawatts}
import squants.{Dimensionless, Money, Power, Temperature}
import java.time.ZonedDateTime
@@ -24,7 +27,7 @@ final case class BMModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhi: Double,
@@ -32,17 +35,21 @@ final case class BMModel(
private val isCostControlled: Boolean,
private val opex: Money,
private val feedInTariff: EnergyPrice,
- private val loadGradient: Double
-) extends SystemParticipant[BMCalcRelevantData, ApparentPower](
+ private val loadGradient: Double,
+) extends SystemParticipant[
+ BMCalcRelevantData,
+ ApparentPower,
+ ConstantState.type,
+ ](
uuid,
id,
operationInterval,
scalingFactor,
qControl,
sRated,
- cosPhi
+ cosPhi,
)
- with ApparentPowerParticipant[BMCalcRelevantData] {
+ with ApparentPowerParticipant[BMCalcRelevantData, ConstantState.type] {
/** Saves power output of last cycle. Needed for load gradient
*/
@@ -51,9 +58,10 @@ final case class BMModel(
override def calculatePower(
tick: Long,
voltage: Dimensionless,
- data: BMCalcRelevantData
+ modelState: ConstantState.type,
+ data: BMCalcRelevantData,
): ApparentPower = {
- val result = super.calculatePower(tick, voltage, data)
+ val result = super.calculatePower(tick, voltage, modelState, data)
_lastPower = Some(result.p)
result
@@ -67,7 +75,8 @@ final case class BMModel(
* Active power
*/
override protected def calculateActivePower(
- data: BMCalcRelevantData
+ modelState: ConstantState.type,
+ data: BMCalcRelevantData,
): Power = {
// Calculate heat demand //
val (k1, k2) = (calculateK1(data.date), calculateK2(data.date))
@@ -133,7 +142,7 @@ final case class BMModel(
private def calculatePTh(
temp: Temperature,
k1: Double,
- k2: Double
+ k2: Double,
): Power = {
// linear regression: Heat-demand in relation to temperature (above 19.28°C: independent of temperature)
val pTh = temp.toCelsiusScale match {
@@ -178,7 +187,7 @@ final case class BMModel(
*/
private def calculateElOutput(
usage: Double,
- eff: Double
+ eff: Double,
): Power = {
val currOpex = opex.divide(eff)
val avgOpex = (currOpex + opex).divide(2)
@@ -216,9 +225,25 @@ final case class BMModel(
}
}
}
+
+ override def determineFlexOptions(
+ data: BMCalcRelevantData,
+ lastState: ConstantState.type,
+ ): ProvideFlexOptions = {
+ val power = calculateActivePower(lastState, data)
+
+ ProvideMinMaxFlexOptions(uuid, power, power, Kilowatts(0d))
+ }
+
+ override def handleControlledPowerChange(
+ data: BMCalcRelevantData,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
}
-case object BMModel {
+object BMModel {
/** Data, that is needed for model calculations with the biomass model
*
@@ -229,6 +254,6 @@ case object BMModel {
*/
final case class BMCalcRelevantData(
date: ZonedDateTime,
- temperature: Temperature
+ temperature: Temperature,
) extends CalcRelevantData
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/ChpModel.scala b/src/main/scala/edu/ie3/simona/model/participant/ChpModel.scala
index 5b5baa8924..302fa02229 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/ChpModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/ChpModel.scala
@@ -8,17 +8,23 @@ package edu.ie3.simona.model.participant
import edu.ie3.datamodel.models.input.system.ChpInput
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
+import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.model.participant.ChpModel._
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.thermal.{MutableStorage, ThermalStorage}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.DefaultQuantities
-import squants.energy.{KilowattHours, Kilowatts}
import squants.{Energy, Power, Seconds, Time}
+import squants.energy.{KilowattHours, Kilowatts}
import java.util.UUID
+import java.time.ZonedDateTime
+
/** Model of a combined heat and power plant (CHP) with a [[ThermalStorage]]
* medium and its current [[ChpState]].
*
@@ -45,22 +51,22 @@ final case class ChpModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
pThermal: Power,
- storage: ThermalStorage with MutableStorage
-) extends SystemParticipant[ChpRelevantData, ApparentPower](
+ storage: ThermalStorage with MutableStorage,
+) extends SystemParticipant[ChpRelevantData, ApparentPower, ConstantState.type](
uuid,
id,
operationInterval,
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
)
- with ApparentPowerParticipant[ChpRelevantData] {
+ with ApparentPowerParticipant[ChpRelevantData, ConstantState.type] {
val pRated: Power = sRated * cosPhiRated
@@ -76,7 +82,8 @@ final case class ChpModel(
* active power
*/
override protected def calculateActivePower(
- chpData: ChpRelevantData
+ modelState: ConstantState.type,
+ chpData: ChpRelevantData,
): Power =
chpData.chpState.activePower
@@ -142,7 +149,7 @@ final case class ChpModel(
isRunning = false,
chpData.currentTimeTick,
DefaultQuantities.zeroKW,
- DefaultQuantities.zeroKWH
+ DefaultQuantities.zeroKWH,
)
/** The demand cannot be covered, therefore this function sets storage level
@@ -179,7 +186,7 @@ final case class ChpModel(
isRunning = false,
chpData.currentTimeTick,
DefaultQuantities.zeroKW,
- DefaultQuantities.zeroKWH
+ DefaultQuantities.zeroKWH,
)
}
@@ -219,7 +226,7 @@ final case class ChpModel(
*/
private def calculateStateRunningSurplus(
chpData: ChpRelevantData,
- surplus: Option[Energy] = None
+ surplus: Option[Energy] = None,
): ChpState = {
surplus match {
case Some(surplusEnergy) =>
@@ -227,14 +234,14 @@ final case class ChpModel(
isRunning = false,
chpData.currentTimeTick,
pRated,
- chpEnergy(chpData) - surplusEnergy
+ chpEnergy(chpData) - surplusEnergy,
)
case None =>
ChpState(
isRunning = true,
chpData.currentTimeTick,
pRated,
- chpEnergy(chpData)
+ chpEnergy(chpData),
)
}
}
@@ -248,7 +255,7 @@ final case class ChpModel(
*/
private def powerToEnergy(
chpData: ChpRelevantData,
- power: Power
+ power: Power,
): Energy =
power * timeRunning(chpData)
@@ -284,11 +291,28 @@ final case class ChpModel(
private def timeRunning(chpData: ChpRelevantData): Time =
Seconds(chpData.currentTimeTick - chpData.chpState.lastTimeTick)
+
+ override def determineFlexOptions(
+ data: ChpRelevantData,
+ lastState: ConstantState.type,
+ ): ProvideFlexOptions =
+ ProvideMinMaxFlexOptions.noFlexOption(
+ uuid,
+ calculateActivePower(lastState, data),
+ )
+
+ override def handleControlledPowerChange(
+ data: ChpRelevantData,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
+
}
/** Create valid ChpModel by calling the apply function.
*/
-case object ChpModel {
+object ChpModel {
/** As the ChpModel class is a dynamic model, it requires a state for its
* calculations. The state contains all variables needed, except the storage
@@ -307,7 +331,7 @@ case object ChpModel {
isRunning: Boolean,
lastTimeTick: Long,
activePower: Power,
- thermalEnergy: Energy
+ thermalEnergy: Energy,
)
/** Main data required for simulation/calculation, containing a [[ChpState]],
@@ -326,17 +350,21 @@ case object ChpModel {
final case class ChpRelevantData(
chpState: ChpState,
heatDemand: Energy,
- currentTimeTick: Long
+ currentTimeTick: Long,
) extends CalcRelevantData
/** Function to construct a new [[ChpModel]] based on a provided [[ChpInput]]
*
* @param chpInput
* instance of [[ChpInput]] this chp model should be built from
- * @param operationInterval
- * operation interval of the simulation
+ * @param simulationStartDate
+ * Simulation time at which the simulation starts
+ * @param simulationEndDate
+ * Simulation time at which the simulation ends
* @param qControl
- * (no usage)
+ * Strategy to control the reactive power output
+ * @param scalingFactor
+ * Scale the output of this asset by the given factor
* @param thermalStorage
* instance of [[ThermalStorage]] used as thermal storage
* @return
@@ -344,21 +372,29 @@ case object ChpModel {
*/
def apply(
chpInput: ChpInput,
- operationInterval: OperationInterval,
+ simulationStartDate: ZonedDateTime,
+ simulationEndDate: ZonedDateTime,
qControl: QControl,
- thermalStorage: ThermalStorage with MutableStorage
- ): ChpModel =
- new ChpModel(
+ scalingFactor: Double,
+ thermalStorage: ThermalStorage with MutableStorage,
+ ): ChpModel = {
+ val operationInterval = SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ chpInput.getOperationTime,
+ )
+
+ val model = new ChpModel(
chpInput.getUuid,
chpInput.getId,
operationInterval,
- scalingFactor = 1.0,
+ scalingFactor,
qControl,
Kilowatts(
chpInput.getType.getsRated
.to(PowerSystemUnits.KILOWATT)
.getValue
- .doubleValue()
+ .doubleValue
),
chpInput.getType.getCosPhiRated,
Kilowatts(
@@ -367,6 +403,10 @@ case object ChpModel {
.getValue
.doubleValue
),
- thermalStorage
+ thermalStorage,
)
+
+ model.enable()
+ model
+ }
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/EvcsModel.scala b/src/main/scala/edu/ie3/simona/model/participant/EvcsModel.scala
deleted file mode 100644
index 710e62b02a..0000000000
--- a/src/main/scala/edu/ie3/simona/model/participant/EvcsModel.scala
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * © 2021. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.model.participant
-
-import com.typesafe.scalalogging.LazyLogging
-import edu.ie3.datamodel.models.input.system.EvcsInput
-import edu.ie3.datamodel.models.input.system.`type`.evcslocation.EvcsLocationType
-import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
-import edu.ie3.simona.api.data.ev.model.EvModel
-import edu.ie3.simona.model.SystemComponent
-import edu.ie3.simona.model.participant.EvcsModel.EvcsRelevantData
-import edu.ie3.simona.model.participant.control.QControl
-import edu.ie3.util.quantities.PowerSystemUnits
-import edu.ie3.util.scala.OperationInterval
-import edu.ie3.util.scala.quantities.{DefaultQuantities, Megavars}
-import squants.energy
-import squants.energy.{KilowattHours, Kilowatts, Megawatts}
-import squants.time.Seconds
-import tech.units.indriya.quantity.Quantities.getQuantity
-
-import java.time.ZonedDateTime
-import java.util.UUID
-
-/** EV charging station model
- *
- * @param uuid
- * the element's uuid
- * @param id
- * the element's human readable id
- * @param operationInterval
- * Interval, in which the system is in operation
- * @param scalingFactor
- * Scaling the output of the system
- * @param qControl
- * Type of reactive power control
- * @param sRated
- * Rated apparent power
- * @param cosPhiRated
- * Rated power factor
- * @param chargingPoints
- * Number of charging points available at this charging station
- * @param locationType
- * The location type
- */
-final case class EvcsModel(
- uuid: UUID,
- id: String,
- operationInterval: OperationInterval,
- scalingFactor: Double,
- qControl: QControl,
- sRated: squants.Power,
- cosPhiRated: Double,
- chargingPoints: Int,
- locationType: EvcsLocationType
-) extends SystemParticipant[EvcsRelevantData, ApparentPower](
- uuid,
- id,
- operationInterval,
- scalingFactor,
- qControl,
- sRated,
- cosPhiRated
- )
- with ApparentPowerParticipant[EvcsRelevantData]
- with LazyLogging {
-
- /** Calculate the power behaviour based on the given data.
- *
- * @param tick
- * Regarded instant in simulation
- * @param voltage
- * Nodal voltage magnitude
- * @param data
- * Further needed, secondary data
- * @return
- * A tuple of active and reactive power
- */
- def calculatePowerAndEvSoc(
- tick: Long,
- voltage: squants.Dimensionless,
- data: EvcsRelevantData
- ): (ApparentPower, Set[EvModel]) = {
- if (isInOperation(tick) && data.evMovementsDataFrameLength > 0) {
- val (activePower, evModels) = calculateActivePowerAndEvSoc(data)
- val reactivePower =
- calculateReactivePower(activePower, voltage)
- (
- ApparentPower(
- activePower,
- reactivePower
- ),
- evModels
- )
- } else {
- (
- ApparentPower(
- Megawatts(0d),
- Megavars(0d)
- ),
- data.currentEvs
- )
- }
- }
-
- /** Calculates active power based on given data. If sRated of this evcs is
- * exceeded, evs are dropped from charging at this time span.
- * @param data
- * The needed data. evMovementsDataFrameLength > 0 is required.
- * @return
- * Active power and ev models with updated stored energy
- */
- def calculateActivePowerAndEvSoc(
- data: EvcsRelevantData
- ): (squants.Power, Set[EvModel]) = {
- val (powerSum, models) = calculateActivePowerAndEvSoc(
- data.currentEvs,
- data.evMovementsDataFrameLength
- )
- if (powerSum <= sRated) {
- (powerSum, models)
- } else {
- // if we exceed sRated, we scale down charging power of all evs proportionally
- logger.warn(
- s"Set of charging evs is charging with $powerSum and thus exceeding evcs sRated $sRated."
- )
-
- val (calcEvs, noCalcEvs, _) =
- data.currentEvs.foldLeft(
- (
- Set.empty[EvModel],
- Set.empty[EvModel],
- DefaultQuantities.zeroKW
- )
- ) { case ((calcEvs, noCalcEvs, powerSum), ev) =>
- val newPower =
- powerSum + Kilowatts(ev.getSRatedAC.getValue.doubleValue())
- if (newPower <= sRated)
- (calcEvs + ev, noCalcEvs, newPower)
- else
- (calcEvs, noCalcEvs + ev, powerSum)
- }
-
- val (power, newCalcEvs) =
- calculateActivePowerAndEvSoc(
- calcEvs,
- data.evMovementsDataFrameLength
- )
- // include ignored evs
- (power, newCalcEvs ++ noCalcEvs)
- }
- }
-
- /** Calculates active power based on given set of evs
- * @param currentEvs
- * The currently charging evs
- * @param dataFrameLength
- * The duration that all evs are charging
- * @return
- * Active power and ev models with updated stored energy
- */
- private def calculateActivePowerAndEvSoc(
- currentEvs: Set[EvModel],
- dataFrameLength: Long
- ): (squants.Power, Set[EvModel]) = {
- val tickDuration = Seconds(dataFrameLength)
-
- currentEvs.foldLeft(DefaultQuantities.zeroKW, Set.empty[EvModel]) {
- case ((powerSum, models), evModel) =>
- val (chargedEnergy, newEvModel) = charge(
- evModel,
- tickDuration
- )
-
- val chargingPower =
- chargedEnergy / tickDuration
-
- (
- powerSum + chargingPower,
- models + newEvModel
- )
- }
- }
-
- /** Charging given ev model (inside a copy) for given duration.
- * @param evModel
- * The ev model to charge
- * @param duration
- * The duration of charging
- * @return
- * Charged energy and updated ev model as a copy
- */
- def charge(
- evModel: EvModel,
- duration: squants.Time
- ): (squants.Energy, EvModel) = {
- if (evModel.getStoredEnergy.isLessThan(evModel.getEStorage)) {
- val chargingPower =
- sRated.min(
- Kilowatts(
- evModel.getSRatedAC
- .to(PowerSystemUnits.KILOWATT)
- .getValue
- .doubleValue()
- )
- )
-
- val chargeLeftToFull = KilowattHours(
- evModel.getEStorage
- .to(PowerSystemUnits.KILOWATTHOUR)
- .getValue
- .doubleValue()
- ) - KilowattHours(
- evModel.getStoredEnergy
- .to(PowerSystemUnits.KILOWATTHOUR)
- .getValue
- .doubleValue()
- )
-
- val potentialChargeDuringTick = chargingPower * duration
-
- val actualCharge = chargeLeftToFull.min(potentialChargeDuringTick)
- val newStoredEnergy = KilowattHours(
- evModel.getStoredEnergy
- .to(PowerSystemUnits.KILOWATTHOUR)
- .getValue
- .doubleValue()
- ) + actualCharge
-
- (
- actualCharge,
- evModel.copyWith(
- getQuantity(
- newStoredEnergy.value.doubleValue(),
- PowerSystemUnits.KILOWATTHOUR
- )
- )
- )
- } else
- (
- DefaultQuantities.zeroKWH,
- evModel
- )
- }
-
- /** Calculate the active power behaviour of the model
- *
- * @param data
- * Further needed, secondary data
- * @return
- * Active power
- */
- override protected def calculateActivePower(
- data: EvcsRelevantData
- ): energy.Power =
- throw new NotImplementedError("Use calculatePowerAndEvSoc() instead.")
-}
-
-object EvcsModel {
-
- /** Class that holds all relevant data for an Evcs model calculation
- *
- * @param evMovementsDataFrameLength
- * the duration in ticks (= seconds) until next tick
- * @param currentEvs
- * EVs that have been charging up until this tick. Can include EVs that are
- * departing
- */
- final case class EvcsRelevantData(
- evMovementsDataFrameLength: Long,
- currentEvs: Set[EvModel]
- ) extends CalcRelevantData
-
- def apply(
- inputModel: EvcsInput,
- scalingFactor: Double,
- simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
- ): EvcsModel = {
- /* Determine the operation interval */
- val operationInterval: OperationInterval =
- SystemComponent.determineOperationInterval(
- simulationStartDate,
- simulationEndDate,
- inputModel.getOperationTime
- )
-
- apply(
- inputModel.getUuid,
- inputModel.getId,
- operationInterval,
- scalingFactor,
- QControl(inputModel.getqCharacteristics),
- Kilowatts(
- inputModel.getType.getsRated
- .to(PowerSystemUnits.KILOWATT)
- .getValue
- .doubleValue()
- ),
- inputModel.getCosPhiRated,
- inputModel.getChargingPoints,
- inputModel.getLocationType
- )
- }
-
- /** Default factory method to create an EvcsModel instance.
- *
- * @param uuid
- * the unique id of the model
- * @param id
- * the human readable id
- * @param operationInterval
- * the operation interval of the model
- * @param scalingFactor
- * the scaling factor of the power output
- * @param qControl
- * the q control this model is using
- * @param sRated
- * the rated apparent power of the model
- * @param cosPhiRated
- * the rated cosine phi of the model
- * @param chargingPoints
- * Number of charging points available at this charging station
- * @param locationType
- * The location type
- * @return
- * the enabled EvcsModel
- */
- def apply(
- uuid: UUID,
- id: String,
- operationInterval: OperationInterval,
- scalingFactor: Double,
- qControl: QControl,
- sRated: squants.Power,
- cosPhiRated: Double,
- chargingPoints: Int,
- locationType: EvcsLocationType
- ): EvcsModel = {
- val model = new EvcsModel(
- uuid,
- id,
- operationInterval,
- scalingFactor,
- qControl,
- sRated,
- cosPhiRated,
- chargingPoints,
- locationType
- )
-
- model.enable()
-
- model
- }
-}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/FixedFeedInModel.scala b/src/main/scala/edu/ie3/simona/model/participant/FixedFeedInModel.scala
index 89fd5096ce..a348923bb5 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/FixedFeedInModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/FixedFeedInModel.scala
@@ -12,10 +12,12 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
-
import squants.Power
import squants.energy.Kilowatts
@@ -43,20 +45,24 @@ final case class FixedFeedInModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
- cosPhiRated: Double
-) extends SystemParticipant[FixedRelevantData.type, ApparentPower](
+ cosPhiRated: Double,
+) extends SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ](
uuid,
id,
operationInterval,
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
)
- with ApparentPowerParticipant[FixedRelevantData.type] {
+ with ApparentPowerParticipant[FixedRelevantData.type, ConstantState.type] {
/** Calculate the active power behaviour of the model
*
@@ -67,24 +73,41 @@ final case class FixedFeedInModel(
* Active power
*/
override protected def calculateActivePower(
- data: FixedRelevantData.type = FixedRelevantData
+ modelState: ConstantState.type,
+ data: FixedRelevantData.type = FixedRelevantData,
): Power =
- sRated * (-1) * cosPhiRated * scalingFactor
+ sRated * (-1) * cosPhiRated
+
+ override def determineFlexOptions(
+ data: FixedRelevantData.type,
+ lastState: ConstantState.type,
+ ): ProvideFlexOptions =
+ ProvideMinMaxFlexOptions.noFlexOption(
+ uuid,
+ calculateActivePower(lastState, data),
+ )
+
+ override def handleControlledPowerChange(
+ data: FixedRelevantData.type,
+ lastState: ConstantState.type,
+ setPower: Power,
+ ): (ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
}
-case object FixedFeedInModel extends LazyLogging {
+object FixedFeedInModel extends LazyLogging {
def apply(
inputModel: FixedFeedInInput,
modelConfiguration: SimonaConfig.FixedFeedInRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): FixedFeedInModel = {
/* Determine the operation interval */
val operationInterval: OperationInterval =
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- inputModel.getOperationTime
+ inputModel.getOperationTime,
)
// build the fixed feed in model
@@ -100,7 +123,7 @@ case object FixedFeedInModel extends LazyLogging {
.getValue
.doubleValue
),
- inputModel.getCosPhiRated
+ inputModel.getCosPhiRated,
)
model.enable()
model
diff --git a/src/main/scala/edu/ie3/simona/model/participant/FlexChangeIndicator.scala b/src/main/scala/edu/ie3/simona/model/participant/FlexChangeIndicator.scala
new file mode 100644
index 0000000000..dda77c2f06
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/participant/FlexChangeIndicator.scala
@@ -0,0 +1,12 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant
+
+final case class FlexChangeIndicator(
+ changesAtNextActivation: Boolean = false,
+ changesAtTick: Option[Long] = None,
+)
diff --git a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala
index 8b727a7390..ec2ad37b5e 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/HpModel.scala
@@ -11,8 +11,10 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPowerAndHe
import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.model.participant.HpModel.{HpRelevantData, HpState}
import edu.ie3.simona.model.participant.control.QControl
-import edu.ie3.simona.model.thermal.ThermalGrid
import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState
+import edu.ie3.simona.model.thermal.{ThermalGrid, ThermalThreshold}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.DefaultQuantities
@@ -51,15 +53,16 @@ final case class HpModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
pThermal: Power,
- thermalGrid: ThermalGrid
+ thermalGrid: ThermalGrid,
) extends SystemParticipant[
HpRelevantData,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
+ HpState,
](
uuid,
id,
@@ -67,37 +70,40 @@ final case class HpModel(
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
)
- with ApparentPowerAndHeatParticipant[HpRelevantData] {
+ with ApparentPowerAndHeatParticipant[HpRelevantData, HpState] {
private val pRated: Power =
- sRated * cosPhiRated * scalingFactor
+ sRated * cosPhiRated
/** As this is a state-full model (with respect to the current operation
* condition and inner temperature), the power calculation operates on the
* current state of the model, which has to be calculated beforehand by
- * [[HpModel.calculateNextState]]. This state then is fed into the power
- * calculation logic by hpData.
+ * [[HpModel.determineState]]. This state then is fed into the power
+ * calculation logic by [[HpState]].
*
+ * @param modelState
+ * Current state of the heat pump
* @param relevantData
* data of heat pump including state of the heat pump
* @return
* active power
*/
override protected def calculateActivePower(
- relevantData: HpRelevantData
- ): Power = {
- relevantData.hpState = calculateNextState(relevantData)
- relevantData.hpState.activePower
- }
+ modelState: HpState,
+ relevantData: HpRelevantData,
+ ): Power = modelState.activePower
/** "Calculate" the heat output of the heat pump. The hp's state is already
* updated, because the calculation of apparent power in
* [[ApparentPowerAndHeatParticipant]] did trigger it. So we only need to
* extract the information
+ *
* @param tick
* Current simulation time for the calculation
+ * @param modelState
+ * Current state of the heat pump
* @param data
* Relevant (external) data for calculation
* @return
@@ -105,23 +111,27 @@ final case class HpModel(
*/
override def calculateHeat(
tick: Long,
- data: HpRelevantData
- ): Power =
- data.hpState.qDot
+ modelState: HpState,
+ data: HpRelevantData,
+ ): Power = modelState.qDot
- /** Given a [[HpRelevantData]] object, containing the [[HpState]], other
- * values and the current time tick, this function calculates the heat pump's
- * next state To get the actual active power of this state use
- * [[calculateActivePower]] with the generated state
+ /** Given a [[HpRelevantData]] object and the current [[HpState]], this
+ * function calculates the heat pump's next state to get the actual active
+ * power of this state use [[calculateActivePower]] with the generated state
*
- * @param hpData
- * data of heat pump including state of the heat pump
+ * @param state
+ * Current state of the heat pump
+ * @param relevantData
+ * data of heat pump including
* @return
* next [[HpState]]
*/
- def calculateNextState(hpData: HpRelevantData): HpState = {
- val turnOn = operatesInNextState(hpData)
- calcState(hpData, turnOn)
+ def determineState(
+ state: HpState,
+ relevantData: HpRelevantData,
+ ): HpState = {
+ val turnOn = operatesInNextState(state, relevantData)
+ calcState(state, relevantData, turnOn)
}
/** Depending on the input, this function decides whether the heat pump will
@@ -130,74 +140,157 @@ final case class HpModel(
* met or the heat pump currently is in operation and the grid is able to
* handle additional energy
*
+ * @param state
+ * Current state of the heat pump
+ * @param relevantData
+ * Relevant (external) data
* @return
* boolean defining if heat pump runs in next time step
*/
- private def operatesInNextState(hpData: HpRelevantData): Boolean =
- hpData match {
- case HpRelevantData(hpState, currentTimeTick, ambientTemperature) =>
- val demand = thermalGrid.energyDemand(
- currentTimeTick,
- ambientTemperature,
- hpState.thermalGridState
- )
- demand.hasRequiredDemand || (hpState.isRunning && demand.hasAdditionalDemand)
- }
+ private def operatesInNextState(
+ state: HpState,
+ relevantData: HpRelevantData,
+ ): Boolean = {
+ val demand = thermalGrid.energyDemand(
+ relevantData.currentTick,
+ relevantData.ambientTemperature,
+ state.thermalGridState,
+ )
+ demand.hasRequiredDemand || (state.isRunning && demand.hasAdditionalDemand)
+ }
/** Calculate state depending on whether heat pump is needed or not. Also
* calculate inner temperature change of thermal house and update its inner
* temperature.
*
- * @param hpData
+ * @param state
+ * Current state of the heat pump
+ * @param relevantData
* data of heat pump including state of the heat pump
* @param isRunning
* determines whether the heat pump is running or not
* @return
* next [[HpState]]
*/
- private def calcState(hpData: HpRelevantData, isRunning: Boolean): HpState = {
+ private def calcState(
+ state: HpState,
+ relevantData: HpRelevantData,
+ isRunning: Boolean,
+ ): HpState = {
val (newActivePower, newThermalPower) =
if (isRunning)
- (pRated, pThermal * scalingFactor)
+ (pRated, pThermal)
else (DefaultQuantities.zeroKW, DefaultQuantities.zeroKW)
- /* Push thermal energy to the thermal grid and get it's updated state in return */
- val thermalGridState = hpData match {
- case HpRelevantData(hpState, currentTimeTick, ambientTemperature) =>
- thermalGrid.updateState(
- currentTimeTick,
- hpState.thermalGridState,
- ambientTemperature,
- newThermalPower
- )
- }
+
+ /* Push thermal energy to the thermal grid and get its updated state in return */
+ val (thermalGridState, maybeThreshold) =
+ thermalGrid.updateState(
+ relevantData.currentTick,
+ state.thermalGridState,
+ state.ambientTemperature.getOrElse(relevantData.ambientTemperature),
+ newThermalPower,
+ )
HpState(
isRunning,
- hpData.currentTimeTick,
+ relevantData.currentTick,
+ Some(relevantData.ambientTemperature),
newActivePower,
newThermalPower,
- thermalGridState._1
+ thermalGridState,
+ maybeThreshold,
+ )
+ }
+
+ override def determineFlexOptions(
+ data: HpRelevantData,
+ lastState: HpState,
+ ): ProvideFlexOptions = {
+ /* Determine the operating state in the given tick */
+ val updatedState = determineState(lastState, data)
+
+ /* Determine the options we have */
+ val thermalEnergyDemand = thermalGrid.energyDemand(
+ data.currentTick,
+ data.ambientTemperature,
+ lastState.thermalGridState,
+ )
+ val canOperate =
+ thermalEnergyDemand.hasRequiredDemand || thermalEnergyDemand.hasAdditionalDemand
+ val canBeOutOfOperation = !thermalEnergyDemand.hasRequiredDemand
+
+ val lowerBoundary =
+ if (canBeOutOfOperation)
+ Kilowatts(0d)
+ else
+ updatedState.activePower
+ val upperBoundary =
+ if (canOperate)
+ sRated * cosPhiRated
+ else
+ Kilowatts(0d)
+
+ ProvideMinMaxFlexOptions(
+ uuid,
+ updatedState.activePower,
+ lowerBoundary,
+ upperBoundary,
+ )
+ }
+
+ /** Handle a controlled power change. If the requested active power is greater
+ * than 50 % of the rated electrical power switch on the heat pump, otherwise
+ * switch it off. If it is already in the requested state, return old state
+ * and next trigger information, otherwise, update the state with new
+ * operating state and give back the next tick in which something will
+ * change.
+ *
+ * @param data
+ * Relevant data for model calculation
+ * @param lastState
+ * The last known model state
+ * @param setPower
+ * power that has been set by EmAgent
+ * @return
+ * updated relevant data and an indication at which circumstances flex
+ * options will change next
+ */
+ override def handleControlledPowerChange(
+ data: HpRelevantData,
+ lastState: HpState,
+ setPower: Power,
+ ): (HpState, FlexChangeIndicator) = {
+ /* If the setpoint value is above 50 % of the electrical power, turn on the heat pump otherwise turn it off */
+ val turnOn = setPower > (sRated * cosPhiRated * 0.5)
+ val updatedState = calcState(lastState, data, turnOn)
+
+ (
+ updatedState,
+ FlexChangeIndicator(
+ changesAtNextActivation = true,
+ updatedState.maybeThermalThreshold.map(_.tick),
+ ),
)
}
}
/** Create valid [[HpModel]] by calling the apply function.
*/
-case object HpModel {
+object HpModel {
def apply(
inputModel: HpInput,
scaling: Double,
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
- thermalGrid: ThermalGrid
+ thermalGrid: ThermalGrid,
): HpModel = {
/* Determine the operation interval */
val operationInterval: OperationInterval =
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- inputModel.getOperationTime
+ inputModel.getOperationTime,
)
val qControl = QControl(inputModel.getqCharacteristics())
@@ -209,21 +302,19 @@ case object HpModel {
scaling,
qControl,
Kilowatts(
- inputModel.getType
- .getsRated()
+ inputModel.getType.getsRated
.to(PowerSystemUnits.KILOWATT)
.getValue
.doubleValue
),
inputModel.getType.getCosPhiRated,
Kilowatts(
- inputModel.getType
- .getpThermal()
+ inputModel.getType.getpThermal
.to(PowerSystemUnits.KILOWATT)
.getValue
.doubleValue
),
- thermalGrid
+ thermalGrid,
)
model.enable()
@@ -238,36 +329,40 @@ case object HpModel {
* indicates if CHP is turned on
* @param lastTimeTick
* contains last time tick
+ * @param ambientTemperature
+ * Optional ambient temperature, if available
* @param activePower
* result active power
* @param qDot
* result heat power
* @param thermalGridState
* Currently applicable state of the thermal grid
+ * @param maybeThermalThreshold
+ * An optional threshold of the thermal grid, indicating the next state
+ * change
*/
final case class HpState(
isRunning: Boolean,
lastTimeTick: Long,
+ ambientTemperature: Option[Temperature],
activePower: Power,
qDot: Power,
- thermalGridState: ThermalGridState
- )
+ thermalGridState: ThermalGridState,
+ maybeThermalThreshold: Option[ThermalThreshold],
+ ) extends ModelState
/** Main data required for simulation/calculation, containing a [[HpState]]
- * and the current time tick. [[HpRelevantData.currentTimeTick]] and
- * [[HpState.lastTimeTick]] form a time interval for the current state
- * calculation. One time tick represents one second (3600 time ticks = 1
- * hour).
+ * and the current time tick. One time tick represents one second (3600 time
+ * ticks = 1 hour).
*
- * @param hpState
- * a [[HpState]]
- * @param currentTimeTick
+ * @param currentTick
* contains current time tick
+ * @param ambientTemperature
+ * Ambient temperature at current tick
*/
final case class HpRelevantData(
- var hpState: HpState,
- currentTimeTick: Long,
- ambientTemperature: Temperature
+ currentTick: Long,
+ ambientTemperature: Temperature,
) extends CalcRelevantData
/** Internal method to construct a new [[HpModel]] based on a provided
@@ -275,10 +370,14 @@ case object HpModel {
*
* @param hpInput
* instance of [[HpInput]] this chp model should be built from
- * @param operationInterval
- * operation interval of the simulation
+ * @param simulationStartDate
+ * Simulation time at which the simulation starts
+ * @param simulationEndDate
+ * Simulation time at which the simulation ends
* @param qControl
- * (no usage)
+ * Strategy to control the reactive power output
+ * @param scalingFactor
+ * Scale the output of this asset by the given factor
* @param thermalGrid
* thermal grid, defining the behaviour of the connected sinks and storages
* @return
@@ -286,15 +385,23 @@ case object HpModel {
*/
def apply(
hpInput: HpInput,
- operationInterval: OperationInterval,
+ simulationStartDate: ZonedDateTime,
+ simulationEndDate: ZonedDateTime,
qControl: QControl,
- thermalGrid: ThermalGrid
+ scalingFactor: Double,
+ thermalGrid: ThermalGrid,
): HpModel = {
+ val operationInterval = SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ hpInput.getOperationTime,
+ )
+
val model = new HpModel(
hpInput.getUuid,
hpInput.getId,
operationInterval,
- scalingFactor = 1.0,
+ scalingFactor,
qControl,
Kilowatts(
hpInput.getType.getsRated
@@ -309,7 +416,7 @@ case object HpModel {
.getValue
.doubleValue
),
- thermalGrid
+ thermalGrid,
)
model.enable()
diff --git a/src/main/scala/edu/ie3/simona/model/participant/ModelState.scala b/src/main/scala/edu/ie3/simona/model/participant/ModelState.scala
new file mode 100644
index 0000000000..1416a6a4f5
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/participant/ModelState.scala
@@ -0,0 +1,14 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant
+
+trait ModelState
+
+object ModelState {
+
+ case object ConstantState extends ModelState
+}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/PvModel.scala b/src/main/scala/edu/ie3/simona/model/participant/PvModel.scala
index 34ca829923..fb8023b40a 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/PvModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/PvModel.scala
@@ -9,8 +9,11 @@ package edu.ie3.simona.model.participant
import edu.ie3.datamodel.models.input.system.PvInput
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.PvModel.PvRelevantData
import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities._
@@ -29,7 +32,7 @@ final case class PvModel private (
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
@@ -39,17 +42,17 @@ final case class PvModel private (
private val etaConv: Dimensionless,
private val alphaE: Angle,
private val gammaE: Angle,
- private val moduleSurface: Area = SquareMeters(1d)
-) extends SystemParticipant[PvRelevantData, ApparentPower](
+ private val moduleSurface: Area = SquareMeters(1d),
+) extends SystemParticipant[PvRelevantData, ApparentPower, ConstantState.type](
uuid,
id,
operationInterval,
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
)
- with ApparentPowerParticipant[PvRelevantData] {
+ with ApparentPowerParticipant[PvRelevantData, ConstantState.type] {
/** Override sMax as the power output of a pv unit could become easily up to
* 10% higher than the sRated value found in the technical sheets
@@ -72,7 +75,8 @@ final case class PvModel private (
* Active power
*/
override protected def calculateActivePower(
- data: PvRelevantData
+ modelState: ConstantState.type,
+ data: PvRelevantData,
): Power = {
// === Weather Base Data === //
/* The pv model calculates the power in-feed based on the solar irradiance that is received over a specific
@@ -111,7 +115,7 @@ final case class PvModel private (
lat,
gammaE,
alphaE,
- duration
+ duration,
)
// === Diffuse Radiation Parameters ===//
@@ -127,7 +131,7 @@ final case class PvModel private (
extraterrestrialRadiationI0,
thetaZ,
thetaG,
- gammaE
+ gammaE,
)
// === Reflected Radiation ===//
@@ -141,8 +145,8 @@ final case class PvModel private (
calcOutput(
eTotal,
data.dateTime,
- irraditionSTC
- ) * scalingFactor
+ irraditionSTC,
+ )
}
/** Calculates the position of the earth in relation to the sun (day angle)
@@ -200,7 +204,7 @@ final case class PvModel private (
private def calcHourAngleOmega(
time: ZonedDateTime,
angleJ: Angle,
- longitude: Angle
+ longitude: Angle,
): Angle = {
val jInRad = angleJ.toRadians
val lambda = longitude.toDegrees
@@ -229,7 +233,7 @@ final case class PvModel private (
*/
private def calcSunsetAngleOmegaSS(
latitude: Angle,
- delta: Angle
+ delta: Angle,
): Angle = {
val latInRad = latitude.toRadians
val deltaInRad = delta.toRadians
@@ -258,7 +262,7 @@ final case class PvModel private (
private def calcSolarAltitudeAngleAlphaS(
omega: Angle,
delta: Angle,
- latitude: Angle
+ latitude: Angle,
): Angle = {
val latInRad = latitude.toRadians
val deltaInRad = delta.toRadians
@@ -268,9 +272,9 @@ final case class PvModel private (
max(
cos(omegaInRad) * cos(latInRad) * cos(deltaInRad) +
sin(latInRad) * sin(deltaInRad),
- -1
+ -1,
),
- 1
+ 1,
)
Radians(asin(sinAlphaS))
}
@@ -365,7 +369,7 @@ final case class PvModel private (
latitude: Angle,
gammaE: Angle,
alphaE: Angle,
- omega: Angle
+ omega: Angle,
): Angle = {
val deltaInRad = delta.toRadians
val omegaInRad = omega.toRadians
@@ -404,7 +408,7 @@ final case class PvModel private (
thetaG: Angle,
omega: Angle,
omegaSS: Angle,
- omegaSR: Angle
+ omegaSR: Angle,
): Option[(Angle, Angle)] = {
val omegaSSInRad = omegaSS.toRadians
val omegaSRInRad = omegaSR.toRadians
@@ -473,7 +477,7 @@ final case class PvModel private (
latitude: Angle,
gammaE: Angle,
alphaE: Angle,
- duration: Time
+ duration: Time,
): Irradiation = {
omegas match {
@@ -566,7 +570,7 @@ final case class PvModel private (
extraterrestrialRadiationI0: Irradiation,
thetaZ: Angle,
thetaG: Angle,
- gammaE: Angle
+ gammaE: Angle,
): Irradiation = {
val thetaZInRad = thetaZ.toRadians
val thetaGInRad = thetaG.toRadians
@@ -584,10 +588,10 @@ final case class PvModel private (
var epsilon = ((eDifH + eBeamH / cos(thetaZInRad)) / eDifH +
(5.535d * 1.0e-6) * pow(
thetaZ.toDegrees,
- 3
+ 3,
)) / (1d + (5.535d * 1.0e-6) * pow(
thetaZ.toDegrees,
- 3
+ 3,
))
// get the corresponding bin if epsilon is smaller than 6.2
@@ -599,7 +603,7 @@ final case class PvModel private (
Array(1.500, 1.950),
Array(1.950, 2.800),
Array(2.800, 4.500),
- Array(4.500, 6.200)
+ Array(4.500, 6.200),
)
// adapt the epsilon as we have no bin < 1
epsilon = max(1, epsilon)
@@ -663,7 +667,7 @@ final case class PvModel private (
eBeamH: Irradiation,
eDifH: Irradiation,
gammaE: Angle,
- albedo: Double
+ albedo: Double,
): Irradiation = {
val gammaEInRad = gammaE.toRadians
(eBeamH + eDifH) * (albedo * 0.5 * (1 - cos(gammaEInRad)))
@@ -671,7 +675,7 @@ final case class PvModel private (
private def generatorCorrectionFactor(
time: ZonedDateTime,
- gammaE: Angle
+ gammaE: Angle,
): Double = {
val gammaEValInDeg = gammaE.toDegrees
@@ -705,7 +709,7 @@ final case class PvModel private (
private def calcOutput(
eTotalInWhPerSM: Irradiation,
time: ZonedDateTime,
- irradiationSTC: Irradiation
+ irradiationSTC: Irradiation,
): Power = {
val genCorr = generatorCorrectionFactor(time, gammaE)
val tempCorr = temperatureCorrectionFactor(time)
@@ -726,7 +730,7 @@ final case class PvModel private (
"The fed in active power is higher than the estimated maximum active power of this plant ({} < {}). " +
"Did you provide wrong weather input data?",
proposal,
- pMax
+ pMax,
)
/* If the output is marginally small, suppress the output, as we are likely to be in night and then only produce incorrect output */
@@ -734,6 +738,22 @@ final case class PvModel private (
DefaultQuantities.zeroMW
else proposal
}
+
+ override def determineFlexOptions(
+ data: PvRelevantData,
+ lastState: ConstantState.type,
+ ): ProvideFlexOptions = {
+ val power = calculateActivePower(ConstantState, data)
+
+ ProvideMinMaxFlexOptions(uuid, power, power, DefaultQuantities.zeroKW)
+ }
+
+ override def handleControlledPowerChange(
+ data: PvRelevantData,
+ lastState: ConstantState.type,
+ setPower: squants.Power,
+ ): (ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
}
object PvModel {
@@ -754,21 +774,21 @@ object PvModel {
dateTime: ZonedDateTime,
weatherDataFrameLength: Long,
diffIrradiance: Irradiance,
- dirIrradiance: Irradiance
+ dirIrradiance: Irradiance,
) extends CalcRelevantData
def apply(
inputModel: PvInput,
scalingFactor: Double,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): PvModel = {
/* Determine the operation interval */
val operationInterval: OperationInterval =
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- inputModel.getOperationTime
+ inputModel.getOperationTime,
)
// moduleSurface and yieldSTC are left out for now
@@ -805,7 +825,7 @@ object PvModel {
.to(RADIAN)
.getValue
.doubleValue
- )
+ ),
)
model.enable()
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 e8bbf14c5a..92d47f8ae0 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala
@@ -8,15 +8,16 @@ package edu.ie3.simona.model.participant
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- PrimaryDataWithApparentPower
+ PrimaryDataWithApparentPower,
}
import edu.ie3.simona.model.SystemComponent
import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.{
DefaultQuantities,
Megavars,
- ReactivePower
+ ReactivePower,
}
import squants.Dimensionless
import squants.energy.{Kilowatts, Power}
@@ -43,18 +44,21 @@ import java.util.UUID
* Type of data, that is needed for model calculation
* @tparam PD
* Primary data, that this asset does produce
+ * @tparam MS
+ * Type of model state data
*/
abstract class SystemParticipant[
CD <: CalcRelevantData,
- +PD <: PrimaryDataWithApparentPower[PD]
+ +PD <: PrimaryDataWithApparentPower[PD],
+ MS <: ModelState,
](
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ val scalingFactor: Double,
qControl: QControl,
sRated: Power,
- cosPhiRated: Double
+ cosPhiRated: Double,
) extends SystemComponent(uuid, id, operationInterval) {
/** Maximum allowed apparent power output of this system participant. Used to
@@ -70,6 +74,8 @@ abstract class SystemParticipant[
* Regarded instant in simulation
* @param voltage
* Nodal voltage magnitude
+ * @param modelState
+ * Current state of the model
* @param data
* Further needed, secondary data
* @return
@@ -78,7 +84,8 @@ abstract class SystemParticipant[
def calculatePower(
tick: Long,
voltage: Dimensionless,
- data: CD
+ modelState: MS,
+ data: CD,
): PD
/** Calculate the apparent power behaviour based on the given data.
@@ -95,29 +102,62 @@ abstract class SystemParticipant[
protected def calculateApparentPower(
tick: Long,
voltage: Dimensionless,
- data: CD
+ modelState: MS,
+ data: CD,
): ApparentPower = {
if (isInOperation(tick)) {
- val activePower = calculateActivePower(data)
+ val activePower = calculateActivePower(modelState, data)
val reactivePower =
calculateReactivePower(activePower, voltage)
- ApparentPower(activePower, reactivePower)
+ ApparentPower(
+ activePower * scalingFactor,
+ reactivePower * scalingFactor,
+ )
} else {
ApparentPower(
DefaultQuantities.zeroMW,
- DefaultQuantities.zeroMVAr
+ DefaultQuantities.zeroMVAr,
)
}
}
/** Calculate the active power behaviour of the model
*
+ * @param modelState
+ * Current state of the model
* @param data
* Further needed, secondary data
* @return
* Active power
*/
- protected def calculateActivePower(data: CD): Power
+ protected def calculateActivePower(
+ modelState: MS,
+ data: CD,
+ ): Power
+
+ /** @param data
+ * @param lastState
+ * @return
+ * flex options
+ */
+ def determineFlexOptions(
+ data: CD,
+ lastState: MS,
+ ): ProvideFlexOptions
+
+ /** @param data
+ * @param lastState
+ * @param setPower
+ * power that has been set by EmAgent
+ * @return
+ * updated relevant data and an indication at which circumstances flex
+ * options will change next
+ */
+ def handleControlledPowerChange(
+ data: CD,
+ lastState: MS,
+ setPower: Power,
+ ): (MS, FlexChangeIndicator)
/** Get a partial function, that transfers the current active into reactive
* power based on the participants properties and the given nodal voltage
@@ -131,9 +171,9 @@ abstract class SystemParticipant[
nodalVoltage: Dimensionless
): Power => ReactivePower =
qControl.activeToReactivePowerFunc(
- sRated * scalingFactor,
+ sRated,
cosPhiRated,
- nodalVoltage
+ nodalVoltage,
)
/** Calculate the reactive power of the model
@@ -147,11 +187,11 @@ abstract class SystemParticipant[
*/
def calculateReactivePower(
activePower: Power,
- voltage: Dimensionless
+ voltage: Dimensionless,
): ReactivePower = {
limitReactivePower(
activePower,
- activeToReactivePowerFunc(voltage)(activePower)
+ activeToReactivePowerFunc(voltage)(activePower),
)
}
@@ -167,7 +207,7 @@ abstract class SystemParticipant[
*/
private def limitReactivePower(
activePower: Power,
- reactivePower: ReactivePower
+ reactivePower: ReactivePower,
): ReactivePower = {
{
val apparentPower: Power = Kilowatts(
diff --git a/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala b/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala
index ca1719dbb0..2154f03d03 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala
@@ -10,21 +10,24 @@ import edu.ie3.datamodel.models.input.system.WecInput
import edu.ie3.datamodel.models.input.system.characteristic.WecCharacteristicInput
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.WecModel.{
WecCharacteristic,
- WecRelevantData
+ WecRelevantData,
}
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.system.Characteristic
import edu.ie3.simona.model.system.Characteristic.XYPair
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.quantities.PowerSystemUnits._
import edu.ie3.util.scala.OperationInterval
+import squants._
import squants.energy.{Kilowatts, Watts}
import squants.mass.{Kilograms, KilogramsPerCubicMeter}
import squants.motion.{MetersPerSecond, Pressure}
import squants.space.SquareMeters
import squants.thermal.JoulesPerKelvin
-import squants._
import tech.units.indriya.unit.Units._
import java.time.ZonedDateTime
@@ -57,24 +60,24 @@ final case class WecModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
rotorArea: Area,
- betzCurve: WecCharacteristic
-) extends SystemParticipant[WecRelevantData, ApparentPower](
+ betzCurve: WecCharacteristic,
+) extends SystemParticipant[WecRelevantData, ApparentPower, ConstantState.type](
uuid,
id,
operationInterval,
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
)
- with ApparentPowerParticipant[WecRelevantData] {
+ with ApparentPowerParticipant[WecRelevantData, ConstantState.type] {
- /** Universal gas constant, actually in J/(K * mol)
+ /** Universal gas constant
*/
private val UniversalGasConstantR = JoulesPerKelvin(8.31446261815324d)
@@ -91,7 +94,8 @@ final case class WecModel(
* active power output
*/
override protected def calculateActivePower(
- wecData: WecRelevantData
+ modelState: ConstantState.type,
+ wecData: WecRelevantData,
): Power = {
val activePower = determinePower(wecData)
val pMax = sMax * cosPhiRated
@@ -101,7 +105,7 @@ final case class WecModel(
"The fed in active power is higher than the estimated maximum active power of this plant ({} > {}). " +
"Did you provide wrong weather input data?",
activePower,
- pMax
+ pMax,
)
pMax
} else {
@@ -130,7 +134,7 @@ final case class WecModel(
val airDensity =
calculateAirDensity(
wecData.temperature,
- wecData.airPressure
+ wecData.airPressure,
).toKilogramsPerCubicMeter
val v = wecData.windVelocity.toMetersPerSecond
@@ -175,7 +179,7 @@ final case class WecModel(
*/
private def calculateAirDensity(
temperature: Temperature,
- airPressure: Option[Pressure]
+ airPressure: Option[Pressure],
): Density = {
airPressure match {
case None =>
@@ -188,6 +192,22 @@ final case class WecModel(
)
}
}
+
+ override def determineFlexOptions(
+ data: WecRelevantData,
+ lastState: ConstantState.type,
+ ): ProvideFlexOptions = {
+ val power = calculateActivePower(ConstantState, data)
+
+ ProvideMinMaxFlexOptions(uuid, power, power, Kilowatts(0d))
+ }
+
+ override def handleControlledPowerChange(
+ data: WecRelevantData,
+ lastState: ConstantState.type,
+ setPower: Power,
+ ): (ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
}
/** Create valid [[WecModel]] by calling the apply function.
@@ -216,7 +236,7 @@ object WecModel {
input.getPoints.asScala.map(p =>
XYPair[Velocity, Dimensionless](
MetersPerSecond(p.getX.to(METRE_PER_SECOND).getValue.doubleValue),
- Each(p.getY.to(PU).getValue.doubleValue)
+ Each(p.getY.to(PU).getValue.doubleValue),
)
)
)
@@ -235,19 +255,19 @@ object WecModel {
final case class WecRelevantData(
windVelocity: Velocity,
temperature: Temperature,
- airPressure: Option[Pressure]
+ airPressure: Option[Pressure],
) extends CalcRelevantData
def apply(
inputModel: WecInput,
scalingFactor: Double,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): WecModel = {
val operationInterval = SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- inputModel.getOperationTime
+ inputModel.getOperationTime,
)
val model = new WecModel(
@@ -261,7 +281,7 @@ object WecModel {
SquareMeters(
inputModel.getType.getRotorArea.to(SQUARE_METRE).getValue.doubleValue
),
- WecCharacteristic(inputModel.getType.getCpCharacteristic)
+ WecCharacteristic(inputModel.getType.getCpCharacteristic),
)
model.enable()
diff --git a/src/main/scala/edu/ie3/simona/model/participant/control/QControl.scala b/src/main/scala/edu/ie3/simona/model/participant/control/QControl.scala
index 02c42be94f..37355d55b4 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/control/QControl.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/control/QControl.scala
@@ -45,7 +45,7 @@ sealed trait QControl {
def activeToReactivePowerFunc(
sRated: Power,
cosPhiRated: Double,
- nodalVoltage: Dimensionless
+ nodalVoltage: Dimensionless,
): Power => ReactivePower
}
@@ -73,7 +73,7 @@ object QControl {
cosPhiP.getPoints.asScala.map(point =>
XYPair[Dimensionless, Dimensionless](
Each(point.getX.getValue.doubleValue()),
- Each(point.getY.getValue.doubleValue())
+ Each(point.getY.getValue.doubleValue()),
)
)
)
@@ -85,7 +85,7 @@ object QControl {
.map(point =>
XYPair[Dimensionless, Dimensionless](
Each(point.getX.getValue.doubleValue()),
- Each(point.getY.getValue.doubleValue())
+ Each(point.getY.getValue.doubleValue()),
)
)
.toSeq
@@ -118,7 +118,7 @@ object QControl {
override def activeToReactivePowerFunc(
sRated: Power,
cosPhiRated: Double,
- nodalVoltage: Dimensionless
+ nodalVoltage: Dimensionless,
): Power => ReactivePower = { activePower: Power =>
_cosPhiMultiplication(cosPhi, activePower)
}
@@ -151,7 +151,7 @@ object QControl {
*/
def q(
vInPu: Dimensionless,
- qMax: ReactivePower
+ qMax: ReactivePower,
): ReactivePower = {
qMax * interpolateXy(vInPu)._2.toEach
}
@@ -170,7 +170,7 @@ object QControl {
override def activeToReactivePowerFunc(
sRated: Power,
cosPhiRated: Double,
- nodalVoltage: Dimensionless
+ nodalVoltage: Dimensionless,
): Power => ReactivePower = { activePower: Power =>
val qMaxFromP = Megavars(
sqrt(
@@ -197,7 +197,7 @@ object QControl {
*/
private def qMaxPossible(
qMaxFromP: ReactivePower,
- qFromCharacteristic: ReactivePower
+ qFromCharacteristic: ReactivePower,
): ReactivePower =
if (qFromCharacteristic.abs >= qMaxFromP.abs)
qMaxFromP * copySign(1, qFromCharacteristic.toMegavars)
@@ -245,7 +245,7 @@ object QControl {
override def activeToReactivePowerFunc(
sRated: Power,
cosPhiRated: Double,
- nodalVoltage: Dimensionless
+ nodalVoltage: Dimensionless,
): Power => ReactivePower = { activePower: Power =>
/* cosphi( P / P_N ) = cosphi( P / (S_N * cosphi_rated) ) */
val pInPu =
diff --git a/src/main/scala/edu/ie3/simona/model/participant/evcs/ChargingStrategy.scala b/src/main/scala/edu/ie3/simona/model/participant/evcs/ChargingStrategy.scala
new file mode 100644
index 0000000000..d21965f8d1
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/participant/evcs/ChargingStrategy.scala
@@ -0,0 +1,25 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs
+
+/** Enumeration that represents all implemented charging strategies
+ */
+object ChargingStrategy extends Enumeration {
+ val MAX_POWER, CONSTANT_POWER, GRID_ORIENTED, MARKET_ORIENTED = Value
+
+ def apply(token: String): ChargingStrategy.Value =
+ "[-_]".r.replaceAllIn(token.trim.toLowerCase, "") match {
+ case "maxpower" => MAX_POWER
+ case "constantpower" => CONSTANT_POWER
+ case "gridorientedscheduling" => GRID_ORIENTED
+ case "marketorientedscheduling" => MARKET_ORIENTED
+ case malformed =>
+ throw new RuntimeException(
+ s"The token '$malformed' cannot be parsed to charging strategy."
+ )
+ }
+}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/EvModelWrapper.scala b/src/main/scala/edu/ie3/simona/model/participant/evcs/EvModelWrapper.scala
similarity index 57%
rename from src/main/scala/edu/ie3/simona/model/participant/EvModelWrapper.scala
rename to src/main/scala/edu/ie3/simona/model/participant/evcs/EvModelWrapper.scala
index 7b9019aede..f928af2fcf 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/EvModelWrapper.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/evcs/EvModelWrapper.scala
@@ -4,7 +4,7 @@
* Research group Distribution grid planning and operation
*/
-package edu.ie3.simona.model.participant
+package edu.ie3.simona.model.participant.evcs
import edu.ie3.simona.api.data.ev.model.EvModel
import edu.ie3.util.quantities.PowerSystemUnits._
@@ -13,9 +13,21 @@ import squants.energy.{KilowattHours, Kilowatts}
import java.util.UUID
+/** Wrapper for objects that extend [[EvModel]], which uses
+ * [[javax.measure.Quantity]]. Since operations on javax/indriya quantities are
+ * a bit slow, we lazily convert them to [[squants.Quantity]]. When
+ * "unwrapping", we store back the only value that can have changed (while
+ * considering immutability, i.e. when using [[copy]]), which is
+ * [[storedEnergy]].
+ *
+ * @param storedEnergy
+ * Currently stored energy in the EV battery
+ * @param original
+ * The wrapped [[EvModel]]
+ */
final case class EvModelWrapper(
storedEnergy: squants.Energy,
- private val original: EvModel
+ private val original: EvModel,
) {
def uuid: UUID = original.getUuid
@@ -29,22 +41,27 @@ final case class EvModelWrapper(
)
def departureTick: Long = original.getDepartureTick
- def unwrap(): EvModel = {
+ /** Unwrapping the original [[EvModel]] while also updating the
+ * [[storedEnergy]], which could have changed.
+ *
+ * @return
+ * The original [[EvModel]] with updated stored energy.
+ */
+ def unwrap(): EvModel =
original.copyWith(
storedEnergy.toKilowattHours.asKiloWattHour
)
- }
}
object EvModelWrapper {
- def apply(evModel: EvModel): EvModelWrapper = {
+ def apply(evModel: EvModel): EvModelWrapper =
new EvModelWrapper(
KilowattHours(
evModel.getStoredEnergy.to(KILOWATTHOUR).getValue.doubleValue
),
- evModel
+ evModel,
)
- }
+
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala b/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala
new file mode 100644
index 0000000000..c6445dd0ea
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/participant/evcs/EvcsModel.scala
@@ -0,0 +1,1082 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs
+
+import com.typesafe.scalalogging.LazyLogging
+import edu.ie3.datamodel.models.ElectricCurrentType
+import edu.ie3.datamodel.models.input.system.EvcsInput
+import edu.ie3.datamodel.models.input.system.`type`.evcslocation.EvcsLocationType
+import edu.ie3.datamodel.models.result.system.{EvResult, EvcsResult}
+import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
+import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.model.participant.evcs.EvcsModel._
+import edu.ie3.simona.model.participant.evcs.uncontrolled.{
+ ConstantPowerCharging,
+ MaximumPowerCharging,
+}
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ FlexChangeIndicator,
+ ModelState,
+ SystemParticipant,
+}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
+import edu.ie3.simona.util.TickUtil.TickLong
+import edu.ie3.util.quantities.PowerSystemUnits._
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
+import edu.ie3.util.scala.OperationInterval
+import squants.energy.{KilowattHours, Kilowatts}
+import squants.time.Seconds
+import squants.{Dimensionless, Energy, Power}
+import tech.units.indriya.unit.Units.PERCENT
+
+import java.time.ZonedDateTime
+import java.util.UUID
+import scala.collection.SortedMap
+import scala.collection.immutable.SortedSet
+
+/** EV charging station model
+ *
+ * @param uuid
+ * the element's uuid
+ * @param id
+ * the element's human readable id
+ * @param operationInterval
+ * Interval, in which the system is in operation
+ * @param scalingFactor
+ * Scaling the output of the system
+ * @param simulationStartDate
+ * The start date of the simulation
+ * @param qControl
+ * Type of reactive power control
+ * @param sRated
+ * Rated apparent power per charging point
+ * @param cosPhiRated
+ * Rated power factor
+ * @param chargingPoints
+ * Number of charging points available at this charging station
+ * @param locationType
+ * The location type
+ * @param strategy
+ * Strategy to follow in oder to determine the charging habits
+ * @param lowestEvSoc
+ * The lowest SOC possible for EV batteries (inverse of max dod)
+ */
+final case class EvcsModel(
+ uuid: UUID,
+ id: String,
+ operationInterval: OperationInterval,
+ override val scalingFactor: Double,
+ simulationStartDate: ZonedDateTime,
+ qControl: QControl,
+ sRated: Power,
+ currentType: ElectricCurrentType,
+ cosPhiRated: Double,
+ chargingPoints: Int,
+ locationType: EvcsLocationType,
+ vehicle2grid: Boolean,
+ strategy: ChargingStrategy.Value,
+ lowestEvSoc: Double,
+) extends SystemParticipant[EvcsRelevantData, ApparentPower, EvcsState](
+ uuid,
+ id,
+ operationInterval,
+ scalingFactor,
+ qControl,
+ sRated * chargingPoints,
+ cosPhiRated,
+ )
+ with LazyLogging
+ with MaximumPowerCharging
+ with ConstantPowerCharging {
+
+ /** Determine scheduling for charging the EVs currently parked at the charging
+ * station until their departure. The scheduling depends on the chosen
+ * strategy.
+ *
+ * @param data
+ * data including the current EVs parked at the charging station
+ * @return
+ * Charging schedule for charging given EVs
+ */
+ def calculateNewScheduling(
+ data: EvcsRelevantData,
+ evs: Seq[EvModelWrapper],
+ ): ScheduleMap = {
+ if (
+ locationType == EvcsLocationType.CHARGING_HUB_TOWN || locationType == EvcsLocationType.CHARGING_HUB_HIGHWAY
+ ) {
+ /* Cars at charging hubs always charge eagerly */
+ chargeWithMaximumPower(
+ data.tick,
+ evs,
+ )
+ } else
+ scheduleByStrategy(
+ strategy,
+ data.tick,
+ evs,
+ )
+ }
+
+ /** Determine the schedule by defined charging strategy
+ *
+ * @param strategy
+ * Chosen charging strategy
+ * @param currentTick
+ * Current simulation time
+ * @param evs
+ * Collection of currently parked evs
+ * @return
+ * Charging schedule for charging given EVs
+ */
+ private def scheduleByStrategy(
+ strategy: ChargingStrategy.Value,
+ currentTick: Long,
+ evs: Seq[EvModelWrapper],
+ ): ScheduleMap = strategy match {
+ case ChargingStrategy.MAX_POWER =>
+ chargeWithMaximumPower(
+ currentTick,
+ evs,
+ )
+ case ChargingStrategy.CONSTANT_POWER =>
+ chargeWithConstantPower(
+ currentTick,
+ evs,
+ )
+ case ChargingStrategy.GRID_ORIENTED =>
+ throw new NotImplementedError(
+ "Grid oriented strategy currently not implemented"
+ )
+ case ChargingStrategy.MARKET_ORIENTED =>
+ throw new NotImplementedError(
+ "Market oriented strategy currently not implemented"
+ )
+
+ }
+
+ /** Update EVs for the timeframe since the last scheduling
+ *
+ * @param state
+ * the last state
+ * @param currentTick
+ * current tick
+ * @return
+ * updated EVs
+ */
+ def applySchedule(
+ state: EvcsState,
+ currentTick: Long,
+ ): Seq[EvModelWrapper] = if (state.schedule.nonEmpty) {
+ state.evs
+ .map(ev =>
+ state.schedule
+ .get(ev.uuid)
+ .map {
+ chargeEv(
+ ev,
+ _,
+ state.tick,
+ currentTick,
+ )
+ }
+ .getOrElse(ev)
+ )
+ } else {
+ logger.debug(
+ "There are EVs parked at this charging station, but there was no scheduling since the last" +
+ "update. Probably the EVs already finished charging."
+ )
+ state.evs
+ }
+
+ /** Charge the given EV under consideration a applicable schedule
+ *
+ * @param ev
+ * Electric vehicle to charge
+ * @param schedule
+ * Schedule for this car
+ * @param lastSchedulingTick
+ * Last tick, that a schedule has been processed
+ * @param currentTick
+ * Current time in Simulation
+ * @return
+ * The charged EV
+ */
+ private def chargeEv(
+ ev: EvModelWrapper,
+ schedule: ChargingSchedule,
+ lastSchedulingTick: Long,
+ currentTick: Long,
+ ): EvModelWrapper = {
+ /* Determine charged energy in the charging interval */
+ val chargedEnergySinceLastScheduling =
+ schedule.toSeq
+ .filter { case ScheduleEntry(tickStart, tickStop, _) =>
+ /* Filter for entries, that end after the last schedule application (that slice is not yet fully applied)
+ * and that start before the current tick */
+ tickStop > lastSchedulingTick && tickStart < currentTick
+ }
+ .sortBy(_.tickStart)
+ .foldLeft(KilowattHours(0d)) {
+ case (accumulatedEnergy, scheduleEntry) =>
+ /* Only the timeframe from the start of last scheduling update and current tick must be considered */
+ val trimmedEntry = trimScheduleEntry(
+ scheduleEntry,
+ lastSchedulingTick,
+ currentTick,
+ )
+
+ /* Determine the energy charged within this slice of the schedule and accumulate it */
+ accumulatedEnergy + calcChargedEnergy(trimmedEntry)
+ }
+ /* Update EV with the charged energy during the charging interval */
+ ev.copy(
+ storedEnergy = ev.storedEnergy + chargedEnergySinceLastScheduling
+ )
+ }
+
+ /** Create [[EvResult]]s and [[EvcsResult]]s for all EVs that have been
+ * connected to this charging station for some time in the time interval from
+ * (and including) the last tick to (but excluding) the current tick.
+ * Schedule entries that start at current tick are excluded, but will be
+ * considered in the next interval.
+ *
+ * As an exception to the rule, for EVs that are departing at current tick,
+ * an [[EvResult]] with 0 kW starting at current tick is created.
+ * @param lastState
+ * The last EVCS state
+ * @param currentTick
+ * The current tick
+ * @param voltageMagnitude
+ * The voltage magnitude used for reactive power calculation
+ * @return
+ * EV and EVCS results
+ */
+ def createResults(
+ lastState: EvcsState,
+ currentTick: Long,
+ voltageMagnitude: Dimensionless,
+ ): (Iterable[EvResult], Iterable[EvcsResult]) = {
+
+ val lastTick = lastState.tick
+
+ val lastEvMap = lastState.evs.map(ev => ev.uuid -> ev).toMap
+
+ val prefilteredSchedules = lastState.schedule.view.mapValues {
+ _.filter { case ScheduleEntry(tickStart, tickStop, _) =>
+ /* Filter for entries, that end after the last schedule application
+ and that start before the current tick.
+ Entries that end at lastTick are not included because schedule
+ intervals are open at the right hand side.
+ Entries that start at currentTick are not included because these
+ will be calculated with the next state.
+ */
+ tickStop > lastTick && tickStart < currentTick
+ }
+ }
+
+ val entriesByStartTick = prefilteredSchedules.toSeq
+ .flatMap { case (evUuid, entries) =>
+ // unsorted for speedier execution
+ entries.unsorted.map { entry =>
+ // trim down entries to the currently
+ // considered window of the charging schedule
+ evUuid -> trimScheduleEntry(
+ entry,
+ lastTick,
+ currentTick,
+ )
+ }
+ }
+ .groupBy { case (_, entry) =>
+ entry.tickStart
+ }
+ .to(SortedMap)
+
+ val startAndStopTicks = prefilteredSchedules.values
+ .flatMap {
+ _.unsorted.flatMap { case ScheduleEntry(start, stop, _) =>
+ Iterable(start, stop)
+ }
+ }
+ .filter(tick => tick >= lastTick && tick < currentTick)
+ .to(SortedSet)
+ // the last tick needs to be present
+ .incl(lastTick)
+
+ // in order to create 0kW entries for EVs that do not
+ // start charging right away at lastTick, create mock
+ // schedule entries that end before lastTick
+ val startingSchedules = lastEvMap.keys.map {
+ _ -> ScheduleEntry(lastTick, lastTick, Kilowatts(0d))
+ }
+
+ val (currentEvs, currentSchedules, evResults, evcsResults) =
+ startAndStopTicks.foldLeft(
+ lastEvMap,
+ startingSchedules,
+ Seq.empty[EvResult],
+ Seq.empty[EvcsResult],
+ ) { case ((evMap, lastActiveEntries, evResults, evcsResults), tick) =>
+ val time = tick.toDateTime(simulationStartDate)
+
+ // separate into those entries that are still active
+ // and those that have ended before or at tick
+ val (stillActive, endedEntries) = lastActiveEntries.partition {
+ case (_, entry) =>
+ entry.tickStop > tick
+ }
+ // entries that become active with tick
+ val newActiveEntries =
+ entriesByStartTick.getOrElse(tick, Iterable.empty).toMap
+
+ // for those entries that ended with tick and that
+ // do not have a directly connected entry after that,
+ // add 0 kW entries
+ val noChargingEvResults =
+ endedEntries
+ .filterNot { case evUuid -> _ =>
+ newActiveEntries.contains(evUuid)
+ }
+ .map { case evUuid -> _ =>
+ val ev = evMap(evUuid)
+
+ createEvResult(
+ ev,
+ tick,
+ Kilowatts(0d),
+ voltageMagnitude,
+ )
+ }
+
+ // create result and update EVs with the
+ // newly active entries
+ val (updatedEvMap, chargingEvResults) =
+ newActiveEntries.foldLeft(evMap, Seq.empty[EvResult]) {
+ case ((evMap, results), evUuid -> entry) =>
+ val ev = evMap(evUuid)
+
+ val result = createEvResult(
+ ev,
+ entry.tickStart,
+ entry.chargingPower,
+ voltageMagnitude,
+ )
+
+ // update EV
+ val newEvStoredEnergy = ev.storedEnergy +
+ calcChargedEnergy(entry)
+ val newEv = ev.copy(storedEnergy = newEvStoredEnergy)
+
+ (
+ evMap.updated(evUuid, newEv),
+ results.appended(result),
+ )
+ }
+
+ val currentActiveEntries = stillActive ++ newActiveEntries
+
+ // create the EVCS result with all currently active entries
+ val evcsP = currentActiveEntries.foldLeft(Kilowatts(0d)) {
+ case (powerSum, _ -> entry) =>
+ powerSum + entry.chargingPower
+ }
+ val evcsQ = calculateReactivePower(
+ evcsP,
+ voltageMagnitude,
+ )
+ val evcsResult = new EvcsResult(
+ time,
+ uuid,
+ evcsP.toMegawatts.asMegaWatt,
+ evcsQ.toMegavars.asMegaVar,
+ )
+
+ (
+ updatedEvMap,
+ currentActiveEntries,
+ evResults ++ chargingEvResults ++ noChargingEvResults,
+ evcsResults :+ evcsResult,
+ )
+ }
+
+ // special case: also add EVs that are departing at current tick
+ // because they won't be included when the next results are created
+ val departingEvResults = currentSchedules
+ .map { case evUuid -> _ =>
+ currentEvs(evUuid)
+ }
+ .filter {
+ // only take those that are departing now
+ _.departureTick == currentTick
+ }
+ .map {
+ createEvResult(
+ _,
+ currentTick,
+ Kilowatts(0d),
+ voltageMagnitude,
+ )
+ }
+
+ (evResults ++ departingEvResults, evcsResults)
+ }
+
+ private def createEvResult(
+ ev: EvModelWrapper,
+ tick: Long,
+ p: Power,
+ voltageMagnitude: Dimensionless,
+ ) = {
+ val q = calculateReactivePower(
+ p,
+ voltageMagnitude,
+ )
+ val soc = (ev.storedEnergy / ev.eStorage).asPu
+ .to(PERCENT)
+
+ new EvResult(
+ tick.toDateTime(simulationStartDate),
+ ev.uuid,
+ p.toMegawatts.asMegaWatt,
+ q.toMegavars.asMegaVar,
+ soc,
+ )
+ }
+
+ /** Limit the actual charging window. The beginning is determined by the
+ * latest tick of either the schedule start or the last scheduled tick. The
+ * end is determined by the earlier tick of either the schedule end or the
+ * current tick.
+ *
+ * @param scheduleEntry
+ * Section of a scheduled
+ * @param lastSchedulingTick
+ * The last scheduling tick
+ * @param currentTick
+ * The current tick
+ * @return
+ * A trimmed version of given scheduleEntry
+ */
+ private def trimScheduleEntry(
+ scheduleEntry: ScheduleEntry,
+ lastSchedulingTick: Long,
+ currentTick: Long,
+ ): ScheduleEntry =
+ scheduleEntry.copy(
+ tickStart = math.max(scheduleEntry.tickStart, lastSchedulingTick),
+ tickStop = math.min(scheduleEntry.tickStop, currentTick),
+ )
+
+ /** Determine the energy, that has been charged during the schedule entry time
+ * interval.
+ *
+ * @param scheduleEntry
+ * The schedule entry
+ * @return
+ * The energy charged during the time interval of the schedule entry
+ */
+ private def calcChargedEnergy(
+ scheduleEntry: ScheduleEntry
+ ): Energy =
+ scheduleEntry.chargingPower * Seconds(
+ scheduleEntry.tickStop - scheduleEntry.tickStart
+ )
+
+ /** Returns the maximum available charging power for an EV, which depends on
+ * ev and charging station limits for AC and DC current
+ *
+ * @param ev
+ * ev for which the max charging power should be returned
+ * @return
+ * maximum charging power for the EV at this charging station
+ */
+ def getMaxAvailableChargingPower(
+ ev: EvModelWrapper
+ ): Power = {
+ val evPower = currentType match {
+ case ElectricCurrentType.AC =>
+ ev.sRatedAc
+ case ElectricCurrentType.DC =>
+ ev.sRatedDc
+ }
+ /* Limit the charging power to the minimum of ev's and evcs' permissible power */
+ evPower.min(sRated)
+ }
+
+ override def calculatePower(
+ tick: Long,
+ voltage: Dimensionless,
+ modelState: EvcsState,
+ data: EvcsRelevantData,
+ ): ApparentPower =
+ throw new NotImplementedError("Use calculatePowerAndEvSoc() instead.")
+
+ override protected def calculateActivePower(
+ modelState: EvcsState,
+ data: EvcsRelevantData,
+ ): Power =
+ throw new NotImplementedError("Use calculatePowerAndEvSoc() instead.")
+
+ override def determineFlexOptions(
+ data: EvcsRelevantData,
+ lastState: EvcsState,
+ ): FlexibilityMessage.ProvideFlexOptions = {
+
+ val currentEvs = determineCurrentEvs(data, lastState)
+
+ val preferredScheduling = calculateNewScheduling(data, currentEvs)
+
+ val (maxCharging, preferredPower, forcedCharging, maxDischarging) =
+ currentEvs.foldLeft(
+ (Kilowatts(0d), Kilowatts(0d), Kilowatts(0d), Kilowatts(0d))
+ ) {
+ case (
+ (chargingSum, preferredSum, forcedSum, dischargingSum),
+ ev,
+ ) =>
+ val maxPower = getMaxAvailableChargingPower(ev)
+
+ val preferred = preferredScheduling
+ .get(ev.uuid)
+ .flatMap {
+ _.find { case ScheduleEntry(tickStart, tickStop, _) =>
+ tickStart <= data.tick && tickStop > data.tick
+ }.map(_.chargingPower)
+ }
+
+ val maxCharging =
+ if (!isFull(ev))
+ maxPower
+ else
+ Kilowatts(0d)
+
+ val forced =
+ if (isEmpty(ev) && !isInLowerMargin(ev))
+ preferred.getOrElse(maxPower)
+ else
+ Kilowatts(0d)
+
+ val maxDischarging =
+ if (!isEmpty(ev) && vehicle2grid)
+ maxPower * -1
+ else
+ Kilowatts(0d)
+
+ (
+ chargingSum + maxCharging,
+ preferredSum + preferred.getOrElse(Kilowatts(0d)),
+ forcedSum + forced,
+ dischargingSum + maxDischarging,
+ )
+ }
+
+ // if we need to charge at least one EV, we cannot discharge any other
+ val (adaptedMaxDischarging, adaptedPreferred) =
+ if (forcedCharging > Kilowatts(0d))
+ (forcedCharging, preferredPower.max(forcedCharging))
+ else
+ (maxDischarging, preferredPower)
+
+ ProvideMinMaxFlexOptions(
+ uuid,
+ adaptedPreferred,
+ adaptedMaxDischarging,
+ maxCharging,
+ )
+ }
+
+ // TODO less activations could be possible if after departure of vehicles, the additional power might be added to remaining non-full vehicles
+ // (minor) TODO? if IssueNoControl is sent, there might be a different result than anticipated when calculating flex options (strat is not used)
+ override def handleControlledPowerChange(
+ data: EvcsRelevantData,
+ lastState: EvcsState,
+ setPower: Power,
+ ): (EvcsState, FlexChangeIndicator) = {
+ val currentEvs = determineCurrentEvs(data, lastState)
+
+ if (setPower == Kilowatts(0d))
+ return (
+ EvcsState(
+ evs = currentEvs,
+ schedule = Map.empty,
+ tick = data.tick,
+ ),
+ FlexChangeIndicator(),
+ )
+
+ // applicable evs can be charged/discharged, other evs cannot
+ val applicableEvs = currentEvs.filter { ev =>
+ if (setPower > Kilowatts(0d))
+ !isFull(ev)
+ else
+ !isEmpty(ev)
+ }
+
+ val (forcedChargingEvs, regularChargingEvs) =
+ if (setPower > Kilowatts(0d))
+ // lower margin is excluded since charging is not required here anymore
+ applicableEvs.partition { ev =>
+ isEmpty(ev) && !isInLowerMargin(ev)
+ }
+ else
+ (Seq.empty, applicableEvs)
+
+ val (forcedSchedules, remainingPower) =
+ createScheduleWithSetPower(data.tick, forcedChargingEvs, setPower)
+
+ val (regularSchedules, _) =
+ createScheduleWithSetPower(data.tick, regularChargingEvs, remainingPower)
+
+ val combinedSchedules = forcedSchedules ++ regularSchedules
+
+ val allSchedules = combinedSchedules.map { case (ev, (schedule, _, _)) =>
+ ev -> schedule
+ }.toMap
+
+ (
+ EvcsState(
+ evs = currentEvs,
+ schedule = allSchedules,
+ tick = data.tick,
+ ),
+ aggregateFlexChange(combinedSchedules),
+ )
+ }
+
+ /** Aggregates a flex change indicator from controlled schedule calcuation
+ * result
+ *
+ * @param combinedSchedules
+ * The schedule calculation results
+ * @return
+ * The aggregated flex change indicator
+ */
+ private def aggregateFlexChange(
+ combinedSchedules: Seq[(UUID, (ChargingSchedule, Long, Boolean))]
+ ): FlexChangeIndicator = {
+ val schedulesOnly =
+ combinedSchedules.map { case (_, schedule) => schedule }
+
+ val scheduleAtNextActivation = schedulesOnly
+ .map { case (_, _, scheduleAtNext) => scheduleAtNext }
+ .reduceOption(_ || _)
+ .getOrElse(false)
+
+ val nextScheduledTick = schedulesOnly.map { case (_, endTick, _) =>
+ endTick
+ }.minOption
+
+ FlexChangeIndicator(
+ scheduleAtNextActivation,
+ nextScheduledTick,
+ )
+ }
+
+ /** Distributes some set power value across given EVs, taking into
+ * consideration the maximum charging power of EVs and the charging station
+ *
+ * @param currentTick
+ * The current tick
+ * @param evs
+ * The collection of EVs to assign charging power to
+ * @param setPower
+ * The remaining power to assign to given EVs
+ * @return
+ * A set of EV model and possibly charging schedule and activation
+ * indicators, as well as the remaining power that could not be assigned to
+ * given EVs
+ */
+ private def createScheduleWithSetPower(
+ currentTick: Long,
+ evs: Seq[EvModelWrapper],
+ setPower: Power,
+ ): (
+ Seq[(UUID, (ChargingSchedule, Long, Boolean))],
+ Power,
+ ) = {
+
+ if (evs.isEmpty) return (Seq.empty, setPower)
+
+ if (setPower.~=(Kilowatts(0d))(Kilowatts(1e-6))) {
+ // No power left. Rest is not charging
+ return (Seq.empty, Kilowatts(0d))
+ }
+
+ val proposedPower = setPower.divide(evs.size)
+
+ val (exceedingPowerEvs, fittingPowerEvs) = evs.partition { ev =>
+ if (setPower > Kilowatts(0d))
+ proposedPower > getMaxAvailableChargingPower(ev)
+ else
+ proposedPower < (getMaxAvailableChargingPower(ev) * -1)
+ }
+
+ if (exceedingPowerEvs.isEmpty) {
+ // end of recursion, rest of charging power fits to all
+
+ val results = fittingPowerEvs.map { ev =>
+ val chargingTicks = calcFlexOptionsChange(ev, proposedPower)
+ val endTick = Math.min(currentTick + chargingTicks, ev.departureTick)
+
+ (
+ ev.uuid,
+ (
+ SortedSet(
+ ScheduleEntry(currentTick, endTick, proposedPower)
+ ),
+ endTick,
+ isFull(ev) || isEmpty(ev) || isInLowerMargin(ev),
+ ),
+ )
+ }
+
+ (results, Kilowatts(0d))
+ } else {
+ // not all evs can be charged with proposed power
+
+ // charge all exceeded evs with their respective maximum power
+ val maxCharged = exceedingPowerEvs.map { ev =>
+ val maxPower = getMaxAvailableChargingPower(ev)
+ val power =
+ if (setPower > Kilowatts(0d))
+ maxPower
+ else
+ maxPower * (-1)
+
+ val chargingTicks = calcFlexOptionsChange(ev, power)
+ val endTick = Math.min(currentTick + chargingTicks, ev.departureTick)
+
+ (ev, power, endTick)
+ }
+
+ val maxChargedResults = maxCharged.map { case (ev, power, endTick) =>
+ (
+ ev.uuid,
+ (
+ SortedSet(ScheduleEntry(currentTick, endTick, power)),
+ endTick,
+ isFull(ev) || isEmpty(ev) || isInLowerMargin(ev),
+ ),
+ )
+ }
+
+ // sum up allocated power
+ val chargingPowerSum = maxCharged.foldLeft(Kilowatts(0d)) {
+ case (powerSum, (_, chargingPower, _)) =>
+ powerSum + chargingPower
+ }
+
+ val remainingAfterAllocation = setPower - chargingPowerSum
+
+ // go into the next recursion step with the remaining power
+ val (nextIterationResults, remainingAfterRecursion) =
+ createScheduleWithSetPower(
+ currentTick,
+ fittingPowerEvs,
+ remainingAfterAllocation,
+ )
+
+ val combinedResults = maxChargedResults ++ nextIterationResults
+
+ (combinedResults, remainingAfterRecursion)
+ }
+
+ }
+
+ /** Calculates the duration (in ticks) until the flex options will change
+ * next, which could be the battery being fully charged or discharged or the
+ * minimum SOC requirement being reached
+ *
+ * @param ev
+ * The EV to charge/discharge
+ * @param power
+ * The charging/discharging power
+ * @return
+ * The tick at which flex options will change
+ */
+ private def calcFlexOptionsChange(
+ ev: EvModelWrapper,
+ power: Power,
+ ): Long = {
+ val timeUntilFullOrEmpty =
+ if (power > Kilowatts(0d)) {
+
+ // if we're below lowest SOC, flex options will change at that point
+ val targetEnergy =
+ if (isEmpty(ev) && !isInLowerMargin(ev))
+ ev.eStorage * lowestEvSoc
+ else
+ ev.eStorage
+
+ (targetEnergy - ev.storedEnergy) / power
+ } else
+ (ev.storedEnergy - (ev.eStorage * lowestEvSoc)) / (power * (-1))
+
+ Math.round(timeUntilFullOrEmpty.toSeconds)
+ }
+
+ /** @param ev
+ * the ev whose stored energy is to be checked
+ * @return
+ * whether the given ev's stored energy is greater than the maximum charged
+ * energy allowed (minus a tolerance margin)
+ */
+ private def isFull(ev: EvModelWrapper): Boolean =
+ ev.storedEnergy >= (ev.eStorage - calcToleranceMargin(ev))
+
+ /** @param ev
+ * the ev whose stored energy is to be checked
+ * @return
+ * whether the given ev's stored energy is less than the minimal charged
+ * energy allowed (plus a tolerance margin)
+ */
+ private def isEmpty(ev: EvModelWrapper): Boolean =
+ ev.storedEnergy <= (
+ ev.eStorage * lowestEvSoc + calcToleranceMargin(ev)
+ )
+
+ /** @param ev
+ * the ev whose stored energy is to be checked
+ * @return
+ * whether the given ev's stored energy is within +- tolerance of the
+ * minimal charged energy allowed
+ */
+ private def isInLowerMargin(ev: EvModelWrapper): Boolean = {
+ val toleranceMargin = calcToleranceMargin(ev)
+ val lowestSoc = ev.eStorage * lowestEvSoc
+
+ ev.storedEnergy <= (
+ lowestSoc + toleranceMargin
+ ) && ev.storedEnergy >= (
+ lowestSoc - toleranceMargin
+ )
+ }
+
+ private def calcToleranceMargin(ev: EvModelWrapper): Energy =
+ getMaxAvailableChargingPower(ev) * Seconds(1)
+
+ /** Determines the current state of staying and arriving EVs.
+ *
+ * @param data
+ * the EvcsRelevantData containing arriving EVs, the current tick etc.
+ * @param lastState
+ * the last known state of the EVCS. Could be the state at the current
+ * tick.
+ * @return
+ * The EVs currently parked at the EVCS, including the arriving EVs
+ */
+ def determineCurrentEvs(
+ data: EvcsRelevantData,
+ lastState: EvcsState,
+ ): Seq[EvModelWrapper] = {
+
+ // If last state is outdated, determine
+ // current state for already parked EVs
+ val currentEVs =
+ if (lastState.tick < data.tick)
+ applySchedule(lastState, data.tick)
+ else
+ lastState.evs
+
+ validateArrivals(
+ lastState.evs,
+ data.arrivals,
+ chargingPoints,
+ )
+
+ currentEVs ++ data.arrivals
+ }
+
+ /** Checks whether requested departing EVs are consistent with currently
+ * connected EVs. Only logs warnings, does not throw exceptions.
+ *
+ * @param lastEvs
+ * EVs of the last tick
+ * @param departures
+ * Departing EVs at the current tick
+ */
+ def validateDepartures(
+ lastEvs: Seq[EvModelWrapper],
+ departures: Seq[UUID],
+ ): Unit = {
+ departures.foreach { ev =>
+ if (!lastEvs.exists(_.uuid == ev))
+ logger.warn(
+ s"EV $ev should depart from this station (according to external simulation), but has not been parked here."
+ )
+ }
+
+ }
+
+ /** Checks whether provided arriving EVs are consistent with charging station
+ * specifications and currently connected EVs. Only logs warnings, does not
+ * throw exceptions.
+ *
+ * @param lastEvs
+ * EVs of the last tick
+ * @param arrivals
+ * Arriving EVs at the current tick
+ * @param chargingPoints
+ * max number of charging points available at this CS
+ */
+ def validateArrivals(
+ lastEvs: Seq[EvModelWrapper],
+ arrivals: Seq[EvModelWrapper],
+ chargingPoints: Int,
+ ): Unit = {
+
+ arrivals.foreach { ev =>
+ if (lastEvs.exists(_.uuid == ev.uuid))
+ logger.warn(
+ s"EV ${ev.id} should arrive at this station (according to external simulation), but is already parked here."
+ )
+ }
+
+ val newCount = lastEvs.size +
+ arrivals.count { ev =>
+ !lastEvs.exists(_.uuid == ev.uuid)
+ }
+
+ if (newCount > chargingPoints)
+ logger.warn(
+ "More EVs are parking at this station than physically possible."
+ )
+ }
+}
+
+object EvcsModel {
+
+ /** A charging schedule for a single EV, consisting of multiple schedule
+ * entries that are (primarily) sorted by start tick
+ */
+ type ChargingSchedule = SortedSet[ScheduleEntry]
+
+ /** A schedule map consisting of charging schedules for multiple EVs,
+ * referenced by their model UUID
+ */
+ type ScheduleMap = Map[UUID, ChargingSchedule]
+
+ /** Class that holds all relevant data for an EVCS model calculation
+ *
+ * @param tick
+ * The current tick
+ * @param arrivals
+ * The evs arriving at the current tick
+ */
+ final case class EvcsRelevantData(
+ tick: Long,
+ arrivals: Seq[EvModelWrapper],
+ ) extends CalcRelevantData
+
+ /** Class that represents the state of the charging station (including
+ * schedules for future charging) at a given point in time
+ *
+ * @param evs
+ * EVs that are parked at the charging station
+ * @param schedule
+ * The schedule determining when to load which EVs with which power, as a
+ * map EV model UUID -> charging schedule
+ * @param tick
+ * The tick that the data has been calculated for
+ */
+ final case class EvcsState(
+ evs: Seq[EvModelWrapper],
+ schedule: ScheduleMap,
+ tick: Long,
+ ) extends ModelState
+
+ /** Schedule entry specifying a time interval in which the EV should be
+ * charged/discharged with some given power
+ *
+ * @param tickStart
+ * start of charging interval
+ * @param tickStop
+ * end of charging interval
+ * @param chargingPower
+ * charging power for the charging interval
+ */
+ final case class ScheduleEntry(
+ tickStart: Long,
+ tickStop: Long,
+ chargingPower: squants.Power,
+ ) extends Ordered[ScheduleEntry] {
+ override def compare(that: ScheduleEntry): Int = {
+ val startComp = tickStart.compare(that.tickStart)
+ if (startComp != 0)
+ startComp
+ else {
+ // important for checking equality: consider other fields as well
+ val stopComp = tickStop.compare(that.tickStop)
+ if (stopComp != 0)
+ stopComp
+ else
+ chargingPower.compareTo(that.chargingPower)
+ }
+ }
+ }
+
+ /** Default factory method to create an EvcsModel instance.
+ *
+ * @param inputModel
+ * The EVCS input model providing parameters
+ * @param scalingFactor
+ * The scaling factor of the power output
+ * @param simulationStartDate
+ * The start date of the simulation
+ * @param simulationEndDate
+ * The end date of the simulation
+ * @param chargingStrategy
+ * The charging strategy to use
+ * @param lowestEvSoc
+ * The lowest SOC possible for EV batteries (inverse of max dod)
+ * @return
+ * The enabled EvcsModel
+ */
+ def apply(
+ inputModel: EvcsInput,
+ scalingFactor: Double,
+ simulationStartDate: ZonedDateTime,
+ simulationEndDate: ZonedDateTime,
+ chargingStrategy: String,
+ lowestEvSoc: Double,
+ ): EvcsModel = {
+ /* Determine the operation interval */
+ val operationInterval: OperationInterval =
+ SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ inputModel.getOperationTime,
+ )
+
+ val model = EvcsModel(
+ inputModel.getUuid,
+ inputModel.getId,
+ operationInterval,
+ scalingFactor,
+ simulationStartDate,
+ QControl(inputModel.getqCharacteristics),
+ Kilowatts(inputModel.getType.getsRated.to(KILOWATT).getValue.doubleValue),
+ inputModel.getType.getElectricCurrentType,
+ inputModel.getCosPhiRated,
+ inputModel.getChargingPoints,
+ inputModel.getLocationType,
+ inputModel.getV2gSupport,
+ ChargingStrategy(chargingStrategy),
+ lowestEvSoc,
+ )
+
+ model.enable()
+
+ model
+ }
+
+}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/ConstantPowerCharging.scala b/src/main/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/ConstantPowerCharging.scala
new file mode 100644
index 0000000000..20106c77af
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/ConstantPowerCharging.scala
@@ -0,0 +1,55 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs.uncontrolled
+
+import edu.ie3.simona.model.participant.evcs.EvcsModel.{
+ ScheduleMap,
+ ScheduleEntry,
+}
+import edu.ie3.simona.model.participant.evcs.{EvModelWrapper, EvcsModel}
+import squants.Seconds
+
+import scala.collection.immutable.SortedSet
+
+trait ConstantPowerCharging {
+ this: EvcsModel =>
+
+ /** Determine scheduling for charging the EVs currently parked at the charging
+ * station until their departure. In this case, each EV is charged with
+ * constant power from current time until departure. If less than the maximum
+ * power is required to reach 100% SoC, the power is reduced accordingly.
+ *
+ * @param currentTick
+ * current tick
+ * @param evs
+ * currently parked evs at the charging station
+ * @return
+ * scheduling for charging the EVs
+ */
+ def chargeWithConstantPower(
+ currentTick: Long,
+ evs: Seq[EvModelWrapper],
+ ): ScheduleMap = evs
+ .filter(ev => ev.storedEnergy < ev.eStorage)
+ .map { ev =>
+ val maxChargingPower = getMaxAvailableChargingPower(ev)
+ val remainingParkingTime = Seconds(ev.departureTick - currentTick)
+
+ val requiredEnergyUntilFull = ev.eStorage - ev.storedEnergy
+ val maxChargedEnergyUntilDeparture =
+ maxChargingPower * remainingParkingTime
+ val actualChargedEnergy =
+ requiredEnergyUntilFull.min(maxChargedEnergyUntilDeparture)
+
+ val chargingPower = actualChargedEnergy / remainingParkingTime
+
+ ev.uuid -> SortedSet(
+ ScheduleEntry(currentTick, ev.departureTick, chargingPower)
+ )
+ }
+ .toMap
+}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/MaximumPowerCharging.scala b/src/main/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/MaximumPowerCharging.scala
new file mode 100644
index 0000000000..eac55d9fcc
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/MaximumPowerCharging.scala
@@ -0,0 +1,61 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs.uncontrolled
+
+import edu.ie3.simona.model.participant.evcs.EvcsModel.{
+ ScheduleMap,
+ ScheduleEntry,
+}
+import edu.ie3.simona.model.participant.evcs.{EvModelWrapper, EvcsModel}
+import squants.Seconds
+
+import scala.collection.immutable.SortedSet
+
+trait MaximumPowerCharging {
+ this: EvcsModel =>
+
+ /** Determine scheduling for charging the EVs currently parked at the charging
+ * station until their departure. In this case, each EV is charged with
+ * maximum power from current time until it reaches either 100% SoC or its
+ * departure time.
+ *
+ * @param currentTick
+ * current tick
+ * @param evs
+ * currently parked evs at the charging station
+ * @return
+ * scheduling for charging the EVs
+ */
+ def chargeWithMaximumPower(
+ currentTick: Long,
+ evs: Seq[EvModelWrapper],
+ ): ScheduleMap = evs
+ .filter(ev => ev.storedEnergy < ev.eStorage)
+ .map { ev =>
+ val chargingPower = getMaxAvailableChargingPower(ev)
+ val remainingParkingTime =
+ Seconds(ev.departureTick - currentTick)
+
+ val possibleChargeableEnergyUntilDeparture =
+ chargingPower * remainingParkingTime
+
+ val endTick: Long =
+ if (
+ ev.storedEnergy + possibleChargeableEnergyUntilDeparture <= ev.eStorage
+ ) {
+ /* Charge with full power, if battery can accommodate the energy */
+ ev.departureTick
+ } else {
+ /* Charge only until the car is full */
+ ((ev.eStorage - ev.storedEnergy) / chargingPower).toSeconds.toLong + currentTick
+ }
+
+ ev.uuid ->
+ SortedSet(ScheduleEntry(currentTick, endTick, chargingPower))
+ }
+ .toMap
+}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/FixedLoadModel.scala b/src/main/scala/edu/ie3/simona/model/participant/load/FixedLoadModel.scala
index 7ad8f0f339..b74e154b61 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/FixedLoadModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/FixedLoadModel.scala
@@ -8,11 +8,12 @@ package edu.ie3.simona.model.participant.load
import edu.ie3.datamodel.models.input.system.LoadInput
import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.participant.load.FixedLoadModel.FixedLoadRelevantData
import edu.ie3.simona.model.participant.load.LoadReference.{
ActivePower,
- EnergyConsumption
+ EnergyConsumption,
}
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
@@ -45,11 +46,11 @@ final case class FixedLoadModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
- reference: LoadReference
+ reference: LoadReference,
) extends LoadModel[FixedLoadRelevantData.type](
uuid,
id,
@@ -57,7 +58,7 @@ final case class FixedLoadModel(
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
) {
val activePower: Power = reference match {
@@ -76,8 +77,9 @@ final case class FixedLoadModel(
* Active power
*/
override protected def calculateActivePower(
- data: FixedLoadRelevantData.type = FixedLoadRelevantData
- ): Power = activePower * scalingFactor
+ modelState: ConstantState.type,
+ data: FixedLoadRelevantData.type = FixedLoadRelevantData,
+ ): Power = activePower
}
object FixedLoadModel {
@@ -85,22 +87,26 @@ object FixedLoadModel {
def apply(
input: LoadInput,
- operationInterval: OperationInterval,
scalingFactor: Double,
- reference: LoadReference
- ): FixedLoadModel = FixedLoadModel(
- input.getUuid,
- input.getId,
- operationInterval,
- scalingFactor,
- QControl(input.getqCharacteristics()),
- Kilowatts(
- input.getsRated
- .to(PowerSystemUnits.KILOWATT)
- .getValue
- .doubleValue
- ),
- input.getCosPhiRated,
- reference
- )
+ operationInterval: OperationInterval,
+ reference: LoadReference,
+ ): FixedLoadModel = {
+ val model = FixedLoadModel(
+ input.getUuid,
+ input.getId,
+ operationInterval,
+ scalingFactor,
+ QControl(input.getqCharacteristics()),
+ Kilowatts(
+ input.getsRated
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ input.getCosPhiRated,
+ reference,
+ )
+ model.enable()
+ model
+ }
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/LoadModel.scala b/src/main/scala/edu/ie3/simona/model/participant/load/LoadModel.scala
index 3db9c720f5..874b5a5038 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/LoadModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/LoadModel.scala
@@ -10,11 +10,15 @@ import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.models.input.system.LoadInput
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.participant.{
ApparentPowerParticipant,
- SystemParticipant
+ FlexChangeIndicator,
+ SystemParticipant,
}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
import squants.energy.Megawatts
@@ -34,17 +38,34 @@ abstract class LoadModel[D <: LoadRelevantData](
scalingFactor: Double,
qControl: QControl,
sRated: Power,
- cosPhiRated: Double
-) extends SystemParticipant[D, ApparentPower](
+ cosPhiRated: Double,
+) extends SystemParticipant[D, ApparentPower, ConstantState.type](
uuid,
id,
operationInterval,
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
)
- with ApparentPowerParticipant[D]
+ with ApparentPowerParticipant[D, ConstantState.type] {
+
+ override def determineFlexOptions(
+ data: D,
+ lastState: ConstantState.type,
+ ): ProvideFlexOptions =
+ ProvideMinMaxFlexOptions.noFlexOption(
+ uuid,
+ calculateActivePower(lastState, data),
+ )
+
+ override def handleControlledPowerChange(
+ data: D,
+ lastState: ConstantState.type,
+ setPower: Power,
+ ): (ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
+}
case object LoadModel extends LazyLogging {
@@ -69,7 +90,7 @@ case object LoadModel extends LazyLogging {
def scaleSRatedActivePower(
inputModel: LoadInput,
activePower: Power,
- safetyFactor: Double = 1d
+ safetyFactor: Double = 1d,
): Power = {
val sRated = Megawatts(
inputModel.getsRated
@@ -87,7 +108,7 @@ case object LoadModel extends LazyLogging {
*
* When the load is scaled based on the consumed energy per year, the
* installed sRated capacity is not usable anymore instead, the load's rated
- * apparent power ist scaled on the maximum power occurring in the specified
+ * apparent power is scaled on the maximum power occurring in the specified
* load profile multiplied by the ratio of the annual consumption and the
* standard load profile scale
*
@@ -112,7 +133,7 @@ case object LoadModel extends LazyLogging {
energyConsumption: Energy,
profileMaxPower: Power,
profileEnergyScaling: Energy,
- safetyFactor: Double = 1d
+ safetyFactor: Double = 1d,
): Power = {
(profileMaxPower / inputModel.getCosPhiRated) * (
energyConsumption / profileEnergyScaling
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/LoadReference.scala b/src/main/scala/edu/ie3/simona/model/participant/load/LoadReference.scala
index b0c2877cf1..5a795673c5 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/LoadReference.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/LoadReference.scala
@@ -20,7 +20,7 @@ sealed trait LoadReference {
def getKey: String = key
}
-case object LoadReference {
+object LoadReference {
/** Scale the load model behaviour to reach the given active power in max
*
@@ -61,7 +61,7 @@ case object LoadReference {
*/
def apply(
inputModel: LoadInput,
- modelConfig: SimonaConfig.LoadRuntimeConfig
+ modelConfig: SimonaConfig.LoadRuntimeConfig,
): LoadReference =
StringUtils.cleanString(modelConfig.reference).toLowerCase match {
case "power" =>
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala
index cb3cb60e90..af77c3fa55 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileKey.scala
@@ -25,7 +25,7 @@ import edu.ie3.simona.model.participant.load.{DayType, profile}
final case class LoadProfileKey(
standardLoadProfile: StandardLoadProfile,
season: Season.Value,
- dayType: DayType.Value
+ dayType: DayType.Value,
)
case object LoadProfileKey {
@@ -64,19 +64,19 @@ case object LoadProfileKey {
def apply(
loadProfile: String,
season: String,
- dayType: String
+ dayType: String,
): LoadProfileKey = {
try {
new LoadProfileKey(
StandardLoadProfile.parse(loadProfile),
Season(season),
- DayType(dayType)
+ DayType(dayType),
)
} catch {
case e: ParsingException =>
throw new IllegalArgumentException(
s"Cannot parse '$loadProfile' to a now StandardLoadProfile.",
- e
+ e,
)
}
}
@@ -93,12 +93,12 @@ case object LoadProfileKey {
*/
def apply(
loadProfile: StandardLoadProfile,
- time: ZonedDateTime
+ time: ZonedDateTime,
): LoadProfileKey = {
new LoadProfileKey(
loadProfile,
profile.Season(time),
- load.DayType(time.getDayOfWeek)
+ load.DayType(time.getDayOfWeek),
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala
index 8c3769a9cf..f1e7d54f62 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/profile/LoadProfileStore.scala
@@ -6,24 +6,23 @@
package edu.ie3.simona.model.participant.load.profile
-import java.io.{InputStreamReader, Reader}
-import java.time.{Duration, ZonedDateTime}
-import java.util
-
import breeze.numerics.round
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.models.profile.{
BdewStandardLoadProfile,
- StandardLoadProfile
+ StandardLoadProfile,
}
import edu.ie3.simona.model.participant.load.profile.LoadProfileStore.{
initializeMaxConsumptionPerProfile,
- initializeTypeDayValues
+ initializeTypeDayValues,
}
import edu.ie3.simona.model.participant.load.{DayType, profile}
import org.apache.commons.csv.CSVFormat
import squants.energy.{KilowattHours, Watts}
+import java.io.{InputStreamReader, Reader}
+import java.time.{Duration, ZonedDateTime}
+import java.util
import scala.jdk.CollectionConverters._
import scala.math.pow
@@ -57,7 +56,7 @@ class LoadProfileStore private (val reader: Reader) {
*/
def entry(
time: ZonedDateTime,
- loadProfile: StandardLoadProfile
+ loadProfile: StandardLoadProfile,
): squants.Power = {
val key = LoadProfileKey(loadProfile, time)
profileMap.get(key) match {
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala b/src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala
index 77782e4907..8fd4128996 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/profile/ProfileLoadModel.scala
@@ -6,9 +6,10 @@
package edu.ie3.simona.model.participant.load.profile
-import edu.ie3.datamodel.models.profile.StandardLoadProfile
import edu.ie3.datamodel.models.input.system.LoadInput
+import edu.ie3.datamodel.models.profile.StandardLoadProfile
import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.participant.load.LoadReference._
import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel.ProfileRelevantData
@@ -44,12 +45,12 @@ final case class ProfileLoadModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
loadProfile: StandardLoadProfile,
- reference: LoadReference
+ reference: LoadReference,
) extends LoadModel[ProfileRelevantData](
uuid,
id,
@@ -57,7 +58,7 @@ final case class ProfileLoadModel(
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
) {
private val loadProfileStore: LoadProfileStore = LoadProfileStore()
@@ -84,7 +85,8 @@ final case class ProfileLoadModel(
* Active power
*/
override protected def calculateActivePower(
- data: ProfileRelevantData
+ modelState: ConstantState.type,
+ data: ProfileRelevantData,
): Power = {
/* The power comes in W and is delivered all 15 minutes */
val averagePower: Power = loadProfileStore
@@ -99,11 +101,11 @@ final case class ProfileLoadModel(
/* scale the profiles average power based on the energyConsumption/profileEnergyScaling(=1000kWh/year) ratio */
averagePower * energyReferenceScalingFactor
}
- activePower * scalingFactor
+ activePower
}
}
-case object ProfileLoadModel {
+object ProfileLoadModel {
final case class ProfileRelevantData(date: ZonedDateTime)
extends LoadRelevantData
@@ -112,43 +114,47 @@ case object ProfileLoadModel {
input: LoadInput,
operationInterval: OperationInterval,
scalingFactor: Double,
- reference: LoadReference
- ): ProfileLoadModel = reference match {
- case LoadReference.ActivePower(power) =>
- val sRatedPowerScaled = LoadModel.scaleSRatedActivePower(input, power)
- ProfileLoadModel(
- input.getUuid,
- input.getId,
- operationInterval,
- scalingFactor,
- QControl.apply(input.getqCharacteristics()),
- sRatedPowerScaled,
- input.getCosPhiRated,
- input.getLoadProfile.asInstanceOf[StandardLoadProfile],
- reference
- )
+ reference: LoadReference,
+ ): ProfileLoadModel = {
+ val model = reference match {
+ case LoadReference.ActivePower(power) =>
+ val sRatedPowerScaled = LoadModel.scaleSRatedActivePower(input, power)
+ ProfileLoadModel(
+ input.getUuid,
+ input.getId,
+ operationInterval,
+ scalingFactor,
+ QControl.apply(input.getqCharacteristics()),
+ sRatedPowerScaled,
+ input.getCosPhiRated,
+ input.getLoadProfile.asInstanceOf[StandardLoadProfile],
+ reference,
+ )
- case LoadReference.EnergyConsumption(energyConsumption) =>
- val loadProfileMax =
- LoadProfileStore().maxPower(
- input.getLoadProfile.asInstanceOf[StandardLoadProfile]
+ case LoadReference.EnergyConsumption(energyConsumption) =>
+ val loadProfileMax =
+ LoadProfileStore().maxPower(
+ input.getLoadProfile.asInstanceOf[StandardLoadProfile]
+ )
+ val sRatedEnergy = LoadModel.scaleSRatedEnergy(
+ input,
+ energyConsumption,
+ loadProfileMax,
+ LoadProfileStore.defaultLoadProfileEnergyScaling,
)
- val sRatedEnergy = LoadModel.scaleSRatedEnergy(
- input,
- energyConsumption,
- loadProfileMax,
- LoadProfileStore.defaultLoadProfileEnergyScaling
- )
- ProfileLoadModel(
- input.getUuid,
- input.getId,
- operationInterval,
- scalingFactor,
- QControl.apply(input.getqCharacteristics()),
- sRatedEnergy,
- input.getCosPhiRated,
- input.getLoadProfile.asInstanceOf[StandardLoadProfile],
- reference
- )
+ ProfileLoadModel(
+ input.getUuid,
+ input.getId,
+ operationInterval,
+ scalingFactor,
+ QControl.apply(input.getqCharacteristics()),
+ sRatedEnergy,
+ input.getCosPhiRated,
+ input.getLoadProfile.asInstanceOf[StandardLoadProfile],
+ reference,
+ )
+ }
+ model.enable()
+ model
}
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala b/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala
index 7d8db0c833..4897530a1e 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadModel.scala
@@ -10,6 +10,7 @@ import de.lmu.ifi.dbs.elki.math.statistics.distribution.GeneralizedExtremeValueD
import de.lmu.ifi.dbs.elki.utilities.random.RandomFactory
import edu.ie3.datamodel.models.input.system.LoadInput
import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.participant.load.LoadReference._
import edu.ie3.simona.model.participant.load.random.RandomLoadModel.RandomRelevantData
@@ -50,11 +51,11 @@ final case class RandomLoadModel(
uuid: UUID,
id: String,
operationInterval: OperationInterval,
- scalingFactor: Double,
+ override val scalingFactor: Double,
qControl: QControl,
sRated: Power,
cosPhiRated: Double,
- reference: LoadReference
+ reference: LoadReference,
) extends LoadModel[RandomRelevantData](
uuid,
id,
@@ -62,7 +63,7 @@ final case class RandomLoadModel(
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
) {
private lazy val energyReferenceScalingFactor = reference match {
@@ -89,14 +90,15 @@ final case class RandomLoadModel(
*/
@tailrec
override protected def calculateActivePower(
- data: RandomRelevantData
+ modelState: ConstantState.type,
+ data: RandomRelevantData,
): Power = {
val gev = getGevDistribution(data.date)
/* Get a next random power (in kW) */
val randomPower = gev.nextRandom()
if (randomPower < 0)
- calculateActivePower(data)
+ calculateActivePower(modelState, data)
else {
val profilePower = Kilowatts(randomPower)
val activePower = reference match {
@@ -109,7 +111,7 @@ final case class RandomLoadModel(
/* scale the profiles random power based on the energyConsumption/profileEnergyScaling(=1000kWh/year) ratio */
profilePower * energyReferenceScalingFactor
}
- activePower * scalingFactor
+ activePower
}
}
@@ -128,7 +130,7 @@ final case class RandomLoadModel(
* available, yet, instantiate one. */
val key: GevKey = (
DayType(dateTime.getDayOfWeek),
- TimeUtil.withDefaults.getQuarterHourOfDay(dateTime)
+ TimeUtil.withDefaults.getQuarterHourOfDay(dateTime),
)
gevStorage.get(key) match {
case Some(foundIt) => foundIt
@@ -140,7 +142,7 @@ final case class RandomLoadModel(
gevParameters.my,
gevParameters.sigma,
gevParameters.k,
- randomFactory
+ randomFactory,
)
gevStorage += (key -> newGev)
newGev
@@ -148,7 +150,7 @@ final case class RandomLoadModel(
}
}
-case object RandomLoadModel {
+object RandomLoadModel {
final case class RandomRelevantData(date: ZonedDateTime)
extends LoadRelevantData
@@ -178,9 +180,9 @@ case object RandomLoadModel {
input: LoadInput,
operationInterval: OperationInterval,
scalingFactor: Double,
- reference: LoadReference
+ reference: LoadReference,
): RandomLoadModel = {
- reference match {
+ val model = reference match {
case ActivePower(power) =>
val sRatedPowerScaled =
LoadModel.scaleSRatedActivePower(input, power, 1.1)
@@ -193,7 +195,7 @@ case object RandomLoadModel {
QControl.apply(input.getqCharacteristics()),
sRatedPowerScaled,
input.getCosPhiRated,
- reference
+ reference,
)
case EnergyConsumption(energyConsumption) =>
val sRatedEnergy = LoadModel.scaleSRatedEnergy(
@@ -201,7 +203,7 @@ case object RandomLoadModel {
energyConsumption,
randomMaxPower,
randomProfileEnergyScaling,
- 1.1
+ 1.1,
)
RandomLoadModel(
@@ -212,8 +214,10 @@ case object RandomLoadModel {
QControl.apply(input.getqCharacteristics()),
sRatedEnergy,
input.getCosPhiRated,
- reference
+ reference,
)
}
+ model.enable()
+ model
}
}
diff --git a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala b/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala
index 44f481eafe..36635ae940 100644
--- a/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala
+++ b/src/main/scala/edu/ie3/simona/model/participant/load/random/RandomLoadParamStore.scala
@@ -36,7 +36,7 @@ final case class RandomLoadParamStore private (reader: Reader) {
dayType,
throw new RuntimeException(
s"Cannot determine the random load parameters for '$time' (day type '$dayType')."
- )
+ ),
)
.getQuarterHourParameters(time)
}
@@ -73,6 +73,11 @@ case object RandomLoadParamStore extends LazyLogging {
def apply(reader: Reader): RandomLoadParamStore =
new RandomLoadParamStore(reader)
+ /** Returns a [[CSVFormat]] with the first line as its header
+ */
+ private def csvParser: CSVFormat =
+ CSVFormat.DEFAULT.builder().setHeader().setSkipHeaderRecord(true).build()
+
/** Initializes all type day values by receiving values from provided reader.
*
* @param reader
@@ -81,7 +86,7 @@ case object RandomLoadParamStore extends LazyLogging {
def initializeDayTypeValues(
reader: Reader
): Map[DayType.Value, TypeDayParameters] = {
- val parser = CSVFormat.DEFAULT.withFirstRecordAsHeader.parse(reader)
+ val parser = csvParser.parse(reader)
/* records list is an ArrayList */
val records = parser.getRecords
@@ -101,7 +106,7 @@ case object RandomLoadParamStore extends LazyLogging {
case e: FileIOException =>
throw new FileIOException(
s"Cannot determine random load parameters for day type '$dayType' and quarter hour '$quartHour'",
- e
+ e,
)
}
}
@@ -175,7 +180,7 @@ case object RandomLoadParamStore extends LazyLogging {
*/
private def assembleParameters(
record: CSVRecord,
- parameterToCol: Map[RandomLoadParameters.Value, Int]
+ parameterToCol: Map[RandomLoadParameters.Value, Int],
): RandomLoadParameters = {
val k = record
.get(
@@ -183,7 +188,7 @@ case object RandomLoadParamStore extends LazyLogging {
RandomLoadParameters.K,
throw new FileIOException(
s"Cannot determine column index for random load parameter ${RandomLoadParameters.K}."
- )
+ ),
)
)
.toDouble
@@ -193,7 +198,7 @@ case object RandomLoadParamStore extends LazyLogging {
RandomLoadParameters.MY,
throw new FileIOException(
s"Cannot determine column index for random load parameter ${RandomLoadParameters.MY}."
- )
+ ),
)
)
.toDouble
@@ -203,7 +208,7 @@ case object RandomLoadParamStore extends LazyLogging {
RandomLoadParameters.SIGMA,
throw new FileIOException(
s"Cannot determine column index for random load parameter ${RandomLoadParameters.SIGMA}."
- )
+ ),
)
)
.toDouble
diff --git a/src/main/scala/edu/ie3/simona/model/system/Characteristic.scala b/src/main/scala/edu/ie3/simona/model/system/Characteristic.scala
index 2be558d535..2088ca6cdc 100644
--- a/src/main/scala/edu/ie3/simona/model/system/Characteristic.scala
+++ b/src/main/scala/edu/ie3/simona/model/system/Characteristic.scala
@@ -38,7 +38,7 @@ trait Characteristic[A <: Quantity[A], O <: Quantity[O]] {
xyCoordinates.toSeq
.map(xyPair => xyPair.x -> xyPair.y)
.toMap,
- requestedAbscissaQuantity
+ requestedAbscissaQuantity,
)
xyCoords.foldLeft(
@@ -56,7 +56,7 @@ trait Characteristic[A <: Quantity[A], O <: Quantity[O]] {
Some(requestedAbscissaQuantity),
Some(
b.map(_ + (m * deltaX).value)
- )
+ ),
)
case _ =>
throw new CharacteristicsException(
@@ -75,7 +75,7 @@ trait Characteristic[A <: Quantity[A], O <: Quantity[O]] {
object Characteristic {
final case class XYPair[A <: Quantity[A], O <: Quantity[O]](
x: A,
- y: O
+ y: O,
) extends Ordered[XYPair[A, O]] {
/** The pairs are ordered by their x value first. If two pairs have the same
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/CylindricalThermalStorage.scala b/src/main/scala/edu/ie3/simona/model/thermal/CylindricalThermalStorage.scala
index 19d3470526..b6828f7dc5 100644
--- a/src/main/scala/edu/ie3/simona/model/thermal/CylindricalThermalStorage.scala
+++ b/src/main/scala/edu/ie3/simona/model/thermal/CylindricalThermalStorage.scala
@@ -10,19 +10,19 @@ import edu.ie3.datamodel.models.OperationTime
import edu.ie3.datamodel.models.input.OperatorInput
import edu.ie3.datamodel.models.input.thermal.{
CylindricalStorageInput,
- ThermalBusInput
+ ThermalBusInput,
}
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.{
StorageEmpty,
- StorageFull
+ StorageFull,
}
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.quantities.SquantsUtils.RichEnergy
import edu.ie3.util.scala.quantities.{
DefaultQuantities,
KilowattHoursPerKelvinCubicMeters,
- SpecificHeatCapacity
+ SpecificHeatCapacity,
}
import squants.energy.{Kilowatts, Megawatts}
import squants.space.{CubicMeters, Volume}
@@ -63,7 +63,7 @@ final case class CylindricalThermalStorage(
minEnergyThreshold: Energy,
maxEnergyThreshold: Energy,
chargingPower: Power,
- override protected var _storedEnergy: Energy
+ override protected var _storedEnergy: Energy,
) extends ThermalStorage(
uuid,
id,
@@ -72,7 +72,7 @@ final case class CylindricalThermalStorage(
bus,
minEnergyThreshold,
maxEnergyThreshold,
- chargingPower
+ chargingPower,
)
with MutableStorage {
@@ -93,45 +93,53 @@ final case class CylindricalThermalStorage(
override def updateState(
tick: Long,
qDot: Power,
- lastState: ThermalStorageState
+ lastState: ThermalStorageState,
): (ThermalStorageState, Option[ThermalThreshold]) = {
/* Determine new state based on time difference and given state */
val energyBalance = lastState.qDot * Seconds(tick - lastState.tick)
-
val newEnergy = lastState.storedEnergy + energyBalance
- val updatedEnergy = newEnergy match {
- case energy if energy > maxEnergyThreshold => maxEnergyThreshold
- case energy if energy < minEnergyThreshold => minEnergyThreshold
- case energy => energy
- }
+ val updatedEnergy =
+ if (isFull(newEnergy))
+ maxEnergyThreshold
+ else if (isEmpty(newEnergy))
+ minEnergyThreshold
+ else
+ newEnergy
/* Determine, when a threshold is reached */
- val nextThreshold = {
- qDot match {
- case positive if qDot > Megawatts(0d) => {
- val duration = (maxEnergyThreshold - updatedEnergy) / qDot
- Some(StorageFull(tick + Math.max(duration.toSeconds.toLong, 0L)))
- }
- case negative if qDot < Megawatts(0d) => {
- val duration = ((updatedEnergy - minEnergyThreshold) / qDot * (-1))
- Some(StorageEmpty(tick + Math.max(duration.toSeconds.toLong, 0L)))
- }
-
- case equal if qDot == Megawatts(0d) => {
-
- ThermalStorageState(tick, updatedEnergy, qDot)
+ val nextThreshold =
+ if (qDot > Megawatts(0d)) {
+ val duration = (maxEnergyThreshold - updatedEnergy) / qDot
+ val durationInTicks = Math.round(duration.toSeconds)
+ if (durationInTicks <= 0L)
None
- }
-
+ else
+ Some(StorageFull(tick + durationInTicks))
+ } else if (qDot < Megawatts(0d)) {
+ val duration = (updatedEnergy - minEnergyThreshold) / qDot * (-1)
+ val durationInTicks = Math.round(duration.toSeconds)
+ if (durationInTicks <= 0L)
+ None
+ else
+ Some(StorageEmpty(tick + durationInTicks))
+ } else {
+ return (ThermalStorageState(tick, updatedEnergy, qDot), None)
}
- }
(ThermalStorageState(tick, updatedEnergy, qDot), nextThreshold)
}
+ override def startingState: ThermalStorageState = ThermalStorageState(
+ -1L,
+ getMinEnergyThreshold,
+ Kilowatts(0d),
+ )
+
+ @deprecated("Use thermal storage state instead")
override def usableThermalEnergy: Energy =
_storedEnergy - minEnergyThreshold
+ @deprecated("Use thermal storage state instead")
override def tryToStoreAndReturnRemainder(
addedEnergy: Energy
): Option[Energy] = {
@@ -146,6 +154,7 @@ final case class CylindricalThermalStorage(
Option.empty
}
+ @deprecated("Use thermal storage state instead")
override def tryToTakeAndReturnLack(
takenEnergy: Energy
): Option[Energy] = {
@@ -154,20 +163,15 @@ final case class CylindricalThermalStorage(
if (_storedEnergy < minEnergyThreshold) {
val lack = minEnergyThreshold - _storedEnergy
_storedEnergy = minEnergyThreshold
- return Option(lack)
+ return Some(lack)
}
}
- Option.empty
+ None
}
- override def startingState: ThermalStorageState = ThermalStorageState(
- -1L,
- getMinEnergyThreshold,
- Kilowatts(0d)
- )
}
-case object CylindricalThermalStorage {
+object CylindricalThermalStorage {
/** Function to construct a new [[CylindricalThermalStorage]] based on a
* provided [[CylindricalStorageInput]]
@@ -181,7 +185,7 @@ case object CylindricalThermalStorage {
*/
def apply(
input: CylindricalStorageInput,
- initialStoredEnergy: Energy = DefaultQuantities.zeroKWH
+ initialStoredEnergy: Energy = DefaultQuantities.zeroKWH,
): CylindricalThermalStorage = {
val minEnergyThreshold: Energy =
CylindricalThermalStorage.volumeToEnergy(
@@ -197,8 +201,8 @@ case object CylindricalThermalStorage {
.getValue
.doubleValue
),
- Celsius(input.getInletTemp.to(Units.CELSIUS).getValue.doubleValue),
- Celsius(input.getReturnTemp.to(Units.CELSIUS).getValue.doubleValue)
+ Celsius(input.getInletTemp.to(Units.CELSIUS).getValue.doubleValue()),
+ Celsius(input.getReturnTemp.to(Units.CELSIUS).getValue.doubleValue()),
)
val maxEnergyThreshold: Energy =
@@ -212,13 +216,14 @@ case object CylindricalThermalStorage {
.getValue
.doubleValue
),
- Celsius(input.getInletTemp.to(Units.CELSIUS).getValue.doubleValue),
- Celsius(input.getReturnTemp.to(Units.CELSIUS).getValue.doubleValue)
+ Celsius(input.getInletTemp.to(Units.CELSIUS).getValue.doubleValue()),
+ Celsius(input.getReturnTemp.to(Units.CELSIUS).getValue.doubleValue()),
)
/* TODO: Currently, the input model does not define any maximum charge power. Assume, that the usable energy can
* be charged / discharged within the interval of an hour */
val chargingPower = (maxEnergyThreshold - minEnergyThreshold) / Hours(1d)
+
new CylindricalThermalStorage(
input.getUuid,
input.getId,
@@ -228,7 +233,7 @@ case object CylindricalThermalStorage {
minEnergyThreshold,
maxEnergyThreshold,
chargingPower,
- initialStoredEnergy
+ initialStoredEnergy,
)
}
@@ -249,7 +254,7 @@ case object CylindricalThermalStorage {
volume: Volume,
c: SpecificHeatCapacity,
inletTemp: Temperature,
- returnTemp: Temperature
+ returnTemp: Temperature,
): Energy = {
c.calcEnergy(returnTemp, inletTemp, volume)
}
@@ -271,7 +276,7 @@ case object CylindricalThermalStorage {
energy: Energy,
c: SpecificHeatCapacity,
inletTemp: Temperature,
- returnTemp: Temperature
+ returnTemp: Temperature,
): Volume = {
val energyDensity = c.calcEnergyDensity(returnTemp, inletTemp)
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/RandomStorageState.scala b/src/main/scala/edu/ie3/simona/model/thermal/RandomStorageState.scala
index acd5c33317..c447a5bf73 100644
--- a/src/main/scala/edu/ie3/simona/model/thermal/RandomStorageState.scala
+++ b/src/main/scala/edu/ie3/simona/model/thermal/RandomStorageState.scala
@@ -18,12 +18,13 @@ trait RandomStorageState {
override def startingState: ThermalStorage.ThermalStorageState = {
def rnd: Double = new Random(seed).nextDouble()
- def storedEnergy: Energy = getMaxEnergyThreshold * rnd
+ def storedEnergy: Energy =
+ getMinEnergyThreshold + (getMaxEnergyThreshold - getMinEnergyThreshold) * rnd
ThermalStorageState(
-1L,
storedEnergy,
- Kilowatts(0d)
+ Kilowatts(0d),
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala
index a1342c34ee..0586a20ee7 100644
--- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala
+++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalGrid.scala
@@ -11,23 +11,19 @@ import edu.ie3.datamodel.models.input.thermal.CylindricalStorageInput
import edu.ie3.datamodel.models.result.ResultEntity
import edu.ie3.datamodel.models.result.thermal.{
CylindricalStorageResult,
- ThermalHouseResult
+ ThermalHouseResult,
}
import edu.ie3.simona.exceptions.agent.InconsistentStateException
import edu.ie3.simona.model.thermal.ThermalGrid.{
ThermalEnergyDemand,
- ThermalGridState
+ ThermalGridState,
}
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState
-import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.HouseTemperatureUpperBoundaryReached
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState
-import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.StorageEmpty
import edu.ie3.simona.util.TickUtil.TickLong
-import edu.ie3.util.quantities.PowerSystemUnits
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import squants.energy.{Kilowatts, MegawattHours, Megawatts}
import squants.{Energy, Power, Temperature}
-import tech.units.indriya.quantity.Quantities
-import tech.units.indriya.unit.Units
import java.time.ZonedDateTime
import scala.jdk.CollectionConverters.SetHasAsScala
@@ -42,7 +38,7 @@ import scala.jdk.CollectionConverters.SetHasAsScala
*/
final case class ThermalGrid(
house: Option[ThermalHouse],
- storage: Option[ThermalStorage]
+ storage: Option[ThermalStorage],
) extends LazyLogging {
/** Determine the energy demand of the total grid at the given instance in
@@ -59,7 +55,7 @@ final case class ThermalGrid(
def energyDemand(
tick: Long,
ambientTemperature: Temperature,
- state: ThermalGridState
+ state: ThermalGridState,
): ThermalEnergyDemand = {
/* First get the energy demand of the houses */
val houseDemand = house
@@ -68,7 +64,7 @@ final case class ThermalGrid(
house.energyDemand(
tick,
ambientTemperature,
- state
+ state,
)
}
.getOrElse(ThermalEnergyDemand.noDemand)
@@ -82,14 +78,11 @@ final case class ThermalGrid(
val remaining = storage.getMaxEnergyThreshold - usableEnergy
(
usableEnergy,
- remaining
+ remaining,
)
}
.getOrElse(
- (
- MegawattHours(0d),
- MegawattHours(0d)
- )
+ (MegawattHours(0d), MegawattHours(0d))
)
}
@@ -98,12 +91,11 @@ final case class ThermalGrid(
houseDemand.required
else
storedEnergy
- val finallyRemaining =
- remainingCapacity + usedEnergy
+ val finallyRemaining = remainingCapacity + usedEnergy
ThermalEnergyDemand(
houseDemand.required - usedEnergy,
- houseDemand.possible + finallyRemaining
+ houseDemand.possible + finallyRemaining,
)
}
@@ -123,11 +115,8 @@ final case class ThermalGrid(
tick: Long,
state: ThermalGridState,
ambientTemperature: Temperature,
- qDot: Power
- ): (ThermalGridState, Option[ThermalThreshold]) = if (
- qDot >
- Kilowatts(0d)
- )
+ qDot: Power,
+ ): (ThermalGridState, Option[ThermalThreshold]) = if (qDot > Kilowatts(0d))
handleInfeed(tick, ambientTemperature, state, qDot)
else
handleConsumption(tick, ambientTemperature, state, qDot)
@@ -149,71 +138,80 @@ final case class ThermalGrid(
tick: Long,
ambientTemperature: Temperature,
state: ThermalGridState,
- qDot: Power
+ qDot: Power,
): (ThermalGridState, Option[ThermalThreshold]) =
house.zip(state.houseState) match {
case Some((thermalHouse, lastHouseState)) =>
/* Set thermal power exchange with storage to zero */
// TODO: We would need to issue a storage result model here...
- val zeroStorageState = storage.zip(state.storageState) match {
+ val updatedStorageState = storage.zip(state.storageState) match {
case Some((thermalStorage, storageState)) =>
Some(
thermalStorage
.updateState(
tick,
Kilowatts(0d),
- storageState
+ storageState,
)
._1
)
case _ => state.storageState
}
- thermalHouse.updateState(
- tick,
- lastHouseState,
- ambientTemperature,
- qDot
- ) match {
- case (_, Some(HouseTemperatureUpperBoundaryReached(thresholdTick)))
- if thresholdTick == tick =>
- /* The house is already heated up fully, set back the infeed and put it into storage, if available */
- val (updatedHouseState, maybeHouseThreshold) =
- thermalHouse.updateState(
- tick,
- lastHouseState,
- ambientTemperature,
- Kilowatts(0d)
+ val (updatedHouseState, maybeHouseThreshold) =
+ thermalHouse.determineState(
+ tick,
+ lastHouseState,
+ ambientTemperature,
+ qDot,
+ )
+
+ if (
+ thermalHouse.isInnerTemperatureTooHigh(
+ updatedHouseState.innerTemperature
+ )
+ ) {
+ /* The house is already heated up fully, set back the infeed and put it into storage, if available */
+ val (fullHouseState, maybeFullHouseThreshold) =
+ thermalHouse.determineState(
+ tick,
+ lastHouseState,
+ ambientTemperature,
+ Kilowatts(0d),
+ )
+ storage.zip(updatedStorageState) match {
+ case Some((thermalStorage, storageState)) =>
+ val (updatedStorageState, maybeStorageThreshold) =
+ thermalStorage.updateState(tick, qDot, storageState)
+
+ /* Both house and storage are updated. Determine what reaches the next threshold */
+ val nextThreshold = determineMostRecentThreshold(
+ maybeFullHouseThreshold,
+ maybeStorageThreshold,
)
- storage.zip(zeroStorageState) match {
- case Some((thermalStorage, storageState)) =>
- val (updatedStorageState, maybeStorageThreshold) =
- thermalStorage.updateState(tick, qDot, storageState)
-
- /* Both house and storage are updated. Determine what reaches the next threshold */
- val nextThreshold = determineMostRecentThreshold(
- maybeHouseThreshold,
- maybeStorageThreshold
- )
- (
- state.copy(
- houseState = Some(updatedHouseState),
- storageState = Some(updatedStorageState)
- ),
- nextThreshold
- )
- case None =>
- /* There is no storage, house determines the next activation */
- (
- state.copy(houseState = Some(updatedHouseState)),
- maybeHouseThreshold
- )
- }
- case (updatedState, maybeThreshold) =>
- /* The house can handle the infeed */
- (state.copy(houseState = Some(updatedState)), maybeThreshold)
+ (
+ state.copy(
+ houseState = Some(fullHouseState),
+ storageState = Some(updatedStorageState),
+ ),
+ nextThreshold,
+ )
+ case None =>
+ /* There is no storage, house determines the next activation */
+ (
+ state.copy(houseState = Some(fullHouseState)),
+ maybeFullHouseThreshold,
+ )
+ }
+ } else {
+ /* The house can handle the infeed */
+ (
+ state.copy(houseState = Some(updatedHouseState)),
+ maybeHouseThreshold,
+ )
}
+
case None =>
storage.zip(state.storageState) match {
case Some((thermalStorage, storageState)) =>
@@ -221,7 +219,7 @@ final case class ThermalGrid(
thermalStorage.updateState(tick, qDot, storageState)
(
state.copy(storageState = Some(updatedStorageState)),
- maybeStorageThreshold
+ maybeStorageThreshold,
)
case None =>
throw new InconsistentStateException(
@@ -232,7 +230,7 @@ final case class ThermalGrid(
private def determineMostRecentThreshold(
maybeHouseThreshold: Option[ThermalThreshold],
- maybeStorageThreshold: Option[ThermalThreshold]
+ maybeStorageThreshold: Option[ThermalThreshold],
): Option[ThermalThreshold] =
(maybeHouseThreshold, maybeStorageThreshold) match {
case (Some(houseThreshold), Some(storageThreshold)) =>
@@ -246,6 +244,7 @@ final case class ThermalGrid(
}
/** Handle consumption (or no infeed) from thermal grid
+ *
* @param tick
* Current tick
* @param ambientTemperature
@@ -261,16 +260,16 @@ final case class ThermalGrid(
tick: Long,
ambientTemperature: Temperature,
state: ThermalGridState,
- qDot: Power
+ qDot: Power,
): (ThermalGridState, Option[ThermalThreshold]) = {
/* House will be left with no influx in all cases. Determine if and when a threshold is reached */
val maybeUpdatedHouseState =
house.zip(state.houseState).map { case (house, houseState) =>
- house.updateState(
+ house.determineState(
tick,
houseState,
ambientTemperature,
- Megawatts(0d)
+ Megawatts(0d),
)
}
@@ -288,27 +287,27 @@ final case class ThermalGrid(
state.houseState,
state.storageState,
ambientTemperature,
- qDot
+ qDot,
)
val nextThreshold = determineMostRecentThreshold(
revisedHouseState.flatMap(_._2),
- revisedStorageState.flatMap(_._2)
+ revisedStorageState.flatMap(_._2),
)
(
state.copy(
houseState = revisedHouseState.map(_._1),
- storageState = revisedStorageState.map(_._1)
+ storageState = revisedStorageState.map(_._1),
),
- nextThreshold
+ nextThreshold,
)
}
/** Check, if the storage can heat the house. This is only done, if
- * - The house has reached it's lower temperature boundary
- There
- * is no infeed from external
- The storage is not empty itself
- *
+ * the house has reached it's lower temperature boundary, there
+ * is no infeed from external and the storage is not empty
+ * itself
* @param tick
* The current tick
* @param maybeHouseState
@@ -335,32 +334,32 @@ final case class ThermalGrid(
formerHouseState: Option[ThermalHouseState],
formerStorageState: Option[ThermalStorageState],
ambientTemperature: Temperature,
- qDot: Power
+ qDot: Power,
): (
Option[(ThermalHouseState, Option[ThermalThreshold])],
- Option[(ThermalStorageState, Option[ThermalThreshold])]
+ Option[(ThermalStorageState, Option[ThermalThreshold])],
) = house.zip(maybeHouseState).zip(storage.zip(maybeStorageState)) match {
case Some(
(
(thermalHouse, (houseState, _)),
- (thermalStorage, (_, maybeStorageThreshold))
+ (thermalStorage, (storageState, _)),
)
)
- if (Math.abs(qDot.toKilowatts) < 10e-3) && thermalHouse
- .isInnerTemperatureTooLow(
+ if qDot.~=(Kilowatts(0d))(Kilowatts(10e-3)) &&
+ thermalHouse.isInnerTemperatureTooLow(
houseState.innerTemperature
- ) && !maybeStorageThreshold.contains(StorageEmpty(tick)) =>
+ ) && !thermalStorage.isEmpty(storageState.storedEnergy) =>
/* Storage is meant to heat the house only, if there is no infeed from external (+/- 10 W) and the house is cold */
val revisedStorageState = thermalStorage.updateState(
tick,
- thermalStorage.getChargingPower * (-1),
+ thermalStorage.getChargingPower * -1,
formerStorageState.getOrElse(
throw new InconsistentStateException(
"Impossible to find no storage state"
)
- )
+ ),
)
- val revisedHouseState = thermalHouse.updateState(
+ val revisedHouseState = thermalHouse.determineState(
tick,
formerHouseState.getOrElse(
throw new InconsistentStateException(
@@ -368,7 +367,7 @@ final case class ThermalGrid(
)
),
ambientTemperature,
- thermalStorage.getChargingPower
+ thermalStorage.getChargingPower,
)
(Some(revisedHouseState), Some(revisedStorageState))
case _ => (maybeHouseState, maybeStorageState)
@@ -391,17 +390,13 @@ final case class ThermalGrid(
.map {
case (
thermalHouse,
- ThermalHouseState(tick, innerTemperature, thermalInfeed)
+ ThermalHouseState(tick, innerTemperature, thermalInfeed),
) =>
Seq.empty[ResultEntity] :+ new ThermalHouseResult(
tick.toDateTime,
thermalHouse.uuid,
- Quantities.getQuantity(
- thermalInfeed.toMegawatts,
- PowerSystemUnits.MEGAWATT
- ),
- Quantities
- .getQuantity(innerTemperature.toCelsiusScale, Units.CELSIUS)
+ thermalInfeed.toMegawatts.asMegaWatt,
+ innerTemperature.toKelvinScale.asKelvin,
)
}
.getOrElse(Seq.empty[ResultEntity])
@@ -411,20 +406,14 @@ final case class ThermalGrid(
.map {
case (
storage: CylindricalThermalStorage,
- ThermalStorageState(tick, storedEnergy, qDot)
+ ThermalStorageState(tick, storedEnergy, qDot),
) =>
houseResults :+ new CylindricalStorageResult(
tick.toDateTime,
storage.uuid,
- Quantities.getQuantity(
- storedEnergy.toMegawattHours,
- PowerSystemUnits.MEGAWATTHOUR
- ),
- Quantities.getQuantity(qDot.toMegawatts, PowerSystemUnits.MEGAWATT),
- Quantities.getQuantity(
- storage.maxEnergyThreshold.toKilowattHours / storedEnergy.toKilowattHours,
- Units.PERCENT
- )
+ storedEnergy.toMegawattHours.asMegaWattHour,
+ qDot.toMegawatts.asMegaWatt,
+ (storage.maxEnergyThreshold / storedEnergy).asPu,
)
case _ =>
throw new NotImplementedError(
@@ -451,7 +440,7 @@ object ThermalGrid {
.toSet
new ThermalGrid(
houses.headOption,
- storages.headOption
+ storages.headOption,
)
}
@@ -463,13 +452,13 @@ object ThermalGrid {
*/
final case class ThermalGridState(
houseState: Option[ThermalHouseState],
- storageState: Option[ThermalStorageState]
+ storageState: Option[ThermalStorageState],
)
def startingState(thermalGrid: ThermalGrid): ThermalGridState =
ThermalGridState(
thermalGrid.house.map(house => ThermalHouse.startingState(house)),
- thermalGrid.storage.map(_.startingState)
+ thermalGrid.storage.map(_.startingState),
)
/** Defines the thermal energy demand of a thermal grid. It comprises the
@@ -484,11 +473,11 @@ object ThermalGrid {
*/
final case class ThermalEnergyDemand private (
required: Energy,
- possible: Energy
+ possible: Energy,
) {
def +(rhs: ThermalEnergyDemand): ThermalEnergyDemand = ThermalEnergyDemand(
required + rhs.required,
- possible + rhs.possible
+ possible + rhs.possible,
)
def hasRequiredDemand: Boolean = required > MegawattHours(0d)
@@ -509,23 +498,17 @@ object ThermalGrid {
*/
def apply(
required: Energy,
- possible: Energy
+ possible: Energy,
): ThermalEnergyDemand = {
if (possible < required)
- new ThermalEnergyDemand(
- possible,
- possible
- )
+ new ThermalEnergyDemand(possible, possible)
else
- new ThermalEnergyDemand(
- required,
- possible
- )
+ new ThermalEnergyDemand(required, possible)
}
def noDemand: ThermalEnergyDemand = ThermalEnergyDemand(
MegawattHours(0d),
- MegawattHours(0d)
+ MegawattHours(0d),
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala
index b5eb9cbf48..b702102080 100644
--- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala
+++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalHouse.scala
@@ -10,30 +10,28 @@ import edu.ie3.datamodel.models.OperationTime
import edu.ie3.datamodel.models.input.OperatorInput
import edu.ie3.datamodel.models.input.thermal.{
ThermalBusInput,
- ThermalHouseInput
+ ThermalHouseInput,
}
import edu.ie3.simona.model.thermal.ThermalGrid.ThermalEnergyDemand
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{
HouseTemperatureLowerBoundaryReached,
- HouseTemperatureUpperBoundaryReached
+ HouseTemperatureUpperBoundaryReached,
}
import edu.ie3.simona.model.thermal.ThermalHouse.{
ThermalHouseState,
- temperatureTolerance
+ temperatureTolerance,
}
-import edu.ie3.simona.util.TickUtil.TickLong
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.quantities.{ThermalConductance, WattsPerKelvin}
-import squants.energy.{KilowattHours, MegawattHours, Megawatts}
-import squants.thermal.{Celsius, JoulesPerKelvin, Kelvin, ThermalCapacity}
-import squants.time.Hours
+import squants.energy.{KilowattHours, Kilowatts, MegawattHours, Megawatts}
+import squants.thermal.{Kelvin, ThermalCapacity}
+import squants.time.{Hours, Seconds}
import squants.{Energy, Power, Temperature, Time}
import tech.units.indriya.unit.Units
import java.util.UUID
-/** A thermal house model including a variable inner temperature *
- * Important: The field innerTemperature is a variable.
+/** A thermal house model
*
* @param uuid
* the element's uuid
@@ -66,13 +64,13 @@ final case class ThermalHouse(
ethCapa: ThermalCapacity,
targetTemperature: Temperature,
lowerBoundaryTemperature: Temperature,
- upperBoundaryTemperature: Temperature
+ upperBoundaryTemperature: Temperature,
) extends ThermalSink(
uuid,
id,
operatorInput,
operationTime,
- bus
+ bus,
) {
/** Calculate the energy demand at the instance in question. If the inner
@@ -95,27 +93,42 @@ final case class ThermalHouse(
def energyDemand(
tick: Long,
ambientTemperature: Temperature,
- state: ThermalHouseState
+ state: ThermalHouseState,
): ThermalEnergyDemand = {
/* Calculate the inner temperature of the house, at the questioned instance in time */
- val duration = state.tick.durationUntil(tick)
- val innerTemperature = newInnerTemperature(
+ val duration = Seconds(tick - state.tick)
+ val currentInnerTemp = newInnerTemperature(
state.qDot,
duration,
state.innerTemperature,
- ambientTemperature
+ ambientTemperature,
)
- /* Determine the needed energy */
+ /* Determine, which temperature boundary triggers a needed energy to reach the temperature constraints */
+ val temperatureToTriggerRequiredEnergy =
+ if (
+ currentInnerTemp <= state.innerTemperature &&
+ state.qDot <= Kilowatts(0d)
+ ) {
+ // temperature has been decreasing and heat source has been turned off
+ // => we have reached target temp before and are now targeting lower temp
+ lowerBoundaryTemperature
+ } else targetTemperature
val requiredEnergy =
- if (isInnerTemperatureTooLow(innerTemperature)) {
- energy(targetTemperature, innerTemperature)
- } else
+ if (
+ isInnerTemperatureTooLow(
+ currentInnerTemp,
+ temperatureToTriggerRequiredEnergy,
+ )
+ ) energy(targetTemperature, currentInnerTemp)
+ else
MegawattHours(0d)
val possibleEnergy =
- if (!isInnerTemperatureTooHigh(innerTemperature)) {
- energy(upperBoundaryTemperature, innerTemperature)
+ if (!isInnerTemperatureTooHigh(currentInnerTemp)) {
+ // if upper boundary has not been reached,
+ // there is an amount of optional energy that could be stored
+ energy(upperBoundaryTemperature, currentInnerTemp)
} else
MegawattHours(0d)
ThermalEnergyDemand(requiredEnergy, possibleEnergy)
@@ -133,7 +146,7 @@ final case class ThermalHouse(
*/
private def energy(
targetTemperature: Temperature,
- startTemperature: Temperature
+ startTemperature: Temperature,
): Energy = {
ethCapa * Kelvin(
targetTemperature.toKelvinScale - startTemperature.toKelvinScale
@@ -148,7 +161,9 @@ final case class ThermalHouse(
def isInnerTemperatureTooHigh(
innerTemperature: Temperature
): Boolean =
- innerTemperature > (upperBoundaryTemperature - temperatureTolerance)
+ innerTemperature > Kelvin(
+ upperBoundaryTemperature.toKelvinScale - temperatureTolerance.toKelvinScale
+ )
/** Check if inner temperature is lower than preferred minimum temperature
*
@@ -156,9 +171,12 @@ final case class ThermalHouse(
* true, if inner temperature is too low
*/
def isInnerTemperatureTooLow(
- innerTemperature: Temperature
+ innerTemperature: Temperature,
+ boundaryTemperature: Temperature = lowerBoundaryTemperature,
): Boolean =
- innerTemperature < (lowerBoundaryTemperature + temperatureTolerance)
+ innerTemperature < Kelvin(
+ boundaryTemperature.toKelvinScale + temperatureTolerance.toKelvinScale
+ )
/** Calculate the new inner temperature of the thermal house.
*
@@ -177,19 +195,19 @@ final case class ThermalHouse(
thermalPower: Power,
duration: Time,
currentInnerTemperature: Temperature,
- ambientTemperature: Temperature
+ ambientTemperature: Temperature,
): Temperature = {
val thermalEnergyChange = calcThermalEnergyChange(
calcThermalEnergyGain(thermalPower, duration),
calcThermalEnergyLoss(
currentInnerTemperature,
ambientTemperature,
- duration
- )
+ duration,
+ ),
)
calcNewInnerTemperature(
currentInnerTemperature,
- calcInnerTemperatureChange(thermalEnergyChange)
+ calcInnerTemperatureChange(thermalEnergyChange),
)
}
@@ -204,7 +222,7 @@ final case class ThermalHouse(
*/
private def calcNewInnerTemperature(
oldInnerTemperature: Temperature,
- temperatureChange: Temperature
+ temperatureChange: Temperature,
): Temperature =
oldInnerTemperature + temperatureChange
@@ -233,7 +251,7 @@ final case class ThermalHouse(
*/
private def calcThermalEnergyChange(
thermalEnergyGain: Energy,
- thermalEnergyLoss: Energy
+ thermalEnergyLoss: Energy,
): Energy =
thermalEnergyGain - thermalEnergyLoss
@@ -248,7 +266,7 @@ final case class ThermalHouse(
*/
private def calcThermalEnergyGain(
pThermal: Power,
- time: Time
+ time: Time,
): Energy = pThermal * time
/** Calculate the thermal energy loss due to the temperature deviation over
@@ -266,16 +284,17 @@ final case class ThermalHouse(
private def calcThermalEnergyLoss(
innerTemperature: Temperature,
ambientTemperature: Temperature,
- time: Time
+ time: Time,
): Energy = {
ethLosses.thermalConductanceToEnergy(
innerTemperature,
ambientTemperature,
- time
+ time,
)
}
/** Update the current state of the house
+ *
* @param tick
* current instance in time
* @param state
@@ -287,18 +306,18 @@ final case class ThermalHouse(
* @return
* Updated state and the tick in which the next threshold is reached
*/
- def updateState(
+ def determineState(
tick: Long,
state: ThermalHouseState,
ambientTemperature: Temperature,
- qDot: Power
+ qDot: Power,
): (ThermalHouseState, Option[ThermalThreshold]) = {
- val duration = state.tick.durationUntil(tick)
+ val duration = Seconds(tick - state.tick)
val updatedInnerTemperature = newInnerTemperature(
state.qDot,
duration,
state.innerTemperature,
- ambientTemperature
+ ambientTemperature,
)
/* Calculate the next given threshold */
@@ -309,9 +328,9 @@ final case class ThermalHouse(
state.copy(
tick = tick,
innerTemperature = updatedInnerTemperature,
- qDot = qDot
+ qDot = qDot,
),
- threshold
+ threshold,
)
}
@@ -327,46 +346,46 @@ final case class ThermalHouse(
* @return
* The next threshold, that will be reached
*/
-
private def nextThreshold(
tick: Long,
qDotExternal: Power,
innerTemperature: Temperature,
- ambientTemperature: Temperature
+ ambientTemperature: Temperature,
): Option[ThermalThreshold] = {
val artificialDuration = Hours(1d)
val loss = calcThermalEnergyLoss(
innerTemperature,
ambientTemperature,
- artificialDuration
+ artificialDuration,
) / artificialDuration
-
val resultingQDot = qDotExternal - loss
-
- resultingQDot match {
- case qDot if qDot < Megawatts(0d) =>
- /* House has more losses than gain */
- val nextTick = nextActivation(
- tick,
- innerTemperature,
- lowerBoundaryTemperature,
- resultingQDot
- )
- Some(HouseTemperatureLowerBoundaryReached(nextTick))
-
- case qDot if qDot > Megawatts(0d) =>
- /* House has more gain than losses */
- val nextTick = nextActivation(
- tick,
- upperBoundaryTemperature,
- innerTemperature,
- resultingQDot
- )
- Some(HouseTemperatureUpperBoundaryReached(nextTick))
-
- case _ =>
- /* House is in perfect balance */
- None
+ if (
+ resultingQDot < Megawatts(0d) && !isInnerTemperatureTooLow(
+ innerTemperature
+ )
+ ) {
+ /* House has more losses than gain */
+ nextActivation(
+ tick,
+ innerTemperature,
+ lowerBoundaryTemperature,
+ resultingQDot,
+ ).map(HouseTemperatureLowerBoundaryReached)
+ } else if (
+ resultingQDot > Megawatts(0d) && !isInnerTemperatureTooHigh(
+ innerTemperature
+ )
+ ) {
+ /* House has more gain than losses */
+ nextActivation(
+ tick,
+ upperBoundaryTemperature,
+ innerTemperature,
+ resultingQDot,
+ ).map(HouseTemperatureUpperBoundaryReached)
+ } else {
+ /* House is in perfect balance */
+ None
}
}
@@ -374,25 +393,22 @@ final case class ThermalHouse(
tick: Long,
higherTemperature: Temperature,
lowerTemperature: Temperature,
- qDot: Power
- ): Long = {
+ qDot: Power,
+ ): Option[Long] = {
val flexibleEnergy = energy(higherTemperature, lowerTemperature)
- if (
- flexibleEnergy <
- KilowattHours(0d)
- )
- tick
+ if (flexibleEnergy < MegawattHours(0d))
+ None
else {
- val duration =
- flexibleEnergy / (qDot * math.signum(qDot.value.doubleValue()))
- tick + duration.toSeconds.toLong
+ val duration = Math.round(
+ (flexibleEnergy / (qDot * math.signum(qDot.toWatts))).toSeconds
+ )
+ Some(tick + duration)
}
}
}
object ThermalHouse {
- protected def temperatureTolerance: Temperature =
- Kelvin(0.01d)
+ protected def temperatureTolerance: Temperature = Kelvin(0.01d)
def apply(input: ThermalHouseInput): ThermalHouse = new ThermalHouse(
input.getUuid,
@@ -405,24 +421,23 @@ object ThermalHouse {
.to(PowerSystemUnits.KILOWATT_PER_KELVIN)
.getValue
.doubleValue
- // Kilowatt in Watt
- * 1000
+ * 1000 // kW/K to W/K
),
- JoulesPerKelvin(
+ KilowattHours(
input.getEthCapa
.to(PowerSystemUnits.KILOWATTHOUR_PER_KELVIN)
.getValue
.doubleValue
- // from kWh to Joule
- * 3.6e6
+ ) / Kelvin(1d),
+ Kelvin(
+ input.getTargetTemperature.to(Units.KELVIN).getValue.doubleValue
),
- Celsius(input.getTargetTemperature.to(Units.CELSIUS).getValue.doubleValue),
- Celsius(
- input.getLowerTemperatureLimit.to(Units.CELSIUS).getValue.doubleValue
+ Kelvin(
+ input.getLowerTemperatureLimit.to(Units.KELVIN).getValue.doubleValue
+ ),
+ Kelvin(
+ input.getUpperTemperatureLimit.to(Units.KELVIN).getValue.doubleValue
),
- Celsius(
- input.getUpperTemperatureLimit.to(Units.CELSIUS).getValue.doubleValue
- )
)
/** State of a thermal house
@@ -435,16 +450,16 @@ object ThermalHouse {
* Continuous infeed of thermal energy since the given tick
*/
final case class ThermalHouseState(
- override val tick: Long,
+ tick: Long,
innerTemperature: Temperature,
- qDot: Power
- ) extends ThermalModelState
+ qDot: Power,
+ )
def startingState(house: ThermalHouse): ThermalHouseState =
ThermalHouseState(
-1L,
house.targetTemperature,
- Megawatts(0d)
+ Megawatts(0d),
)
object ThermalHouseThreshold {
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalModelState.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalModelState.scala
deleted file mode 100644
index 209c58ff8a..0000000000
--- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalModelState.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * © 2022. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.model.thermal
-
-/** Trait to group all states, that belong to a thermal model
- */
-trait ThermalModelState {
-
- /** Simulation instance, since that state is valid
- */
- val tick: Long
-}
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalSink.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalSink.scala
index 9b46902d8f..97661b0201 100644
--- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalSink.scala
+++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalSink.scala
@@ -30,5 +30,5 @@ abstract class ThermalSink(
id: String,
operatorInput: OperatorInput,
operationTime: OperationTime,
- bus: ThermalBusInput
+ bus: ThermalBusInput,
)
diff --git a/src/main/scala/edu/ie3/simona/model/thermal/ThermalStorage.scala b/src/main/scala/edu/ie3/simona/model/thermal/ThermalStorage.scala
index cc8cd36745..03460fee5e 100644
--- a/src/main/scala/edu/ie3/simona/model/thermal/ThermalStorage.scala
+++ b/src/main/scala/edu/ie3/simona/model/thermal/ThermalStorage.scala
@@ -10,8 +10,8 @@ import edu.ie3.datamodel.models.OperationTime
import edu.ie3.datamodel.models.input.OperatorInput
import edu.ie3.datamodel.models.input.thermal.ThermalBusInput
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState
-import edu.ie3.util.scala.quantities.DefaultQuantities
-import squants.{Energy, Power}
+import squants.{Energy, Power, Seconds}
+import squants.energy.KilowattHours
import java.util.UUID
@@ -42,10 +42,14 @@ abstract class ThermalStorage(
bus: ThermalBusInput,
minEnergyThreshold: Energy,
maxEnergyThreshold: Energy,
- chargingPower: Power
+ chargingPower: Power,
) {
- protected val zeroEnergy: Energy =
- DefaultQuantities.zeroKWH
+ protected val zeroEnergy: Energy = KilowattHours(0d)
+
+ /** In order to avoid faulty flexibility options, we want to avoid offering
+ * charging/discharging that could last less than one second.
+ */
+ private val toleranceMargin = chargingPower * Seconds(1d)
def getUuid: UUID = uuid
@@ -57,10 +61,16 @@ abstract class ThermalStorage(
def startingState: ThermalStorageState
+ def isFull(energy: Energy): Boolean =
+ energy > (maxEnergyThreshold - toleranceMargin)
+
+ def isEmpty(energy: Energy): Boolean =
+ energy < (minEnergyThreshold + toleranceMargin)
+
def updateState(
tick: Long,
qDot: Power,
- lastState: ThermalStorageState
+ lastState: ThermalStorageState,
): (ThermalStorageState, Option[ThermalThreshold])
}
@@ -68,7 +78,7 @@ object ThermalStorage {
final case class ThermalStorageState(
tick: Long,
storedEnergy: Energy,
- qDot: Power
+ qDot: Power,
)
object ThermalStorageThreshold {
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/PowerMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/PowerMessage.scala
index d82961cd5a..2c9ad3fb47 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/PowerMessage.scala
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/PowerMessage.scala
@@ -6,8 +6,10 @@
package edu.ie3.simona.ontology.messages
+import edu.ie3.simona.agent.grid.GridAgentMessage
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
import edu.ie3.util.scala.quantities.ReactivePower
+import org.apache.pekko.actor.typed.ActorRef
import squants.{Dimensionless, Power}
import java.util.UUID
@@ -41,7 +43,7 @@ object PowerMessage {
final case class RequestAssetPowerMessage(
currentTick: Long,
eInPu: Dimensionless,
- fInPu: Dimensionless
+ fInPu: Dimensionless,
) extends PowerRequestMessage
/** Provide power values as a reply to a [[RequestAssetPowerMessage]]
@@ -53,7 +55,7 @@ object PowerMessage {
*/
final case class AssetPowerChangedMessage(
override val p: Power,
- override val q: ReactivePower
+ override val q: ReactivePower,
) extends ProvidePowerMessage
/** Provide values as a reply to a [[RequestAssetPowerMessage]]. In contrast
@@ -67,7 +69,7 @@ object PowerMessage {
*/
final case class AssetPowerUnchangedMessage(
override val p: Power,
- override val q: ReactivePower
+ override val q: ReactivePower,
) extends ProvidePowerMessage
/** Request complex power at the nodes that the inferior sub grid shares with
@@ -79,7 +81,8 @@ object PowerMessage {
*/
final case class RequestGridPowerMessage(
currentSweepNo: Int,
- nodeUuids: Seq[UUID]
+ nodeUuids: Seq[UUID],
+ sender: ActorRef[GridAgentMessage],
) extends PowerRequestMessage
/** Provide complex power at the nodes that the sender's sub grid shares with
@@ -105,7 +108,7 @@ object PowerMessage {
final case class ExchangePower(
nodeUuid: UUID,
override val p: Power,
- override val q: ReactivePower
+ override val q: ReactivePower,
) extends ProvidePowerMessage
}
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala
index 866cae63a8..443971ef18 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/SchedulerMessage.scala
@@ -10,18 +10,18 @@ import org.apache.pekko.actor.typed.ActorRef
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.scheduler.{Scheduler, TimeAdvancer}
-trait SchedulerMessage extends Scheduler.Incoming with TimeAdvancer.Incoming
+trait SchedulerMessage extends Scheduler.Request with TimeAdvancer.Request
object SchedulerMessage {
final case class Completion(
actor: ActorRef[Activation],
- newTick: Option[Long] = None
+ newTick: Option[Long] = None,
) extends SchedulerMessage
final case class ScheduleActivation(
actor: ActorRef[Activation],
tick: Long,
- unlockKey: Option[ScheduleKey] = None
+ unlockKey: Option[ScheduleKey] = None,
) extends SchedulerMessage
}
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/StopMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/StopMessage.scala
deleted file mode 100644
index c6e4809a27..0000000000
--- a/src/main/scala/edu/ie3/simona/ontology/messages/StopMessage.scala
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * © 2022. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.ontology.messages
-
-import edu.ie3.simona.event.listener.ResultEventListener.ResultMessage
-
-final case class StopMessage(simulationSuccessful: Boolean)
- extends ResultMessage
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala
new file mode 100644
index 0000000000..f6994db75c
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/flex/FlexibilityMessage.scala
@@ -0,0 +1,143 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.ontology.messages.flex
+
+import edu.ie3.datamodel.models.input.AssetInput
+import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
+import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
+import org.apache.pekko.actor.typed.ActorRef
+import squants.Power
+
+import java.util.UUID
+
+/** Messages used to facilitate flexibility-based communication between
+ * [[edu.ie3.simona.agent.em.EmAgent]] and
+ * [[edu.ie3.simona.agent.participant.ParticipantAgent]]s.
+ */
+object FlexibilityMessage {
+
+ /** Trait that is extended by all messages that are supposed to be received by
+ * a flex options provider, which could be any
+ * [[edu.ie3.simona.agent.participant.ParticipantAgent]] or
+ * [[edu.ie3.simona.agent.em.EmAgent]], if it is EM-controlled.
+ */
+ sealed trait FlexRequest {
+ val tick: Long
+ }
+
+ /** Trait that is extended by all messages that are supposed to be received by
+ * [[edu.ie3.simona.agent.em.EmAgent]]s.
+ */
+ sealed trait FlexResponse {
+ val modelUuid: UUID
+ }
+
+ /** Message that registers a flex options provider with an
+ * [[edu.ie3.simona.agent.em.EmAgent]].
+ *
+ * @param modelUuid
+ * The UUID of the flex options provider asset model
+ * @param participant
+ * The actor reference to the flex options provider
+ * @param inputModel
+ * The asset input model of the flex options provider
+ */
+ final case class RegisterParticipant(
+ override val modelUuid: UUID,
+ participant: ActorRef[FlexRequest],
+ inputModel: AssetInput,
+ ) extends FlexResponse
+
+ /** Message that schedules a flex request for a flex options provider at given
+ * tick.
+ *
+ * @param modelUuid
+ * The UUID of the flex options provider asset model
+ * @param tick
+ * The tick to schedule the flex options provider for
+ * @param scheduleKey
+ * Optionally a schedule key that unlocks the scheduler once the scheduling
+ * chain is completed
+ */
+ final case class ScheduleFlexRequest(
+ override val modelUuid: UUID,
+ tick: Long,
+ scheduleKey: Option[ScheduleKey] = None,
+ ) extends FlexResponse
+
+ /** Message that requests flex options from a flex options provider for given
+ * tick
+ *
+ * @param tick
+ * The tick to request flex options for
+ */
+ final case class RequestFlexOptions(override val tick: Long)
+ extends FlexRequest
+
+ /** Message that provides flex options to an
+ * [[edu.ie3.simona.agent.em.EmAgent]] after they have been requested via
+ * [[RequestFlexOptions]]
+ */
+ trait ProvideFlexOptions extends FlexResponse
+
+ /** Message that issues flexibility control to a flex options provider, i.e. a
+ * feasible set point is delivered that the flex options provider should
+ * adhere to
+ */
+ trait IssueFlexControl extends FlexRequest
+
+ /** Message sent by [[edu.ie3.simona.agent.em.EmAgent]] that specifies a power
+ * target that needs to be produced/consumed by the system participant.
+ *
+ * @param tick
+ * The current tick
+ * @param setPower
+ * The power that the system participant should produce (negative) or
+ * consume (positive)
+ */
+ final case class IssuePowerControl(
+ override val tick: Long,
+ setPower: Power,
+ ) extends IssueFlexControl
+
+ /** Message sent by [[edu.ie3.simona.agent.em.EmAgent]] indicating that no
+ * power target is set and the reference power communicated by
+ * [[ProvideFlexOptions]] shall be produced/consumed.
+ *
+ * @param tick
+ * The current tick
+ */
+ final case class IssueNoControl(override val tick: Long)
+ extends IssueFlexControl
+
+ /** Message sent by flex options providers indicating that the
+ * [[IssueFlexControl]] message has been handled and the flex communication
+ * for the current tick is completed.
+ *
+ * @param modelUuid
+ * The UUID of the flex options provider asset model
+ * @param result
+ * The apparent power that is produced/consumed by the flex options
+ * provider, which can deviate from the set point communicated by a
+ * [[IssueFlexControl]] message if it is not feasible.
+ * @param requestAtNextActivation
+ * Whether or not to request flex options at the very next activation of
+ * the receiving EM agent. This is the case if flex options change the very
+ * next second after the current tick.
+ * @param requestAtTick
+ * Optionally the tick at which flex options are foreseen to have changed,
+ * i.e. the tick at which the flex options provider would like to be
+ * activated at the latest.
+ */
+ final case class FlexCtrlCompletion(
+ override val modelUuid: UUID,
+ result: ApparentPower,
+ requestAtNextActivation: Boolean = false,
+ requestAtTick: Option[Long] = None,
+ ) extends FlexResponse
+
+}
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala
new file mode 100644
index 0000000000..c814b14ef0
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessage.scala
@@ -0,0 +1,110 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.ontology.messages.flex
+
+import edu.ie3.simona.exceptions.CriticalFailureException
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.ProvideFlexOptions
+import squants.Power
+
+import java.util.UUID
+
+/** Messages that communicate interval-based flexibility with minimum, reference
+ * and maximum power
+ */
+object MinMaxFlexibilityMessage {
+
+ /** Message that provides flexibility options using reference, minimum and
+ * maximum power. It is possible that the power values are either all
+ * negative or all positive, meaning that feed-in or load is mandatory.
+ *
+ * @param modelUuid
+ * The UUID of the flex options provider asset model
+ * @param ref
+ * The reference active power that the flex options provider would
+ * produce/consume regularly at the current tick, i.e. if it was not
+ * flex-controlled
+ * @param min
+ * The minimum active power that the flex options provider allows at the
+ * current tick
+ * @param max
+ * The maximum active power that the flex options provider allows at the
+ * current tick
+ */
+ final case class ProvideMinMaxFlexOptions private (
+ override val modelUuid: UUID,
+ ref: Power,
+ min: Power,
+ max: Power,
+ ) extends ProvideFlexOptions {
+
+ /** Checks whether given power fits within the min-max interval and thus
+ * would be a feasible solution
+ * @param power
+ * The active power to check against the flex options
+ * @return
+ * Whether the given power is within the min-max interval or not
+ */
+ def fits(power: Power): Boolean =
+ min <= power && power <= max
+ }
+
+ object ProvideMinMaxFlexOptions {
+
+ /** Creates a [[ProvideMinMaxFlexOptions]] message with sanity checks
+ * regarding the power values
+ *
+ * @param modelUuid
+ * The UUID of the flex options provider asset model
+ * @param ref
+ * The reference active power that the flex options provider would
+ * produce/consume regularly at the current tick, i.e. if it was not
+ * flex-controlled
+ * @param min
+ * The minimum active power that the flex options provider allows at the
+ * current tick
+ * @param max
+ * The maximum active power that the flex options provider allows at the
+ * current tick
+ * @return
+ * The [[ProvideMinMaxFlexOptions]] message
+ */
+ def apply(
+ modelUuid: UUID,
+ ref: Power,
+ min: Power,
+ max: Power,
+ ): ProvideMinMaxFlexOptions = {
+ if (min > ref)
+ throw new CriticalFailureException(
+ s"Minimum power $min is greater than reference power $ref"
+ )
+
+ if (ref > max)
+ throw new CriticalFailureException(
+ s"Reference power $ref is greater than maximum power $max"
+ )
+
+ new ProvideMinMaxFlexOptions(modelUuid, ref, min, max)
+ }
+
+ /** Creates a [[ProvideMinMaxFlexOptions]] message that does not allow any
+ * flexibility, meaning that min = ref = max power.
+ *
+ * @param modelUuid
+ * The UUID of the flex provider asset model
+ * @param power
+ * The active power that the flex provider requires
+ * @return
+ * The corresponding [[ProvideMinMaxFlexOptions]] message
+ */
+ def noFlexOption(
+ modelUuid: UUID,
+ power: Power,
+ ): ProvideMinMaxFlexOptions =
+ ProvideMinMaxFlexOptions(modelUuid, power, power, power)
+ }
+}
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala
index 760a47296c..05a5be6886 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/EvMessage.scala
@@ -7,12 +7,13 @@
package edu.ie3.simona.ontology.messages.services
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
-import edu.ie3.simona.api.data.ev.model.EvModel
+import edu.ie3.simona.model.participant.evcs.EvModelWrapper
import edu.ie3.simona.ontology.messages.services.ServiceMessage.{
ProvisionMessage,
- ServiceRegistrationMessage
+ ServiceRegistrationMessage,
}
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
+import org.apache.pekko.actor.ActorRef
import java.util.UUID
@@ -45,19 +46,19 @@ object EvMessage {
*/
final case class ProvideEvDataMessage(
override val tick: Long,
+ override val serviceRef: ActorRef,
override val data: EvData,
override val nextDataTick: Option[Long] = None,
- override val unlockKey: Option[ScheduleKey] = None
+ override val unlockKey: Option[ScheduleKey] = None,
) extends EvMessage
with ProvisionMessage[EvData]
/** Requests number of free lots from evcs
+ *
* @param tick
* The latest tick that the data is requested for
*/
- final case class EvFreeLotsRequest(
- tick: Long
- )
+ final case class EvFreeLotsRequest(tick: Long)
/** Requests EV models of departing EVs with given UUIDs
*
@@ -73,20 +74,20 @@ object EvMessage {
* @param arrivals
* EVs arriving at the charging station
*/
-
final case class ArrivingEvsData(
- arrivals: Seq[EvModel]
+ arrivals: Seq[EvModelWrapper]
) extends EvData {}
trait EvResponseMessage extends EvMessage
final case class FreeLotsResponse(
evcs: UUID,
- freeLots: Int
+ freeLots: Int,
) extends EvResponseMessage
final case class DepartingEvsResponse(
evcs: UUID,
- evModels: Set[EvModel]
+ evModels: Seq[EvModelWrapper],
) extends EvResponseMessage
+
}
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala
index fd1d5b1cbd..73aa482b64 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/PrimaryDataMessage.scala
@@ -9,10 +9,11 @@ package edu.ie3.simona.ontology.messages.services
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.ontology.messages.services.ServiceMessage.ProvisionMessage
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
+import org.apache.pekko.actor.ActorRef
sealed trait PrimaryDataMessage
-case object PrimaryDataMessage {
+object PrimaryDataMessage {
/** Provides primary data in the form of [[ApparentPower]]
*
@@ -26,9 +27,10 @@ case object PrimaryDataMessage {
@deprecated
final case class ApparentPowerProvisionMessage(
override val tick: Long,
+ override val serviceRef: ActorRef,
override val data: ApparentPower,
override val nextDataTick: Option[Long],
- override val unlockKey: Option[ScheduleKey] = None
+ override val unlockKey: Option[ScheduleKey] = None,
) extends ProvisionMessage[ApparentPower]
with PrimaryDataMessage
}
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala
index 6d31cd8a8a..33946b669f 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/ServiceMessage.scala
@@ -41,22 +41,28 @@ case object ServiceMessage {
final case class WorkerRegistrationMessage(requestingActor: ActorRef)
extends ServiceRegistrationMessage
- sealed trait RegistrationResponseMessage extends ServiceMessage
+ sealed trait RegistrationResponseMessage extends ServiceMessage {
+ val serviceRef: ActorRef
+ }
- case object RegistrationResponseMessage {
+ object RegistrationResponseMessage {
/** Message, that is used to confirm a successful registration
*/
- final case class RegistrationSuccessfulMessage(nextDataTick: Option[Long])
- extends RegistrationResponseMessage
+ final case class RegistrationSuccessfulMessage(
+ override val serviceRef: ActorRef,
+ nextDataTick: Option[Long],
+ ) extends RegistrationResponseMessage
/** Message, that is used to announce a failed registration
*/
- case object RegistrationFailedMessage extends RegistrationResponseMessage
+ final case class RegistrationFailedMessage(
+ override val serviceRef: ActorRef
+ ) extends RegistrationResponseMessage
final case class ScheduleServiceActivation(
tick: Long,
- unlockKey: ScheduleKey
+ unlockKey: ScheduleKey,
)
}
@@ -67,6 +73,7 @@ case object ServiceMessage {
*/
trait ProvisionMessage[D <: Data] extends ServiceMessage {
val tick: Long
+ val serviceRef: ActorRef
val data: D
val nextDataTick: Option[Long]
val unlockKey: Option[ScheduleKey]
diff --git a/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala b/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala
index 9d9d4ee5f6..9d17284d63 100644
--- a/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala
+++ b/src/main/scala/edu/ie3/simona/ontology/messages/services/WeatherMessage.scala
@@ -9,10 +9,11 @@ package edu.ie3.simona.ontology.messages.services
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.ontology.messages.services.ServiceMessage.{
ProvisionMessage,
- ServiceRegistrationMessage
+ ServiceRegistrationMessage,
}
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.util.scala.quantities.Irradiance
+import org.apache.pekko.actor.ActorRef
import squants.{Temperature, Velocity}
sealed trait WeatherMessage
@@ -35,7 +36,7 @@ object WeatherMessage {
*/
final case class RegisterForWeatherMessage(
latitude: Double,
- longitude: Double
+ longitude: Double,
) extends WeatherMessage
with ServiceRegistrationMessage
@@ -50,9 +51,10 @@ object WeatherMessage {
*/
final case class ProvideWeatherMessage(
override val tick: Long,
+ override val serviceRef: ActorRef,
override val data: WeatherData,
override val nextDataTick: Option[Long],
- override val unlockKey: Option[ScheduleKey] = None
+ override val unlockKey: Option[ScheduleKey] = None,
) extends WeatherMessage
with ProvisionMessage[WeatherData]
@@ -72,7 +74,7 @@ object WeatherMessage {
diffIrr: Irradiance,
dirIrr: Irradiance,
temp: Temperature,
- windVel: Velocity
+ windVel: Velocity,
) extends SecondaryData
}
diff --git a/src/main/scala/edu/ie3/simona/scheduler/RuntimeNotifier.scala b/src/main/scala/edu/ie3/simona/scheduler/RuntimeNotifier.scala
index f80011fb2f..844021c530 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/RuntimeNotifier.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/RuntimeNotifier.scala
@@ -9,7 +9,7 @@ package edu.ie3.simona.scheduler
import org.apache.pekko.actor.typed.ActorRef
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.event.RuntimeEvent._
-import edu.ie3.simona.scheduler.RuntimeNotifier.now
+import edu.ie3.simona.scheduler.RuntimeNotifier._
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
/** Determines runtime events at different stages of the simulation and notifies
@@ -28,15 +28,15 @@ import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
* Time in milliseconds when the simulation was last started (before
* initialization or after a pause)
* @param lastCheckWindowTime
- * Time when in milliseconds when
+ * Time in milliseconds when the last check window was passed
*/
final case class RuntimeNotifier(
- eventListener: ActorRef[RuntimeEvent],
- readyCheckWindow: Option[Int],
- lastCheck: Long = -1,
- simStartTime: Long = -1,
- lastStartTime: Long = -1,
- lastCheckWindowTime: Long = -1
+ private val eventListener: ActorRef[RuntimeEvent],
+ private val readyCheckWindow: Option[Int],
+ private val lastCheck: Option[Long] = None,
+ private val simStartTime: Option[Long] = None,
+ private val lastStartTime: Option[Long] = None,
+ private val lastCheckWindowTime: Option[Long] = None,
) {
/** Notifier listeners that simulation has started or continued with given
@@ -53,7 +53,7 @@ final case class RuntimeNotifier(
def starting(
tick: Long,
pauseTick: Option[Long],
- endTick: Long
+ endTick: Long,
): RuntimeNotifier = {
val nowTime = now()
@@ -65,10 +65,15 @@ final case class RuntimeNotifier(
Simulating(tick, pauseOrEndTick)
})
- if (tick > 0L)
- copy(lastStartTime = nowTime)
+ if (simStartTime.nonEmpty)
+ // Has been started before: resuming simulation
+ copy(lastStartTime = Some(nowTime), lastCheckWindowTime = Some(nowTime))
else
- copy(simStartTime = nowTime, lastStartTime = nowTime)
+ copy(
+ simStartTime = Some(nowTime),
+ lastStartTime = Some(nowTime),
+ lastCheckWindowTime = Some(nowTime),
+ )
}
/** Notifier listeners that simulation is pausing at given tick
@@ -78,7 +83,7 @@ final case class RuntimeNotifier(
* Updated notifier
*/
def pausing(pauseTick: Long): RuntimeNotifier = {
- notify(Ready(pauseTick, now() - simStartTime))
+ notify(Ready(pauseTick, duration(simStartTime)))
this
}
@@ -94,35 +99,33 @@ final case class RuntimeNotifier(
def completing(completedTick: Long): RuntimeNotifier = {
val nowTime = now()
- // check if InitComplete should be sent, then adjust lastCheck
- val adjustedLastCheck = if (lastCheck <= -1) {
- if (completedTick >= INIT_SIM_TICK)
- notify(InitComplete(nowTime - simStartTime))
- 0
- } else
- lastCheck
+ // first tick (usually INIT_SIM_TICK) has completed
+ if (lastCheck.isEmpty)
+ notify(InitComplete(duration(simStartTime)))
+
+ // start with 0 if this is the first completed tick
+ val adjustedLastCheck = lastCheck.getOrElse(0L)
readyCheckWindow
.flatMap { checkWindow =>
val completedWindows =
(adjustedLastCheck + checkWindow) to completedTick by checkWindow
+ completedWindows.foreach { tick =>
+ notify(CheckWindowPassed(tick, duration(lastStartTime, nowTime)))
+ }
+
completedWindows.lastOption
.map { lastPassedCheck =>
- // at least one window has been passed
- completedWindows.foreach { tick =>
- notify(CheckWindowPassed(tick, nowTime - lastCheckWindowTime))
- }
-
copy(
- lastCheck = lastPassedCheck,
- lastCheckWindowTime = nowTime
+ lastCheck = Some(lastPassedCheck),
+ lastCheckWindowTime = Some(nowTime),
)
}
}
.getOrElse {
// no check window set or no windows passed
- copy(lastCheck = adjustedLastCheck)
+ copy(lastCheck = Some(adjustedLastCheck))
}
}
@@ -135,8 +138,8 @@ final case class RuntimeNotifier(
notify(
Done(
endTick,
- simStartTime - now(),
- errorInSim = false
+ duration(simStartTime),
+ errorInSim = false,
)
)
}
@@ -156,8 +159,8 @@ final case class RuntimeNotifier(
notify(
Done(
endTick,
- simStartTime - now(),
- errorInSim = true
+ duration(simStartTime),
+ errorInSim = true,
)
)
}
@@ -169,6 +172,23 @@ final case class RuntimeNotifier(
object RuntimeNotifier {
+ /** Returns the duration from given interval start to end, with a fallback
+ * duration of 0 if the start is not (yet) set (which can happen in edge
+ * cases, e.g. simulation failed and aborts)
+ *
+ * @param intervalStart
+ * Optional start of the interval
+ * @param intervalEnd
+ * The end of the interval
+ * @return
+ * The duration in milliseconds
+ */
+ private def duration(
+ intervalStart: Option[Long],
+ intervalEnd: Long = now(),
+ ): Long =
+ intervalStart.map(intervalEnd - _).getOrElse(0)
+
/** @return
* Current time in milliseconds
*/
diff --git a/src/main/scala/edu/ie3/simona/scheduler/ScheduleLock.scala b/src/main/scala/edu/ie3/simona/scheduler/ScheduleLock.scala
index e9ca91c0d1..803d223987 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/ScheduleLock.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/ScheduleLock.scala
@@ -11,7 +11,7 @@ import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
import org.apache.pekko.actor.typed.{ActorRef, Behavior, Scheduler}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
@@ -38,7 +38,7 @@ object ScheduleLock {
private def lockAdapter(
lock: ActorRef[LockMsg],
- expectedTick: Long
+ expectedTick: Long,
): Behavior[Activation] =
Behaviors.receive { case (ctx, Activation(tick)) =>
if (tick == expectedTick)
@@ -95,7 +95,7 @@ object ScheduleLock {
def singleKey(
ctx: ActorContext[_],
scheduler: ActorRef[SchedulerMessage],
- tick: Long
+ tick: Long,
): ScheduleKey =
singleKey(TypedSpawner(ctx), scheduler, tick)
@@ -114,7 +114,7 @@ object ScheduleLock {
def singleKey(
ctx: org.apache.pekko.actor.ActorContext,
scheduler: ActorRef[SchedulerMessage],
- tick: Long
+ tick: Long,
): ScheduleKey =
singleKey(ClassicSpawner(ctx), scheduler, tick)
@@ -133,7 +133,7 @@ object ScheduleLock {
def singleKey(
spawner: Spawner,
scheduler: ActorRef[SchedulerMessage],
- tick: Long
+ tick: Long,
): ScheduleKey =
multiKey(spawner, scheduler, tick, 1).headOption.getOrElse(
throw new RuntimeException("Should not happen")
@@ -157,7 +157,7 @@ object ScheduleLock {
ctx: ActorContext[_],
scheduler: ActorRef[SchedulerMessage],
tick: Long,
- count: Int
+ count: Int,
): Iterable[ScheduleKey] =
multiKey(TypedSpawner(ctx), scheduler, tick, count)
@@ -179,7 +179,7 @@ object ScheduleLock {
ctx: org.apache.pekko.actor.ActorContext,
scheduler: ActorRef[SchedulerMessage],
tick: Long,
- count: Int
+ count: Int,
): Iterable[ScheduleKey] =
multiKey(ClassicSpawner(ctx), scheduler, tick, count)
@@ -201,7 +201,7 @@ object ScheduleLock {
spawner: Spawner,
scheduler: ActorRef[SchedulerMessage],
tick: Long,
- count: Int
+ count: Int,
): Iterable[ScheduleKey] = {
val keys = (1 to count).map(_ => UUID.randomUUID())
@@ -227,7 +227,7 @@ object ScheduleLock {
*/
private def apply(
scheduler: ActorRef[SchedulerMessage],
- awaitedKeys: Set[UUID]
+ awaitedKeys: Set[UUID],
): Behavior[LockMsg] =
Behaviors.withStash(100) { buffer =>
Behaviors.receiveMessage {
@@ -244,7 +244,7 @@ object ScheduleLock {
private def uninitialized(
scheduler: ActorRef[SchedulerMessage],
awaitedKeys: Set[UUID],
- adapter: ActorRef[Activation]
+ adapter: ActorRef[Activation],
): Behavior[LockMsg] =
Behaviors.withStash(100) { buffer =>
Behaviors.receiveMessage {
@@ -261,7 +261,7 @@ object ScheduleLock {
private def active(
scheduler: ActorRef[SchedulerMessage],
awaitedKeys: Set[UUID],
- adapter: ActorRef[Activation]
+ adapter: ActorRef[Activation],
): Behavior[LockMsg] = Behaviors.receiveMessage { case Unlock(key) =>
val updatedKeys = awaitedKeys - key
diff --git a/src/main/scala/edu/ie3/simona/scheduler/Scheduler.scala b/src/main/scala/edu/ie3/simona/scheduler/Scheduler.scala
index bac0df01ae..7339fbbe61 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/Scheduler.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/Scheduler.scala
@@ -6,20 +6,21 @@
package edu.ie3.simona.scheduler
-import org.apache.pekko.actor.typed.scaladsl.Behaviors
-import org.apache.pekko.actor.typed.{ActorRef, Behavior}
import edu.ie3.simona.actor.ActorUtil.stopOnError
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.core.Core.{
ActiveCore,
CoreFactory,
- InactiveCore
+ InactiveCore,
}
import edu.ie3.simona.scheduler.core.RegularSchedulerCore
+import edu.ie3.util.scala.Scope
+import org.apache.pekko.actor.typed.scaladsl.Behaviors
+import org.apache.pekko.actor.typed.{ActorRef, Behavior}
/** Scheduler that activates actors at specific ticks and keeps them
* synchronized by waiting for the completions of all activations. Can be
@@ -27,10 +28,10 @@ import edu.ie3.simona.scheduler.core.RegularSchedulerCore
*/
object Scheduler {
- trait Incoming
+ trait Request
private final case class WrappedActivation(activation: Activation)
- extends Incoming
+ extends Request
/** Creates a new scheduler with given parent and core. The scheduler starts
* in the inactive state.
@@ -42,116 +43,94 @@ object Scheduler {
*/
def apply(
parent: ActorRef[SchedulerMessage],
- coreFactory: CoreFactory = RegularSchedulerCore
- ): Behavior[Incoming] = Behaviors.setup { ctx =>
+ coreFactory: CoreFactory = RegularSchedulerCore,
+ ): Behavior[Request] = Behaviors.setup { ctx =>
val adapter =
- ctx.messageAdapter[Activation](msg => WrappedActivation(msg))
+ ctx.messageAdapter[Activation](WrappedActivation)
inactive(
SchedulerData(parent, adapter),
- coreFactory.create()
+ coreFactory.create(),
)
}
private def inactive(
data: SchedulerData,
- core: InactiveCore
- ): Behavior[Incoming] =
+ core: InactiveCore,
+ ): Behavior[Request] =
Behaviors.receive {
- case (ctx, WrappedActivation(Activation(tick))) =>
- if (core.checkActivation(tick)) {
- val (toActivate, activeCore) = core.activate().takeNewActivations()
+ case (_, WrappedActivation(Activation(tick))) =>
+ val (toActivate, activeCore) = core
+ .activate(tick)
+ .takeNewActivations()
- toActivate.foreach { _ ! Activation(tick) }
+ toActivate.foreach { _ ! Activation(tick) }
- active(data, activeCore)
- } else {
- stopOnError(ctx, s"Cannot activate with new tick $tick")
- }
+ active(data, activeCore)
case (
- ctx,
- ScheduleActivation(actor, newTick, unlockKey)
+ _,
+ ScheduleActivation(actor, newTick, unlockKey),
) =>
- if (core.checkSchedule(newTick)) {
- val (maybeSchedule, newCore) = core.handleSchedule(actor, newTick)
-
- maybeSchedule match {
- case Some(scheduleTick) =>
- // also potentially schedule with parent if the new earliest tick is
- // different from the old earliest tick (including if nothing had
- // been scheduled before)
- data.parent ! ScheduleActivation(
- data.activationAdapter,
- scheduleTick,
- unlockKey
- )
- case None =>
- // we don't need to escalate to the parent, this means that we can release the lock (if applicable)
- unlockKey.foreach {
- _.unlock()
- }
- }
-
- inactive(data, newCore)
- } else {
- stopOnError(ctx, s"Cannot schedule an event at tick $newTick")
+ val (maybeSchedule, newCore) = core.handleSchedule(actor, newTick)
+ maybeSchedule match {
+ case Some(scheduleTick) =>
+ // also potentially schedule with parent if the new earliest tick is
+ // different from the old earliest tick (including if nothing had
+ // been scheduled before)
+ data.parent ! ScheduleActivation(
+ data.activationAdapter,
+ scheduleTick,
+ unlockKey,
+ )
+ case None =>
+ // we don't need to escalate to the parent, this means that we can release the lock (if applicable)
+ unlockKey.foreach {
+ _.unlock()
+ }
}
+
+ inactive(data, newCore)
+
case (ctx, unexpected) =>
stopOnError(
ctx,
- s"Received unexpected message $unexpected when inactive"
+ s"Received unexpected message $unexpected when inactive",
)
}
private def active(
data: SchedulerData,
- core: ActiveCore
- ): Behavior[Incoming] = Behaviors.receive {
+ core: ActiveCore,
+ ): Behavior[Request] = Behaviors.receive {
case (
- ctx,
- ScheduleActivation(actor, newTick, unlockKey)
+ _,
+ ScheduleActivation(actor, newTick, unlockKey),
) =>
- if (core.checkSchedule(actor, newTick)) {
- val (toActivate, newCore) =
- core.handleSchedule(actor, newTick).takeNewActivations()
-
- // if there's a lock:
- // since we're active and any scheduled activation can still influence our next activation,
- // we can directly unlock the lock with the key
- unlockKey.foreach {
- _.unlock()
- }
-
- toActivate.foreach {
- _ ! Activation(newCore.activeTick)
- }
+ val (toActivate, newCore) = core
+ .handleSchedule(actor, newTick)
+ .takeNewActivations()
+
+ // if there's a lock:
+ // since we're active and any scheduled activation can still influence our next activation,
+ // we can directly unlock the lock with the key
+ unlockKey.foreach {
+ _.unlock()
+ }
- active(data, newCore)
- } else {
- stopOnError(ctx, s"Cannot schedule an event at tick $newTick")
+ toActivate.foreach {
+ _ ! Activation(newCore.activeTick)
}
- case (ctx, Completion(actor, maybeNewTick)) =>
- Either
- .cond(
- core.checkCompletion(actor),
- core.handleCompletion(actor),
- s"Actor $actor is not part of the expected completing actors"
- )
- .flatMap { newCore =>
- // if successful
+ active(data, newCore)
+
+ case (_, Completion(actor, maybeNewTick)) =>
+ Scope(core.handleCompletion(actor))
+ .map { newCore =>
maybeNewTick
- .map { newTick =>
- Either
- .cond(
- newCore.checkSchedule(actor, newTick),
- newCore.handleSchedule(actor, newTick),
- s"Cannot schedule an event at tick $newTick for completing actor $actor"
- )
- }
- .getOrElse(Right(newCore))
+ .map(newCore.handleSchedule(actor, _))
+ .getOrElse(newCore)
}
.map { newCore =>
val (toActivate, updatedCore) = newCore.takeNewActivations()
@@ -167,7 +146,7 @@ object Scheduler {
.map { case (maybeScheduleTick, inactiveCore) =>
data.parent ! Completion(
data.activationAdapter,
- maybeScheduleTick
+ maybeScheduleTick,
)
inactive(data, inactiveCore)
}
@@ -175,10 +154,7 @@ object Scheduler {
active(data, newCore)
}
}
- .fold(
- stopOnError(ctx, _),
- identity
- )
+ .get
case (ctx, unexpected) =>
stopOnError(ctx, s"Received unexpected message $unexpected when active")
@@ -194,6 +170,7 @@ object Scheduler {
parent: ActorRef[
SchedulerMessage
],
- activationAdapter: ActorRef[Activation]
+ activationAdapter: ActorRef[Activation],
)
+
}
diff --git a/src/main/scala/edu/ie3/simona/scheduler/TimeAdvancer.scala b/src/main/scala/edu/ie3/simona/scheduler/TimeAdvancer.scala
index 5931cbb08a..ee6ae7f1a7 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/TimeAdvancer.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/TimeAdvancer.scala
@@ -6,24 +6,24 @@
package edu.ie3.simona.scheduler
-import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
-import org.apache.pekko.actor.typed.{ActorRef, Behavior}
import edu.ie3.simona.actor.ActorUtil.stopOnError
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
-import edu.ie3.simona.sim.SimMessage.{SimulationFailure, SimulationSuccessful}
+import edu.ie3.simona.sim.SimonaSim
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
+import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
+import org.apache.pekko.actor.typed.{ActorRef, Behavior}
/** Unit that is in control of time advancement within the simulation.
* Represents the root entity of any scheduler hierarchy.
*/
object TimeAdvancer {
- trait Incoming
+ trait Request
/** Starts simulation by activating the next (or first) tick
*
@@ -31,17 +31,9 @@ object TimeAdvancer {
* Last tick that can be activated or completed before the simulation is
* paused
*/
- final case class StartSimMessage(
+ final case class Start(
pauseTick: Option[Long] = None
- ) extends Incoming
-
- /** Notifies TimeAdvancer that the simulation should stop because of some
- * error
- *
- * @param errorMsg
- * The error message
- */
- final case class Stop(errorMsg: String) extends Incoming
+ ) extends Request
/** @param simulation
* The root actor of the simulation
@@ -53,21 +45,18 @@ object TimeAdvancer {
* last tick of the simulation
*/
def apply(
- simulation: org.apache.pekko.actor.ActorRef,
+ simulation: ActorRef[SimonaSim.SimulationEnded.type],
eventListener: Option[ActorRef[RuntimeEvent]],
checkWindow: Option[Int],
- endTick: Long
- ): Behavior[Incoming] = Behaviors.receivePartial {
+ endTick: Long,
+ ): Behavior[Request] = Behaviors.receivePartial {
case (_, ScheduleActivation(actor, tick, _)) =>
inactive(
TimeAdvancerData(simulation, actor, endTick),
eventListener.map(RuntimeNotifier(_, checkWindow)),
tick,
- tick
+ tick,
)
-
- case (ctx, Stop(errorMsg: String)) =>
- endWithFailure(ctx, simulation, None, INIT_SIM_TICK, errorMsg)
}
/** TimeAdvancer is inactive and waiting for a StartScheduleMessage to start
@@ -86,14 +75,14 @@ object TimeAdvancer {
data: TimeAdvancerData,
notifier: Option[RuntimeNotifier],
startingTick: Long,
- nextActiveTick: Long
- ): Behavior[Incoming] = Behaviors.receivePartial {
- case (_, StartSimMessage(pauseTick)) =>
+ nextActiveTick: Long,
+ ): Behavior[Request] = Behaviors.receivePartial {
+ case (_, Start(pauseTick)) =>
val updatedNotifier = notifier.map {
_.starting(
startingTick,
pauseTick,
- data.endTick
+ data.endTick,
)
}
@@ -103,11 +92,8 @@ object TimeAdvancer {
data,
updatedNotifier,
nextActiveTick,
- pauseTick
+ pauseTick,
)
-
- case (ctx, Stop(errorMsg: String)) =>
- endWithFailure(ctx, data.simulation, notifier, startingTick, errorMsg)
}
/** TimeAdvancer is active and waiting for the current activation of the
@@ -126,11 +112,11 @@ object TimeAdvancer {
data: TimeAdvancerData,
notifier: Option[RuntimeNotifier],
activeTick: Long,
- pauseTick: Option[Long]
- ): Behavior[Incoming] = Behaviors.receivePartial {
+ pauseTick: Option[Long],
+ ): Behavior[Request] = Behaviors.receivePartial {
case (ctx, Completion(_, maybeNewTick)) =>
checkCompletion(activeTick, maybeNewTick)
- .map(endWithFailure(ctx, data.simulation, notifier, activeTick, _))
+ .map(endWithFailure(ctx, notifier, activeTick, _))
.getOrElse {
(maybeNewTick, pauseTick) match {
@@ -152,7 +138,7 @@ object TimeAdvancer {
data,
updatedNotifier,
pauseTick + 1,
- newTick
+ newTick,
)
case (Some(newTick), _) =>
@@ -165,7 +151,7 @@ object TimeAdvancer {
notifierCompleted.starting(
newTick,
pauseTick,
- data.endTick
+ data.endTick,
)
else
notifierCompleted
@@ -177,7 +163,7 @@ object TimeAdvancer {
data,
updatedNotifier,
newTick,
- pauseTick
+ pauseTick,
)
case (None, _) =>
@@ -188,16 +174,13 @@ object TimeAdvancer {
}
}
- case (ctx, Stop(errorMsg: String)) =>
- endWithFailure(ctx, data.simulation, notifier, activeTick, errorMsg)
-
}
private def endSuccessfully(
data: TimeAdvancerData,
- notifier: Option[RuntimeNotifier]
- ): Behavior[Incoming] = {
- data.simulation ! SimulationSuccessful
+ notifier: Option[RuntimeNotifier],
+ ): Behavior[Request] = {
+ data.simulation ! SimonaSim.SimulationEnded
notifier.foreach {
// we do not want a check window message for the endTick
@@ -210,13 +193,11 @@ object TimeAdvancer {
}
private def endWithFailure(
- ctx: ActorContext[Incoming],
- simulation: org.apache.pekko.actor.ActorRef,
+ ctx: ActorContext[Request],
notifier: Option[RuntimeNotifier],
tick: Long,
- errorMsg: String
- ): Behavior[Incoming] = {
- simulation ! SimulationFailure
+ errorMsg: String,
+ ): Behavior[Request] = {
notifier.foreach(_.error(tick, errorMsg))
stopOnError(ctx, errorMsg)
@@ -224,7 +205,7 @@ object TimeAdvancer {
private def checkCompletion(
activeTick: Long,
- maybeNewTick: Option[Long]
+ maybeNewTick: Option[Long],
): Option[String] =
maybeNewTick.filter(_ <= activeTick).map { newTick =>
s"The next trigger has tick $newTick, although current active tick was $activeTick."
@@ -241,8 +222,8 @@ object TimeAdvancer {
* the last tick of the simulation
*/
private final case class TimeAdvancerData(
- simulation: org.apache.pekko.actor.ActorRef,
+ simulation: ActorRef[SimonaSim.SimulationEnded.type],
schedulee: ActorRef[Activation],
- endTick: Long
+ endTick: Long,
)
}
diff --git a/src/main/scala/edu/ie3/simona/scheduler/core/Core.scala b/src/main/scala/edu/ie3/simona/scheduler/core/Core.scala
index 7fa9220bb5..cfbe3a86fc 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/core/Core.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/core/Core.scala
@@ -31,40 +31,28 @@ object Core {
*/
trait InactiveCore {
- /** Checks whether an activation of the scheduler is valid for given tick.
- * This method cannot change the state of the inactive scheduler core.
+ /** Tries to handle an activation of the scheduler for given tick. If the
+ * activation for the tick is not valid, a
+ * [[edu.ie3.simona.exceptions.CriticalFailureException]] is thrown. If
+ * successful, an [[ActiveCore]] is returned with the active tick set to
+ * the earliest tick scheduled.
*
* @param newTick
* The tick that the scheduler is to be activated with
* @return
- * true if activation with given tick is allowed, false if not
+ * The changed [[ActiveCore]] that should be used for the activated
+ * scheduler
+ * @throws edu.ie3.simona.exceptions.CriticalFailureException
+ * on critical error
*/
- def checkActivation(newTick: Long): Boolean
-
- /** This method should be called when the scheduler is activated. An
- * [[ActiveCore]] is returned with the active tick set to the earliest tick
- * scheduled.
- *
- * @return
- * The [[ActiveCore]] that should be used for the activated scheduler
- */
- def activate(): ActiveCore
-
- /** Checks whether scheduling an activation of an actor for given tick is
- * valid. This method cannot change the state of the inactive scheduler
- * core.
- *
- * @param newTick
- * The tick that the actor wants to be scheduled for
- * @return
- * true if scheduling the activation is allowed, false if not
- */
- def checkSchedule(newTick: Long): Boolean
-
- /** Handles the scheduling of an activation of given actor for given tick.
- * If this activation scheduling makes a separate scheduling of the current
- * scheduler with its parent necessary, the tick that the scheduler needs
- * to be scheduled for is returned.
+ def activate(newTick: Long): ActiveCore
+
+ /** Tries to handle the scheduling of an activation of given actor for given
+ * tick. If scheduling for the tick is not valid, a
+ * [[edu.ie3.simona.exceptions.CriticalFailureException]] is thrown. If, on
+ * the other hand, the activation scheduling is successful and makes a
+ * separate scheduling of the current scheduler with its parent necessary,
+ * the tick that the scheduler needs to be scheduled for is returned.
*
* @param actor
* The actor to be scheduled
@@ -72,12 +60,15 @@ object Core {
* The tick that the actor is scheduled for
* @return
* A tuple of the optional tick that the current scheduler should be
- * scheduled for with its parent, and the changed [[InactiveCore]]
+ * scheduled for with its parent and the changed [[InactiveCore]]
+ * @throws edu.ie3.simona.exceptions.CriticalFailureException
+ * on critical error
*/
def handleSchedule(
actor: Actor,
- newTick: Long
+ newTick: Long,
): (Option[Long], InactiveCore)
+
}
/** Data structure holding relevant data and providing methods that handle
@@ -92,24 +83,16 @@ object Core {
*/
def activeTick: Long
- /** Checks whether completing an activation of given actor for the currently
- * active tick is valid. This method cannot change the state of the active
- * scheduler core.
- *
- * @param actor
- * The actor that wants to be complete its activation
- * @return
- * true if the completion is allowed, false if not
- */
- def checkCompletion(actor: Actor): Boolean
-
- /** Handles the completion of an activation of given actor for the currently
- * active tick.
+ /** Tries to handle the completion of an activation of given actor for the
+ * currently active tick. If the completion is not valid, a
+ * [[edu.ie3.simona.exceptions.CriticalFailureException]] is thrown.
*
* @param actor
* The actor whose activation should be completed
* @return
* The changed [[ActiveCore]]
+ * @throws edu.ie3.simona.exceptions.CriticalFailureException
+ * on critical error
*/
def handleCompletion(actor: Actor): ActiveCore
@@ -125,19 +108,9 @@ object Core {
*/
def maybeComplete(): Option[(Option[Long], InactiveCore)]
- /** Checks whether scheduling an activation of an actor for given tick is
- * valid. This method cannot change the state of the active scheduler core.
- *
- * @param actor
- * The actor that wants to be scheduled for the given tick
- * @param newTick
- * The tick that the actor wants to be scheduled for
- * @return
- * true if scheduling the activation is allowed, false if not
- */
- def checkSchedule(actor: Actor, newTick: Long): Boolean
-
- /** Handles the scheduling of an activation of given actor for given tick.
+ /** Tries to handle the scheduling of an activation of given actor for given
+ * tick. If the scheduling is not valid, a
+ * [[edu.ie3.simona.exceptions.CriticalFailureException]] is thrown.
*
* @param actor
* The actor to be scheduled
@@ -145,8 +118,13 @@ object Core {
* The tick that the actor is scheduled for
* @return
* The changed [[ActiveCore]]
+ * @throws edu.ie3.simona.exceptions.CriticalFailureException
+ * on critical error
*/
- def handleSchedule(actor: Actor, newTick: Long): ActiveCore
+ def handleSchedule(
+ actor: Actor,
+ newTick: Long,
+ ): ActiveCore
/** Removes and returns activations scheduled for the current tick, which
* can be sent out at the current moment.
diff --git a/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala b/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala
index c6384db107..eff9128068 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/core/PhaseSwitchCore.scala
@@ -6,11 +6,12 @@
package edu.ie3.simona.scheduler.core
+import edu.ie3.simona.exceptions.CriticalFailureException
import edu.ie3.simona.scheduler.core.Core.{
ActiveCore,
Actor,
CoreFactory,
- InactiveCore
+ InactiveCore,
}
import edu.ie3.util.scala.collection.immutable.PrioritySwitchBiSet
@@ -32,32 +33,42 @@ object PhaseSwitchCore extends CoreFactory {
final case class PhaseSwitchInactive private (
private val activationQueue: PrioritySwitchBiSet[Long, Actor],
- private val lastActiveTick: Option[Long]
+ private val lastActiveTick: Option[Long],
) extends InactiveCore {
- override def checkActivation(newTick: Long): Boolean =
- activationQueue.headKeyOption.contains(newTick)
-
- override def activate(): Core.ActiveCore = {
- val nextActiveTick = activationQueue.headKeyOption.getOrElse(
- throw new RuntimeException("Nothing scheduled, cannot activate.")
+ override def activate(newTick: Long): ActiveCore = {
+ val nextScheduledTick = activationQueue.headKeyOption.getOrElse(
+ throw new CriticalFailureException(
+ "No activation scheduled, cannot activate."
+ )
)
- PhaseSwitchActive(activationQueue, activeTick = nextActiveTick)
- }
- override def checkSchedule(newTick: Long): Boolean =
- lastActiveTick.forall(newTick >= _ + 1)
+ if (nextScheduledTick != newTick)
+ throw new CriticalFailureException(
+ s"Cannot activate with new tick $newTick because $nextScheduledTick is the next scheduled tick."
+ )
+
+ PhaseSwitchActive(activationQueue, activeTick = newTick)
+ }
override def handleSchedule(
actor: Actor,
- newTick: Long
+ newTick: Long,
): (Option[Long], InactiveCore) = {
+ lastActiveTick.filter(newTick < _).foreach { lastActive =>
+ throw new CriticalFailureException(
+ s"Cannot schedule an activation for $actor at tick $newTick because the last active tick is $lastActive"
+ )
+ }
+
val oldEarliestTick = activationQueue.headKeyOption
val updatedQueue = activationQueue.set(newTick, actor)
val newEarliestTick = updatedQueue.headKeyOption
val maybeScheduleTick =
- Option.when(newEarliestTick != oldEarliestTick)(newEarliestTick).flatten
+ Option
+ .when(newEarliestTick != oldEarliestTick)(newEarliestTick)
+ .flatten
(maybeScheduleTick, copy(activationQueue = updatedQueue))
}
@@ -68,14 +79,17 @@ object PhaseSwitchCore extends CoreFactory {
private val activationQueue: PrioritySwitchBiSet[Long, Actor],
activeTick: Long,
private val phase: Int = 0,
- private val activeActors: Set[Actor] = Set.empty
+ private val activeActors: Set[Actor] = Set.empty,
) extends ActiveCore {
- override def checkCompletion(actor: Actor): Boolean =
- activeActors.contains(actor)
+ override def handleCompletion(actor: Actor): ActiveCore = {
+ if (!activeActors.contains(actor))
+ throw new CriticalFailureException(
+ s"Actor $actor is not part of the expected completing actors"
+ )
- override def handleCompletion(actor: Actor): ActiveCore =
copy(activeActors = activeActors.excl(actor))
+ }
override def maybeComplete(): Option[(Option[Long], InactiveCore)] = {
Option.when(
@@ -85,22 +99,33 @@ object PhaseSwitchCore extends CoreFactory {
) {
(
activationQueue.headKeyOption,
- PhaseSwitchInactive(activationQueue, Some(activeTick))
+ PhaseSwitchInactive(activationQueue, Some(activeTick)),
)
}
}
- override def checkSchedule(actor: Actor, newTick: Long): Boolean = {
+ override def handleSchedule(
+ actor: Actor,
+ newTick: Long,
+ ): ActiveCore = {
if (newTick == activeTick) {
// what's done, is done: old phases are completed,
// thus they cannot handle new activation schedulings
- activationQueue.indexOf(actor).forall(_ >= phase)
- } else
- newTick > activeTick
- }
+ if (activationQueue.indexOf(actor).exists(_ < phase)) {
+ val activeActor = activationQueue.values(phase)
+ throw new CriticalFailureException(
+ s"Cannot schedule an activation at tick $newTick for $actor while actor $activeActor is active now"
+ )
+ }
+ } else {
+ if (newTick < activeTick)
+ throw new CriticalFailureException(
+ s"Cannot schedule an activation at tick $newTick for $actor while tick $activeTick is currently active"
+ )
+ }
- override def handleSchedule(actor: Actor, newTick: Long): ActiveCore =
copy(activationQueue = activationQueue.set(newTick, actor))
+ }
override def takeNewActivations(): (Iterable[Actor], ActiveCore) = {
Option
@@ -123,8 +148,8 @@ object PhaseSwitchCore extends CoreFactory {
copy(
activationQueue = updatedQueue,
phase = newPhase,
- activeActors = activeActors.incl(actor)
- )
+ activeActors = activeActors.incl(actor),
+ ),
)
}
.getOrElse((Iterable.empty, this))
diff --git a/src/main/scala/edu/ie3/simona/scheduler/core/RegularSchedulerCore.scala b/src/main/scala/edu/ie3/simona/scheduler/core/RegularSchedulerCore.scala
index cdc81cfdff..3cb7905219 100644
--- a/src/main/scala/edu/ie3/simona/scheduler/core/RegularSchedulerCore.scala
+++ b/src/main/scala/edu/ie3/simona/scheduler/core/RegularSchedulerCore.scala
@@ -6,11 +6,12 @@
package edu.ie3.simona.scheduler.core
+import edu.ie3.simona.exceptions.CriticalFailureException
import edu.ie3.simona.scheduler.core.Core.{
ActiveCore,
Actor,
CoreFactory,
- InactiveCore
+ InactiveCore,
}
import edu.ie3.util.scala.collection.mutable.PriorityMultiBiSet
@@ -24,32 +25,43 @@ object RegularSchedulerCore extends CoreFactory {
final case class SchedulerInactive private (
private val activationQueue: PriorityMultiBiSet[Long, Actor],
- private val lastActiveTick: Option[Long]
+ private val lastActiveTick: Option[Long],
) extends InactiveCore {
- override def checkActivation(newTick: Long): Boolean =
- activationQueue.headKeyOption.contains(newTick)
- override def activate(): ActiveCore = {
- val newActiveTick = activationQueue.headKeyOption.getOrElse(
- throw new RuntimeException("Nothing scheduled, cannot activate.")
+ override def activate(newTick: Long): ActiveCore = {
+ val nextScheduledTick = activationQueue.headKeyOption.getOrElse(
+ throw new CriticalFailureException(
+ "No activation scheduled, cannot activate."
+ )
)
- SchedulerActive(activationQueue, activeTick = newActiveTick)
- }
- override def checkSchedule(newTick: Long): Boolean =
- lastActiveTick.forall(newTick >= _ + 1)
+ if (nextScheduledTick != newTick)
+ throw new CriticalFailureException(
+ s"Cannot activate with new tick $newTick because $nextScheduledTick is the next scheduled tick."
+ )
+
+ SchedulerActive(activationQueue, activeTick = newTick)
+ }
override def handleSchedule(
actor: Actor,
- newTick: Long
+ newTick: Long,
): (Option[Long], InactiveCore) = {
+ lastActiveTick.filter(newTick <= _).foreach { lastActive =>
+ throw new CriticalFailureException(
+ s"Cannot schedule an activation for $actor at tick $newTick because the last active tick is $lastActive"
+ )
+ }
+
val oldEarliestTick = activationQueue.headKeyOption
activationQueue.set(newTick, actor)
val newEarliestTick = activationQueue.headKeyOption
val maybeScheduleTick =
- Option.when(newEarliestTick != oldEarliestTick)(newEarliestTick).flatten
+ Option
+ .when(newEarliestTick != oldEarliestTick)(newEarliestTick)
+ .flatten
(maybeScheduleTick, this)
}
@@ -59,13 +71,17 @@ object RegularSchedulerCore extends CoreFactory {
private final case class SchedulerActive(
private val activationQueue: PriorityMultiBiSet[Long, Actor],
private val activeActors: Set[Actor] = Set.empty,
- activeTick: Long
+ activeTick: Long,
) extends ActiveCore {
- override def checkCompletion(actor: Actor): Boolean =
- activeActors.contains(actor)
- override def handleCompletion(actor: Actor): ActiveCore =
+ override def handleCompletion(actor: Actor): ActiveCore = {
+ if (!activeActors.contains(actor))
+ throw new CriticalFailureException(
+ s"Actor $actor is not part of the expected completing actors"
+ )
+
copy(activeActors = activeActors.excl(actor))
+ }
override def maybeComplete(): Option[(Option[Long], InactiveCore)] =
Option.when(
@@ -74,14 +90,19 @@ object RegularSchedulerCore extends CoreFactory {
) {
(
activationQueue.headKeyOption,
- SchedulerInactive(activationQueue, Some(activeTick))
+ SchedulerInactive(activationQueue, Some(activeTick)),
)
}
- override def checkSchedule(actor: Actor, newTick: Long): Boolean =
- newTick >= activeTick
+ override def handleSchedule(
+ actor: Actor,
+ newTick: Long,
+ ): ActiveCore = {
+ if (newTick < activeTick)
+ throw new CriticalFailureException(
+ s"Cannot schedule an activation at tick $newTick"
+ )
- override def handleSchedule(actor: Actor, newTick: Long): ActiveCore = {
activationQueue.set(newTick, actor)
this
}
diff --git a/src/main/scala/edu/ie3/simona/service/SimonaService.scala b/src/main/scala/edu/ie3/simona/service/SimonaService.scala
index 89d60e57c4..1b41b30400 100644
--- a/src/main/scala/edu/ie3/simona/service/SimonaService.scala
+++ b/src/main/scala/edu/ie3/simona/service/SimonaService.scala
@@ -12,14 +12,14 @@ import edu.ie3.simona.logging.SimonaActorLogging
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.ScheduleServiceActivation
import edu.ie3.simona.ontology.messages.services.ServiceMessage.ServiceRegistrationMessage
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.service.ServiceStateData.{
InitializeServiceStateData,
- ServiceBaseStateData
+ ServiceBaseStateData,
}
import edu.ie3.simona.service.SimonaService.Create
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
@@ -35,7 +35,7 @@ object SimonaService {
*/
final case class Create[+I <: InitializeServiceStateData](
initializeStateData: I,
- unlockKey: ScheduleKey
+ unlockKey: ScheduleKey,
)
}
@@ -66,12 +66,12 @@ abstract class SimonaService[
case Create(
initializeStateData: InitializeServiceStateData,
- unlockKey: ScheduleKey
+ unlockKey: ScheduleKey,
) =>
scheduler ! ScheduleActivation(
self.toTyped,
INIT_SIM_TICK,
- Some(unlockKey)
+ Some(unlockKey),
)
context become initializing(initializeStateData)
@@ -103,7 +103,7 @@ abstract class SimonaService[
s"\nReceivedData: {}" +
s"\nException: {}",
initializeStateData,
- exception
+ exception,
)
throw exception // if a service fails startup we don't want to go on with the simulation
}
@@ -142,7 +142,7 @@ abstract class SimonaService[
"\nMsg: {}" +
"\nException: {}",
registrationMsg,
- exception
+ exception,
)
unhandled(registrationMsg)
}
@@ -151,7 +151,7 @@ abstract class SimonaService[
scheduler ! ScheduleActivation(
self.toTyped,
tick,
- Some(unlockKey)
+ Some(unlockKey),
)
// activity start trigger for this service
@@ -226,7 +226,7 @@ abstract class SimonaService[
*/
protected def announceInformation(tick: Long)(implicit
serviceStateData: S,
- ctx: ActorContext
+ ctx: ActorContext,
): (S, Option[Long])
}
diff --git a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala
index 4bd2975747..c827cb52fb 100644
--- a/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala
+++ b/src/main/scala/edu/ie3/simona/service/ev/ExtEvDataService.scala
@@ -14,19 +14,21 @@ import edu.ie3.simona.api.data.ev.ontology._
import edu.ie3.simona.api.data.ontology.DataMessageFromExt
import edu.ie3.simona.exceptions.WeatherServiceException.InvalidRegistrationRequestException
import edu.ie3.simona.exceptions.{InitializationException, ServiceException}
+import edu.ie3.simona.model.participant.evcs.EvModelWrapper
import edu.ie3.simona.ontology.messages.services.EvMessage._
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.ServiceRegistrationMessage
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.service.ServiceStateData.{
InitializeServiceStateData,
- ServiceBaseStateData
+ ServiceBaseStateData,
}
import edu.ie3.simona.service.ev.ExtEvDataService.{
ExtEvStateData,
- InitExtEvData
+ InitExtEvData,
}
import edu.ie3.simona.service.{ExtDataSupport, ServiceStateData, SimonaService}
+import edu.ie3.simona.util.ReceiveDataMap
import java.util.UUID
import scala.jdk.CollectionConverters._
@@ -43,15 +45,15 @@ object ExtEvDataService {
extEvData: ExtEvData,
uuidToActorRef: Map[UUID, ActorRef] = Map.empty[UUID, ActorRef],
extEvMessage: Option[EvDataMessageFromExt] = None,
- freeLots: Map[UUID, Option[Int]] = Map.empty,
- departingEvResponses: Map[UUID, Option[Seq[EvModel]]] = Map.empty
+ freeLots: ReceiveDataMap[UUID, Int] = ReceiveDataMap.empty,
+ departingEvResponses: ReceiveDataMap[UUID, Seq[EvModelWrapper]] =
+ ReceiveDataMap.empty,
) extends ServiceBaseStateData
final case class InitExtEvData(
extEvData: ExtEvData
) extends InitializeServiceStateData
- val FALLBACK_EV_MOVEMENTS_STEM_DISTANCE: Long = 3600L
}
class ExtEvDataService(override val scheduler: ActorRef)
@@ -63,7 +65,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
): Try[
(
ExtEvStateData,
- Option[Long]
+ Option[Long],
)
] =
initServiceData match {
@@ -74,7 +76,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
Success(
evInitializedStateData,
- None
+ None,
)
case invalidData =>
@@ -127,20 +129,20 @@ class ExtEvDataService(override val scheduler: ActorRef)
*/
private def handleRegistrationRequest(
agentToBeRegistered: ActorRef,
- evcs: UUID
+ evcs: UUID,
)(implicit
serviceStateData: ExtEvStateData
): ExtEvStateData = {
log.debug(
"Received ev movement service registration from {} for [Evcs:{}]",
agentToBeRegistered.path.name,
- evcs
+ evcs,
)
serviceStateData.uuidToActorRef.get(evcs) match {
case None =>
// Actor is not registered yet
- agentToBeRegistered ! RegistrationSuccessfulMessage(None)
+ agentToBeRegistered ! RegistrationSuccessfulMessage(self, None)
serviceStateData.copy(
uuidToActorRef =
@@ -150,7 +152,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
// actor is already registered, do nothing
log.warning(
"Sending actor {} is already registered",
- agentToBeRegistered
+ agentToBeRegistered,
)
serviceStateData
}
@@ -171,7 +173,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
tick: Long
)(implicit serviceStateData: ExtEvStateData, ctx: ActorContext): (
ExtEvStateData,
- Option[Long]
+ Option[Long],
) = {
serviceStateData.extEvMessage.getOrElse(
throw ServiceException(
@@ -185,7 +187,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
case arrivingEvsProvision: ProvideArrivingEvs =>
handleArrivingEvs(tick, arrivingEvsProvision.arrivals)(
serviceStateData,
- ctx
+ ctx,
)
}
}
@@ -197,10 +199,10 @@ class ExtEvDataService(override val scheduler: ActorRef)
evcsActor ! EvFreeLotsRequest(tick)
}
- val freeLots: Map[UUID, Option[Int]] =
+ val freeLots =
serviceStateData.uuidToActorRef.map { case (evcs, _) =>
- evcs -> None
- }
+ evcs
+ }.toSet
// if there are no evcs, we're sending response right away
if (freeLots.isEmpty)
@@ -209,36 +211,36 @@ class ExtEvDataService(override val scheduler: ActorRef)
(
serviceStateData.copy(
extEvMessage = None,
- freeLots = freeLots
+ freeLots = ReceiveDataMap(freeLots),
),
- None
+ None,
)
}
private def requestDepartingEvs(
tick: Long,
- requestedDepartingEvs: java.util.Map[UUID, java.util.List[UUID]]
+ requestedDepartingEvs: java.util.Map[UUID, java.util.List[UUID]],
)(implicit
serviceStateData: ExtEvStateData
): (ExtEvStateData, Option[Long]) = {
- val departingEvResponses: Map[UUID, Option[Seq[EvModel]]] =
+ val departingEvResponses =
requestedDepartingEvs.asScala.flatMap { case (evcs, departingEvs) =>
serviceStateData.uuidToActorRef.get(evcs) match {
case Some(evcsActor) =>
evcsActor ! DepartingEvsRequest(tick, departingEvs.asScala.toSeq)
- Some(evcs -> None)
+ Some(evcs)
case None =>
log.warning(
"A corresponding actor ref for UUID {} could not be found",
- evcs
+ evcs,
)
None
}
- }.toMap
+ }
// if there are no departing evs during this tick,
// we're sending response right away
@@ -248,28 +250,28 @@ class ExtEvDataService(override val scheduler: ActorRef)
(
serviceStateData.copy(
extEvMessage = None,
- departingEvResponses = departingEvResponses
+ departingEvResponses = ReceiveDataMap(departingEvResponses.toSet),
),
- None
+ None,
)
}
private def handleArrivingEvs(
tick: Long,
- allArrivingEvs: java.util.Map[UUID, java.util.List[EvModel]]
+ allArrivingEvs: java.util.Map[UUID, java.util.List[EvModel]],
)(implicit
serviceStateData: ExtEvStateData,
- ctx: ActorContext
+ ctx: ActorContext,
): (ExtEvStateData, Option[Long]) = {
val actorToEvs = allArrivingEvs.asScala.flatMap {
case (evcs, arrivingEvs) =>
serviceStateData.uuidToActorRef
.get(evcs)
- .map((_, arrivingEvs.asScala.toSeq))
+ .map((_, arrivingEvs.asScala.map(EvModelWrapper.apply).toSeq))
.orElse {
log.warning(
"A corresponding actor ref for UUID {} could not be found",
- evcs
+ evcs,
)
None
}
@@ -282,8 +284,9 @@ class ExtEvDataService(override val scheduler: ActorRef)
actorToEvs.zip(keys).foreach { case ((actor, arrivingEvs), key) =>
actor ! ProvideEvDataMessage(
tick,
+ self,
ArrivingEvsData(arrivingEvs),
- unlockKey = Some(key)
+ unlockKey = Some(key),
)
}
@@ -293,7 +296,7 @@ class ExtEvDataService(override val scheduler: ActorRef)
serviceStateData.copy(
extEvMessage = None
),
- None
+ None,
)
}
@@ -312,50 +315,51 @@ class ExtEvDataService(override val scheduler: ActorRef)
)(implicit serviceStateData: ExtEvStateData): ExtEvStateData = {
extResponseMsg match {
case DepartingEvsResponse(evcs, evModels) =>
- val updatedResponses = serviceStateData.departingEvResponses +
- (evcs -> Some(evModels.toList))
+ val updatedResponses =
+ serviceStateData.departingEvResponses.addData(evcs, evModels)
- if (updatedResponses.values.toSeq.contains(None)) {
+ if (updatedResponses.nonComplete) {
// responses are still incomplete
serviceStateData.copy(
departingEvResponses = updatedResponses
)
} else {
// all responses received, forward them to external simulation in a bundle
- val departingEvs = updatedResponses.values.flatten.flatten
+ val departingEvs =
+ updatedResponses.receivedData.values.flatten.map(_.unwrap())
serviceStateData.extEvData.queueExtResponseMsg(
new ProvideDepartingEvs(departingEvs.toList.asJava)
)
serviceStateData.copy(
- departingEvResponses = Map.empty
+ departingEvResponses = ReceiveDataMap.empty
)
}
case FreeLotsResponse(evcs, freeLots) =>
- val updatedFreeLots = serviceStateData.freeLots +
- (evcs -> Some(freeLots))
+ val updatedFreeLots = serviceStateData.freeLots.addData(evcs, freeLots)
- if (updatedFreeLots.values.toSeq.contains(None)) {
+ if (updatedFreeLots.nonComplete) {
// responses are still incomplete
serviceStateData.copy(
freeLots = updatedFreeLots
)
} else {
// all responses received, forward them to external simulation in a bundle
- val freeLotsResponse = updatedFreeLots.flatMap {
- case (evcs, Some(freeLotsCount)) if freeLotsCount > 0 =>
- Some(evcs -> Integer.valueOf(freeLotsCount))
- case _ =>
- None
- }
+ val freeLotsResponse = updatedFreeLots.receivedData
+ .filter { case (_, freeLotsCount) =>
+ freeLotsCount > 0
+ }
+ .map { case (evcs, freeLotsCount) =>
+ evcs -> Integer.valueOf(freeLotsCount)
+ }
serviceStateData.extEvData.queueExtResponseMsg(
new ProvideEvcsFreeLots(freeLotsResponse.asJava)
)
serviceStateData.copy(
- freeLots = Map.empty
+ freeLots = ReceiveDataMap.empty
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala
index 95103e347e..688ea5c5c3 100644
--- a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala
+++ b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceProxy.scala
@@ -14,19 +14,19 @@ import edu.ie3.datamodel.io.naming.timeseries.IndividualTimeSeriesMetaInformatio
import edu.ie3.datamodel.io.naming.{
DatabaseNamingStrategy,
EntityPersistenceNamingStrategy,
- FileNamingStrategy
+ FileNamingStrategy,
}
import edu.ie3.datamodel.io.source.csv.{
CsvTimeSeriesMappingSource,
- CsvTimeSeriesMetaInformationSource
+ CsvTimeSeriesMetaInformationSource,
}
import edu.ie3.datamodel.io.source.sql.{
SqlTimeSeriesMappingSource,
- SqlTimeSeriesMetaInformationSource
+ SqlTimeSeriesMetaInformationSource,
}
import edu.ie3.datamodel.io.source.{
TimeSeriesMappingSource,
- TimeSeriesMetaInformationSource
+ TimeSeriesMetaInformationSource,
}
import edu.ie3.datamodel.models.value.Value
import edu.ie3.simona.config.SimonaConfig.PrimaryDataCsvParams
@@ -36,7 +36,7 @@ import edu.ie3.simona.config.SimonaConfig.Simona.Input.{
}
import edu.ie3.simona.exceptions.{
InitializationException,
- InvalidConfigParameterException
+ InvalidConfigParameterException,
}
import edu.ie3.simona.logging.SimonaActorLogging
import edu.ie3.simona.ontology.messages.Activation
@@ -44,7 +44,7 @@ import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationFailedMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.{
PrimaryServiceRegistrationMessage,
- WorkerRegistrationMessage
+ WorkerRegistrationMessage,
}
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.service.{ServiceStateData, SimonaService}
@@ -52,12 +52,12 @@ import edu.ie3.simona.service.ServiceStateData.InitializeServiceStateData
import edu.ie3.simona.service.primary.PrimaryServiceProxy.{
InitPrimaryServiceProxyStateData,
PrimaryServiceStateData,
- SourceRef
+ SourceRef,
}
import edu.ie3.simona.service.primary.PrimaryServiceWorker.{
CsvInitPrimaryServiceStateData,
InitPrimaryServiceStateData,
- SqlInitPrimaryServiceStateData
+ SqlInitPrimaryServiceStateData,
}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
@@ -84,7 +84,7 @@ import scala.util.{Failure, Success, Try}
case class PrimaryServiceProxy(
scheduler: ActorRef,
initStateData: InitPrimaryServiceProxyStateData,
- private implicit val startDateTime: ZonedDateTime
+ private implicit val startDateTime: ZonedDateTime,
) extends Actor
with SimonaActorLogging {
@@ -106,7 +106,7 @@ case class PrimaryServiceProxy(
* messages */
prepareStateData(
initStateData.primaryConfig,
- initStateData.simulationStart
+ initStateData.simulationStart,
) match {
case Success(stateData) =>
scheduler ! Completion(self.toTyped)
@@ -114,7 +114,7 @@ case class PrimaryServiceProxy(
case Failure(exception) =>
log.error(
exception,
- s"Unable to initialize the $actorName. Shut it down."
+ s"Unable to initialize the $actorName. Shut it down.",
)
self ! PoisonPill
}
@@ -138,7 +138,7 @@ case class PrimaryServiceProxy(
*/
private def prepareStateData(
primaryConfig: PrimaryConfig,
- simulationStart: ZonedDateTime
+ simulationStart: ZonedDateTime,
): Try[PrimaryServiceStateData] = {
createSources(primaryConfig).map {
case (mappingSource, metaInformationSource) =>
@@ -161,7 +161,7 @@ case class PrimaryServiceProxy(
case None =>
log.warning(
"Unable to acquire meta information for time series '{}'. Leave that out.",
- timeSeriesUuid
+ timeSeriesUuid,
)
None
}
@@ -172,7 +172,7 @@ case class PrimaryServiceProxy(
timeSeriesToSourceRef,
simulationStart,
primaryConfig,
- mappingSource
+ mappingSource,
)
}
}
@@ -184,7 +184,7 @@ case class PrimaryServiceProxy(
primaryConfig.sqlParams,
primaryConfig.influxDb1xParams,
primaryConfig.csvParams,
- primaryConfig.couchbaseParams
+ primaryConfig.couchbaseParams,
).filter(_.isDefined).flatten.headOption match {
case Some(PrimaryDataCsvParams(csvSep, directoryPath, _, _)) =>
val fileNamingStrategy = new FileNamingStrategy()
@@ -192,31 +192,31 @@ case class PrimaryServiceProxy(
new CsvTimeSeriesMappingSource(
csvSep,
Paths.get(directoryPath),
- fileNamingStrategy
+ fileNamingStrategy,
),
new CsvTimeSeriesMetaInformationSource(
csvSep,
Paths.get(directoryPath),
- fileNamingStrategy
- )
+ fileNamingStrategy,
+ ),
)
case Some(sqlParams: SqlParams) =>
val sqlConnector = new SqlConnector(
sqlParams.jdbcUrl,
sqlParams.userName,
- sqlParams.password
+ sqlParams.password,
)
Success(
new SqlTimeSeriesMappingSource(
sqlConnector,
sqlParams.schemaName,
- new EntityPersistenceNamingStrategy()
+ new EntityPersistenceNamingStrategy(),
),
new SqlTimeSeriesMetaInformationSource(
sqlConnector,
sqlParams.schemaName,
- new DatabaseNamingStrategy()
- )
+ new DatabaseNamingStrategy(),
+ ),
)
case Some(x) =>
Failure(
@@ -253,14 +253,14 @@ case class PrimaryServiceProxy(
modelUuid,
timeSeriesUuid,
stateData,
- sender()
+ sender(),
)
case None =>
log.debug(
s"There is no time series apparent for the model with uuid '{}'.",
- modelUuid
+ modelUuid,
)
- sender() ! RegistrationFailedMessage
+ sender() ! RegistrationFailedMessage(self)
}
case x =>
log.error(
@@ -284,7 +284,7 @@ case class PrimaryServiceProxy(
modelUuid: UUID,
timeSeriesUuid: UUID,
stateData: PrimaryServiceStateData,
- requestingActor: ActorRef
+ requestingActor: ActorRef,
): Unit = {
val timeSeriesToSourceRef = stateData.timeSeriesToSourceRef
timeSeriesToSourceRef.get(timeSeriesUuid) match {
@@ -297,7 +297,7 @@ case class PrimaryServiceProxy(
initializeWorker(
metaInformation,
stateData.simulationStart,
- stateData.primaryConfig
+ stateData.primaryConfig,
) match {
case Success(workerRef) =>
/* Forward the registration request. The worker will reply about successful registration or not. */
@@ -311,9 +311,9 @@ case class PrimaryServiceProxy(
log.warning(
s"A failure occurred during spin-off of a primary source for time series '$timeSeriesUuid'. " +
s"Will inform the requesting actor, that registration is not possible.",
- exception
+ exception,
)
- requestingActor ! RegistrationFailedMessage
+ requestingActor ! RegistrationFailedMessage(self)
}
case None =>
@@ -321,7 +321,7 @@ case class PrimaryServiceProxy(
s"There is no source information for time series '$timeSeriesUuid' (requested for model " +
s"'$modelUuid'), although the mapping contains information about it."
)
- requestingActor ! RegistrationFailedMessage
+ requestingActor ! RegistrationFailedMessage(self)
}
}
@@ -340,21 +340,21 @@ case class PrimaryServiceProxy(
protected def initializeWorker(
metaInformation: IndividualTimeSeriesMetaInformation,
simulationStart: ZonedDateTime,
- primaryConfig: PrimaryConfig
+ primaryConfig: PrimaryConfig,
): Try[ActorRef] = {
val workerRef = classToWorkerRef(
metaInformation.getColumnScheme.getValueClass,
- metaInformation.getUuid.toString
+ metaInformation.getUuid.toString,
)
toInitData(
metaInformation,
simulationStart,
- primaryConfig
+ primaryConfig,
) match {
case Success(initData) =>
workerRef ! SimonaService.Create(
initData,
- ScheduleLock.singleKey(context, scheduler.toTyped, INIT_SIM_TICK)
+ ScheduleLock.singleKey(context, scheduler.toTyped, INIT_SIM_TICK),
)
Success(workerRef)
case Failure(cause) =>
@@ -362,7 +362,7 @@ case class PrimaryServiceProxy(
Failure(
new InitializationException(
"Unable to build init data for worker. Kill the uninitialized worker. Goodbye my friend!",
- cause
+ cause,
)
)
}
@@ -382,12 +382,12 @@ case class PrimaryServiceProxy(
*/
protected def classToWorkerRef[V <: Value](
valueClass: Class[V],
- timeSeriesUuid: String
+ timeSeriesUuid: String,
): ActorRef = {
import edu.ie3.simona.actor.SimonaActorNaming._
context.system.simonaActorOf(
PrimaryServiceWorker.props(scheduler, valueClass),
- timeSeriesUuid
+ timeSeriesUuid,
)
}
@@ -404,14 +404,14 @@ case class PrimaryServiceProxy(
private def toInitData(
metaInformation: IndividualTimeSeriesMetaInformation,
simulationStart: ZonedDateTime,
- primaryConfig: PrimaryConfig
+ primaryConfig: PrimaryConfig,
): Try[InitPrimaryServiceStateData] =
primaryConfig match {
case PrimaryConfig(
None,
Some(PrimaryDataCsvParams(csvSep, directoryPath, _, timePattern)),
None,
- None
+ None,
) =>
/* The actual data sources are from csv. Meta information have to match */
metaInformation match {
@@ -424,7 +424,7 @@ case class PrimaryServiceProxy(
Paths.get(directoryPath),
csvMetaData.getFullFilePath,
new FileNamingStrategy(),
- timePattern
+ timePattern,
)
)
case invalidMetaData =>
@@ -439,14 +439,14 @@ case class PrimaryServiceProxy(
None,
None,
None,
- Some(sqlParams: SqlParams)
+ Some(sqlParams: SqlParams),
) =>
Success(
SqlInitPrimaryServiceStateData(
metaInformation.getUuid,
simulationStart,
sqlParams,
- new DatabaseNamingStrategy()
+ new DatabaseNamingStrategy(),
)
)
@@ -472,18 +472,18 @@ case class PrimaryServiceProxy(
private def updateStateData(
stateData: PrimaryServiceStateData,
timeSeriesUuid: UUID,
- workerRef: ActorRef
+ workerRef: ActorRef,
): PrimaryServiceStateData = {
val timeSeriesToSourceRef = stateData.timeSeriesToSourceRef
val sourceRef = timeSeriesToSourceRef.getOrElse(
timeSeriesUuid,
throw new IllegalArgumentException(
s"Cannot update entry for time series '$timeSeriesUuid', as it hasn't been part of it before."
- )
+ ),
)
val updatedTimeSeriesToSourceRef = timeSeriesToSourceRef.updated(
timeSeriesUuid,
- sourceRef.copy(worker = Some(workerRef))
+ sourceRef.copy(worker = Some(workerRef)),
)
stateData.copy(timeSeriesToSourceRef = updatedTimeSeriesToSourceRef)
}
@@ -494,7 +494,7 @@ object PrimaryServiceProxy {
def props(
scheduler: ActorRef,
initStateData: InitPrimaryServiceProxyStateData,
- startDateTime: ZonedDateTime
+ startDateTime: ZonedDateTime,
): Props = Props(
new PrimaryServiceProxy(scheduler, initStateData, startDateTime)
)
@@ -509,7 +509,7 @@ object PrimaryServiceProxy {
*/
final case class InitPrimaryServiceProxyStateData(
primaryConfig: PrimaryConfig,
- simulationStart: ZonedDateTime
+ simulationStart: ZonedDateTime,
) extends InitializeServiceStateData
/** Holding the state of an initialized proxy.
@@ -530,7 +530,7 @@ object PrimaryServiceProxy {
timeSeriesToSourceRef: Map[UUID, SourceRef],
simulationStart: ZonedDateTime,
primaryConfig: PrimaryConfig,
- mappingSource: TimeSeriesMappingSource
+ mappingSource: TimeSeriesMappingSource,
) extends ServiceStateData
/** Giving reference to the target time series and source worker.
@@ -543,7 +543,7 @@ object PrimaryServiceProxy {
*/
final case class SourceRef(
metaInformation: IndividualTimeSeriesMetaInformation,
- worker: Option[ActorRef]
+ worker: Option[ActorRef],
)
/** Check if the config holds correct information to instantiate a mapping
@@ -574,7 +574,7 @@ object PrimaryServiceProxy {
primaryConfig.couchbaseParams,
primaryConfig.csvParams,
primaryConfig.influxDb1xParams,
- primaryConfig.sqlParams
+ primaryConfig.sqlParams,
).filter(_.isDefined).flatten
if (sourceConfigs.size > 1)
throw new InvalidConfigParameterException(
diff --git a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala
index f7d7d58fb5..7e5abcd6f9 100644
--- a/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala
+++ b/src/main/scala/edu/ie3/simona/service/primary/PrimaryServiceWorker.scala
@@ -25,11 +25,11 @@ import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResp
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.service.ServiceStateData.{
InitializeServiceStateData,
- ServiceActivationBaseStateData
+ ServiceActivationBaseStateData,
}
import edu.ie3.simona.service.primary.PrimaryServiceWorker.{
PrimaryServiceInitializedStateData,
- ProvidePrimaryDataMessage
+ ProvidePrimaryDataMessage,
}
import edu.ie3.simona.service.{ServiceStateData, SimonaService}
import edu.ie3.simona.util.TickUtil.{RichZonedDateTime, TickLong}
@@ -44,7 +44,7 @@ import scala.util.{Failure, Success, Try}
final case class PrimaryServiceWorker[V <: Value](
override protected val scheduler: ActorRef,
- valueClass: Class[V]
+ valueClass: Class[V],
) extends SimonaService[PrimaryServiceInitializedStateData[V]](scheduler) {
/** Initialize the actor with the given information. Try to figure out the
@@ -62,7 +62,7 @@ final case class PrimaryServiceWorker[V <: Value](
): Try[
(
PrimaryServiceInitializedStateData[V],
- Option[Long]
+ Option[Long],
)
] = {
(initServiceData match {
@@ -73,7 +73,7 @@ final case class PrimaryServiceWorker[V <: Value](
directoryPath,
filePath,
fileNamingStrategy,
- timePattern
+ timePattern,
) =>
Try {
/* Set up source and acquire information */
@@ -85,7 +85,7 @@ final case class PrimaryServiceWorker[V <: Value](
timeSeriesUuid,
filePath,
valueClass,
- factory
+ factory,
)
(source, simulationStart)
}
@@ -94,7 +94,7 @@ final case class PrimaryServiceWorker[V <: Value](
timeSeriesUuid: UUID,
simulationStart: ZonedDateTime,
sqlParams: SqlParams,
- namingStrategy: DatabaseNamingStrategy
+ namingStrategy: DatabaseNamingStrategy,
) =>
Try {
val factory =
@@ -103,7 +103,7 @@ final case class PrimaryServiceWorker[V <: Value](
val sqlConnector = new SqlConnector(
sqlParams.jdbcUrl,
sqlParams.userName,
- sqlParams.password
+ sqlParams.password,
)
val source = new SqlTimeSeriesSource(
@@ -112,7 +112,7 @@ final case class PrimaryServiceWorker[V <: Value](
namingStrategy,
timeSeriesUuid,
valueClass,
- factory
+ factory,
)
(source, simulationStart)
@@ -148,7 +148,7 @@ final case class PrimaryServiceWorker[V <: Value](
maybeNextTick,
furtherActivationTicks,
simulationStart,
- source
+ source,
)
(initializedStateData, maybeNextTick)
}
@@ -171,7 +171,8 @@ final case class PrimaryServiceWorker[V <: Value](
): Try[PrimaryServiceInitializedStateData[V]] = registrationMessage match {
case ServiceMessage.WorkerRegistrationMessage(requestingActor) =>
requestingActor ! RegistrationSuccessfulMessage(
- serviceStateData.maybeNextActivationTick
+ self,
+ serviceStateData.maybeNextActivationTick,
)
val subscribers = serviceStateData.subscribers :+ requestingActor
Success(serviceStateData.copy(subscribers = subscribers))
@@ -199,10 +200,10 @@ final case class PrimaryServiceWorker[V <: Value](
tick: Long
)(implicit
serviceBaseStateData: PrimaryServiceInitializedStateData[V],
- ctx: ActorContext
+ ctx: ActorContext,
): (
PrimaryServiceInitializedStateData[V],
- Option[Long]
+ Option[Long],
) = {
/* Get the information to distribute */
val wallClockTime = tick.toDateTime(serviceBaseStateData.startDateTime)
@@ -214,7 +215,7 @@ final case class PrimaryServiceWorker[V <: Value](
log.warning(
s"I expected to get data for tick '{}' ({}), but data is not available",
tick,
- wallClockTime
+ wallClockTime,
)
updateStateDataAndBuildTriggerMessages(serviceBaseStateData)
}
@@ -233,16 +234,16 @@ final case class PrimaryServiceWorker[V <: Value](
baseStateData: PrimaryServiceInitializedStateData[V]
): (
PrimaryServiceInitializedStateData[V],
- Option[Long]
+ Option[Long],
) = {
val (maybeNextActivationTick, remainderActivationTicks) =
baseStateData.activationTicks.pop
(
baseStateData.copy(
maybeNextActivationTick = maybeNextActivationTick,
- activationTicks = remainderActivationTicks
+ activationTicks = remainderActivationTicks,
),
- maybeNextActivationTick
+ maybeNextActivationTick,
)
}
@@ -261,10 +262,10 @@ final case class PrimaryServiceWorker[V <: Value](
private def processDataAndAnnounce(
tick: Long,
value: V,
- serviceBaseStateData: PrimaryServiceInitializedStateData[V]
+ serviceBaseStateData: PrimaryServiceInitializedStateData[V],
): (
PrimaryServiceInitializedStateData[V],
- Option[Long]
+ Option[Long],
) = value.toPrimaryData match {
case Success(primaryData) =>
announcePrimaryData(tick, primaryData, serviceBaseStateData)
@@ -273,7 +274,7 @@ final case class PrimaryServiceWorker[V <: Value](
log.warning(
"Unable to convert received value to primary data. Skipped that data." +
"\nException: {}",
- exception
+ exception,
)
updateStateDataAndBuildTriggerMessages(serviceBaseStateData)
}
@@ -293,21 +294,21 @@ final case class PrimaryServiceWorker[V <: Value](
private def announcePrimaryData(
tick: Long,
primaryData: PrimaryData,
- serviceBaseStateData: PrimaryServiceInitializedStateData[V]
+ serviceBaseStateData: PrimaryServiceInitializedStateData[V],
): (
PrimaryServiceInitializedStateData[V],
- Option[Long]
+ Option[Long],
) = {
val (maybeNextTick, remainderActivationTicks) =
serviceBaseStateData.activationTicks.pop
val updatedStateData =
serviceBaseStateData.copy(
maybeNextActivationTick = maybeNextTick,
- activationTicks = remainderActivationTicks
+ activationTicks = remainderActivationTicks,
)
val provisionMessage =
- ProvidePrimaryDataMessage(tick, primaryData, maybeNextTick)
+ ProvidePrimaryDataMessage(tick, self, primaryData, maybeNextTick)
serviceBaseStateData.subscribers.foreach(_ ! provisionMessage)
(updatedStateData, maybeNextTick)
}
@@ -322,12 +323,12 @@ object PrimaryServiceWorker {
ColumnScheme.ACTIVE_POWER,
ColumnScheme.ACTIVE_POWER_AND_HEAT_DEMAND,
ColumnScheme.APPARENT_POWER,
- ColumnScheme.APPARENT_POWER_AND_HEAT_DEMAND
+ ColumnScheme.APPARENT_POWER_AND_HEAT_DEMAND,
)
def props[V <: Value](
scheduler: ActorRef,
- valueClass: Class[V]
+ valueClass: Class[V],
): Props =
Props(new PrimaryServiceWorker(scheduler, valueClass))
@@ -368,7 +369,7 @@ object PrimaryServiceWorker {
directoryPath: Path,
filePath: Path,
fileNamingStrategy: FileNamingStrategy,
- timePattern: String
+ timePattern: String,
) extends InitPrimaryServiceStateData
/** Specific implementation of [[InitPrimaryServiceStateData]], if the source
@@ -387,7 +388,7 @@ object PrimaryServiceWorker {
override val timeSeriesUuid: UUID,
override val simulationStart: ZonedDateTime,
sqlParams: SqlParams,
- databaseNamingStrategy: DatabaseNamingStrategy
+ databaseNamingStrategy: DatabaseNamingStrategy,
) extends InitPrimaryServiceStateData
/** Class carrying the state of a fully initialized [[PrimaryServiceWorker]]
@@ -412,7 +413,7 @@ object PrimaryServiceWorker {
SortedDistinctSeq.empty,
startDateTime: ZonedDateTime,
source: TimeSeriesSource[V],
- subscribers: Vector[ActorRef] = Vector.empty[ActorRef]
+ subscribers: Vector[ActorRef] = Vector.empty[ActorRef],
) extends ServiceActivationBaseStateData
/** Provide primary data to subscribes
@@ -426,8 +427,9 @@ object PrimaryServiceWorker {
*/
final case class ProvidePrimaryDataMessage(
override val tick: Long,
+ override val serviceRef: ActorRef,
override val data: PrimaryData,
override val nextDataTick: Option[Long],
- override val unlockKey: Option[ScheduleKey] = None
+ override val unlockKey: Option[ScheduleKey] = None,
) extends ServiceMessage.ProvisionMessage[PrimaryData]
}
diff --git a/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala b/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala
index d8bf55443f..b4dfa209d4 100644
--- a/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala
+++ b/src/main/scala/edu/ie3/simona/service/weather/SampleWeatherSource.scala
@@ -50,7 +50,7 @@ final class SampleWeatherSource(
*/
override def getWeather(
tick: Long,
- weightedCoordinates: WeatherSource.WeightedCoordinates
+ weightedCoordinates: WeatherSource.WeightedCoordinates,
): WeatherData = getWeather(tick)
/** Get the weather data for the given tick and coordinate. Here, the weather
@@ -75,7 +75,7 @@ final class SampleWeatherSource(
) 2011
else wallClockTime.get(YEAR)
val index = (((year - 2011) * 288) + (month * 24) + hour) + 1
- val weatherResult = WeatherData(
+ WeatherData(
WattsPerSquareMeter(
SampleWeatherSource
.diffuseRadiation(index)
@@ -97,9 +97,8 @@ final class SampleWeatherSource(
SampleWeatherSource
.windVelocity(index)
.doubleValue
- )
+ ),
)
- weatherResult
}
/** Determine an Array with all ticks between the request frame's start and
@@ -114,7 +113,7 @@ final class SampleWeatherSource(
*/
override def getDataTicks(
requestFrameStart: Long,
- requestFrameEnd: Long
+ requestFrameEnd: Long,
): Array[Long] =
TickUtil.getTicksInBetween(requestFrameStart, requestFrameEnd, resolution)
}
@@ -138,13 +137,13 @@ object SampleWeatherSource {
override def getClosestCoordinates(
coordinate: Point,
n: Int,
- distance: ComparableQuantity[Length]
+ distance: ComparableQuantity[Length],
): util.List[CoordinateDistance] = {
if (coordinate.getY.abs <= 90 && coordinate.getX.abs <= 180)
Vector(
new CoordinateDistance(
coordinate,
- coordinate
+ coordinate,
)
).asJava
else
@@ -153,13 +152,13 @@ object SampleWeatherSource {
override def getNearestCoordinates(
coordinate: Point,
- i: Int
+ i: Int,
): util.List[CoordinateDistance] = {
if (coordinate.getY.abs <= 90 && coordinate.getX.abs <= 180)
Vector(
new CoordinateDistance(
coordinate,
- coordinate
+ coordinate,
)
).asJava
else
diff --git a/src/main/scala/edu/ie3/simona/service/weather/WeatherService.scala b/src/main/scala/edu/ie3/simona/service/weather/WeatherService.scala
index 12b2e6e2cb..7543dc74c8 100644
--- a/src/main/scala/edu/ie3/simona/service/weather/WeatherService.scala
+++ b/src/main/scala/edu/ie3/simona/service/weather/WeatherService.scala
@@ -12,22 +12,22 @@ import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.exceptions.WeatherServiceException.InvalidRegistrationRequestException
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.ServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.WeatherMessage._
import edu.ie3.simona.service.SimonaService
import edu.ie3.simona.service.ServiceStateData.{
InitializeServiceStateData,
- ServiceActivationBaseStateData
+ ServiceActivationBaseStateData,
}
import edu.ie3.simona.service.weather.WeatherService.{
InitWeatherServiceStateData,
- WeatherInitializedStateData
+ WeatherInitializedStateData,
}
import edu.ie3.simona.service.weather.WeatherSource.{
AgentCoordinates,
- WeightedCoordinates
+ WeightedCoordinates,
}
import edu.ie3.simona.util.SimonaConstants
import edu.ie3.simona.util.TickUtil.RichZonedDateTime
@@ -42,14 +42,14 @@ object WeatherService {
scheduler: ActorRef,
startDateTime: ZonedDateTime,
simulationEnd: ZonedDateTime,
- amountOfInterpolationCoordinates: Int = 4
+ amountOfInterpolationCoordinates: Int = 4,
): Props =
Props(
new WeatherService(
scheduler,
startDateTime,
simulationEnd,
- amountOfInterpolationCoordinates
+ amountOfInterpolationCoordinates,
)
)
@@ -74,7 +74,7 @@ object WeatherService {
Map.empty[AgentCoordinates, WeightedCoordinates],
override val maybeNextActivationTick: Option[Long],
override val activationTicks: SortedDistinctSeq[Long] =
- SortedDistinctSeq.empty
+ SortedDistinctSeq.empty,
) extends ServiceActivationBaseStateData
/** Weather service state data used for initialization of the weather service
@@ -99,7 +99,7 @@ final case class WeatherService(
override val scheduler: ActorRef,
private implicit val simulationStart: ZonedDateTime,
simulationEnd: ZonedDateTime,
- private val amountOfInterpolationCoords: Int
+ private val amountOfInterpolationCoords: Int,
) extends SimonaService[
WeatherInitializedStateData
](scheduler) {
@@ -131,7 +131,7 @@ final case class WeatherService(
weatherSource
.getDataTicks(
SimonaConstants.FIRST_TICK_IN_SIMULATION,
- simulationEnd.toTick
+ simulationEnd.toTick,
)
.toSeq
).pop
@@ -139,12 +139,12 @@ final case class WeatherService(
val weatherInitializedStateData = WeatherInitializedStateData(
weatherSource,
activationTicks = furtherActivationTicks,
- maybeNextActivationTick = maybeNextTick
+ maybeNextActivationTick = maybeNextTick,
)
Success(
weatherInitializedStateData,
- maybeNextTick
+ maybeNextTick,
)
case invalidData =>
@@ -200,7 +200,7 @@ final case class WeatherService(
private def handleRegistrationRequest(
agentToBeRegistered: ActorRef,
latitude: Double,
- longitude: Double
+ longitude: Double,
)(implicit
serviceStateData: WeatherInitializedStateData
): WeatherInitializedStateData = {
@@ -208,13 +208,13 @@ final case class WeatherService(
"Received weather registration from {} for [Lat:{}, Long:{}]",
agentToBeRegistered.path.name,
latitude,
- longitude
+ longitude,
)
// collate the provided coordinates into a single entity
val agentCoord = AgentCoordinates(
latitude,
- longitude
+ longitude,
)
serviceStateData.coordsToActorRefMap.get(agentCoord) match {
@@ -222,11 +222,12 @@ final case class WeatherService(
/* The coordinate itself is not known yet. Try to figure out, which weather coordinates are relevant */
serviceStateData.weatherSource.getWeightedCoordinates(
agentCoord,
- amountOfInterpolationCoords
+ amountOfInterpolationCoords,
) match {
case Success(weightedCoordinates) =>
agentToBeRegistered ! RegistrationSuccessfulMessage(
- serviceStateData.maybeNextActivationTick
+ self,
+ serviceStateData.maybeNextActivationTick,
)
/* Enhance the mapping from agent coordinate to requesting actor's ActorRef as well as the necessary
@@ -237,21 +238,22 @@ final case class WeatherService(
agentToBeRegistered
)),
weightedWeatherCoordinates =
- serviceStateData.weightedWeatherCoordinates + (agentCoord -> weightedCoordinates)
+ serviceStateData.weightedWeatherCoordinates + (agentCoord -> weightedCoordinates),
)
case Failure(exception) =>
log.error(
exception,
- s"Unable to obtain necessary information to register for coordinate $agentCoord."
+ s"Unable to obtain necessary information to register for coordinate $agentCoord.",
)
- sender() ! RegistrationFailedMessage
+ sender() ! RegistrationFailedMessage(self)
serviceStateData
}
case Some(actorRefs) if !actorRefs.contains(agentToBeRegistered) =>
// coordinate is already known (= we have data for it), but this actor is not registered yet
agentToBeRegistered ! RegistrationSuccessfulMessage(
- serviceStateData.maybeNextActivationTick
+ self,
+ serviceStateData.maybeNextActivationTick,
)
serviceStateData.copy(
@@ -263,14 +265,14 @@ final case class WeatherService(
// actor is already registered, do nothing
log.warning(
"Sending actor {} is already registered",
- agentToBeRegistered
+ agentToBeRegistered,
)
serviceStateData
case _ =>
// actor is not registered and we don't have data for it
// inform the agentToBeRegistered that the registration failed as we don't have data for it
- agentToBeRegistered ! RegistrationFailedMessage
+ agentToBeRegistered ! RegistrationFailedMessage(self)
serviceStateData
}
}
@@ -288,13 +290,13 @@ final case class WeatherService(
*/
override protected def announceInformation(tick: Long)(implicit
serviceStateData: WeatherInitializedStateData,
- ctx: ActorContext
+ ctx: ActorContext,
): (WeatherInitializedStateData, Option[Long]) = {
/* Pop the next activation tick and update the state data */
val (
maybeNextTick: Option[Long],
- updatedStateData: WeatherInitializedStateData
+ updatedStateData: WeatherInitializedStateData,
) = {
val (nextTick, remainderTicks) = serviceStateData.activationTicks.pop
(nextTick, serviceStateData.copy(activationTicks = remainderTicks))
@@ -310,14 +312,19 @@ final case class WeatherService(
.get(coordinate)
.foreach(recipients =>
recipients.foreach(
- _ ! ProvideWeatherMessage(tick, weatherResult, maybeNextTick)
+ _ ! ProvideWeatherMessage(
+ tick,
+ self,
+ weatherResult,
+ maybeNextTick,
+ )
)
)
}
(
updatedStateData,
- maybeNextTick
+ maybeNextTick,
)
}
diff --git a/src/main/scala/edu/ie3/simona/service/weather/WeatherSource.scala b/src/main/scala/edu/ie3/simona/service/weather/WeatherSource.scala
index 14838d6afb..b996d5a475 100644
--- a/src/main/scala/edu/ie3/simona/service/weather/WeatherSource.scala
+++ b/src/main/scala/edu/ie3/simona/service/weather/WeatherSource.scala
@@ -11,11 +11,11 @@ import edu.ie3.datamodel.io.factory.timeseries.{
CosmoIdCoordinateFactory,
IconIdCoordinateFactory,
IdCoordinateFactory,
- SqlIdCoordinateFactory
+ SqlIdCoordinateFactory,
}
import edu.ie3.datamodel.io.naming.FileNamingStrategy
import edu.ie3.datamodel.io.source.IdCoordinateSource
-import edu.ie3.datamodel.io.source.csv.CsvIdCoordinateSource
+import edu.ie3.datamodel.io.source.csv.{CsvDataSource, CsvIdCoordinateSource}
import edu.ie3.datamodel.io.source.sql.SqlIdCoordinateSource
import edu.ie3.datamodel.models.value.WeatherValue
import edu.ie3.simona.config.SimonaConfig
@@ -23,23 +23,22 @@ import edu.ie3.simona.config.SimonaConfig.BaseCsvParams
import edu.ie3.simona.config.SimonaConfig.Simona.Input.Weather.Datasource._
import edu.ie3.simona.exceptions.{
InvalidConfigParameterException,
- ServiceException
+ ServiceException,
}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
import edu.ie3.simona.service.weather.WeatherSource.{
AgentCoordinates,
- WeightedCoordinates
+ WeightedCoordinates,
}
import edu.ie3.simona.util.ConfigUtil.CsvConfigUtil.checkBaseCsvParams
import edu.ie3.simona.util.ConfigUtil.DatabaseConfigUtil.{
checkCouchbaseParams,
checkInfluxDb1xParams,
- checkSqlParams
+ checkSqlParams,
}
import edu.ie3.simona.util.ParsableEnumeration
import edu.ie3.util.geo.{CoordinateDistance, GeoUtils}
import edu.ie3.util.quantities.PowerSystemUnits
-import edu.ie3.util.scala.io.CsvDataSourceAdapter
import edu.ie3.util.scala.quantities.WattsPerSquareMeter
import org.locationtech.jts.geom.{Coordinate, Point}
import squants.motion.MetersPerSecond
@@ -73,11 +72,11 @@ trait WeatherSource {
*/
def getWeightedCoordinates(
coordinate: WeatherSource.AgentCoordinates,
- amountOfInterpolationCoords: Int
+ amountOfInterpolationCoords: Int,
): Try[WeatherSource.WeightedCoordinates] = {
getNearestCoordinatesWithDistances(
coordinate,
- amountOfInterpolationCoords
+ amountOfInterpolationCoords,
) match {
case Success(nearestCoordinates) =>
determineWeights(nearestCoordinates)
@@ -85,7 +84,7 @@ trait WeatherSource {
Failure(
new ServiceException(
"Determination of coordinate weights failed.",
- exception
+ exception,
)
)
}
@@ -105,7 +104,7 @@ trait WeatherSource {
*/
def getNearestCoordinatesWithDistances(
coordinate: WeatherSource.AgentCoordinates,
- amountOfInterpolationCoords: Int
+ amountOfInterpolationCoords: Int,
): Try[Iterable[CoordinateDistance]] = {
val queryPoint = coordinate.toPoint
@@ -114,7 +113,7 @@ trait WeatherSource {
.getClosestCoordinates(
queryPoint,
amountOfInterpolationCoords,
- maxCoordinateDistance
+ maxCoordinateDistance,
)
.asScala
@@ -153,7 +152,7 @@ trait WeatherSource {
tl || (point.getX < queryPoint.getX && point.getY > queryPoint.getY),
tr || (point.getX > queryPoint.getX && point.getY > queryPoint.getY),
bl || (point.getX < queryPoint.getX && point.getY < queryPoint.getY),
- br || (point.getX > queryPoint.getX && point.getY < queryPoint.getY)
+ br || (point.getX > queryPoint.getX && point.getY < queryPoint.getY),
)
}
@@ -253,7 +252,7 @@ trait WeatherSource {
*/
def getWeather(
tick: Long,
- weightedCoordinates: WeightedCoordinates
+ weightedCoordinates: WeightedCoordinates,
): WeatherData
/** Get the weather data for the given tick and agent coordinates having a
@@ -268,7 +267,7 @@ trait WeatherSource {
*/
def getWeather(
tick: Long,
- agentToWeightedCoordinates: Map[AgentCoordinates, WeightedCoordinates]
+ agentToWeightedCoordinates: Map[AgentCoordinates, WeightedCoordinates],
): Map[AgentCoordinates, WeatherData] = agentToWeightedCoordinates.map {
case (agentCoordinates, weightedCoordinates) =>
agentCoordinates -> getWeather(tick, weightedCoordinates)
@@ -287,7 +286,7 @@ trait WeatherSource {
*/
def getDataTicks(
requestFrameStart: Long,
- requestFrameEnd: Long
+ requestFrameEnd: Long,
): Array[Long]
}
@@ -295,7 +294,7 @@ object WeatherSource {
def apply(
dataSourceConfig: SimonaConfig.Simona.Input.Weather.Datasource,
- simulationStart: ZonedDateTime
+ simulationStart: ZonedDateTime,
): WeatherSource =
checkConfig(dataSourceConfig)(simulationStart)
@@ -335,7 +334,7 @@ object WeatherSource {
weatherDataSourceCfg.csvParams,
weatherDataSourceCfg.influxDb1xParams,
weatherDataSourceCfg.couchbaseParams,
- weatherDataSourceCfg.sqlParams
+ weatherDataSourceCfg.sqlParams,
).filter(_.isDefined)
val timestampPattern: Option[String] = weatherDataSourceCfg.timestampPattern
@@ -344,7 +343,7 @@ object WeatherSource {
val distance: ComparableQuantity[Length] =
Quantities.getQuantity(
weatherDataSourceCfg.maxCoordinateDistance,
- Units.METRE
+ Units.METRE,
)
// check that only one source is defined
@@ -353,81 +352,78 @@ object WeatherSource {
s"Multiple weather sources defined: '${definedWeatherSources.map(_.getClass.getSimpleName).mkString("\n\t")}'." +
s"Please define only one source!\nAvailable sources:\n\t${supportedWeatherSources.mkString("\n\t")}"
)
- val weatherSourceFunction: ZonedDateTime => WeatherSource =
- definedWeatherSources.headOption match {
- case Some(
- Some(baseCsvParams @ BaseCsvParams(csvSep, directoryPath, _))
- ) =>
- checkBaseCsvParams(baseCsvParams, "WeatherSource")
- (simulationStart: ZonedDateTime) =>
- WeatherSourceWrapper(
- csvSep,
- Paths.get(directoryPath),
- coordinateSourceFunction,
- timestampPattern,
- scheme,
- resolution,
- distance
- )(simulationStart)
- case Some(Some(params: CouchbaseParams)) =>
- checkCouchbaseParams(params)
- (simulationStart: ZonedDateTime) =>
- WeatherSourceWrapper(
- params,
- coordinateSourceFunction,
- timestampPattern,
- scheme,
- resolution,
- distance
- )(simulationStart)
- case Some(Some(params @ InfluxDb1xParams(database, _, url))) =>
- checkInfluxDb1xParams("WeatherSource", url, database)
- (simulationStart: ZonedDateTime) =>
- WeatherSourceWrapper(
- params,
- coordinateSourceFunction,
- timestampPattern,
- scheme,
- resolution,
- distance
- )(simulationStart)
- case Some(Some(params: SqlParams)) =>
- checkSqlParams(params)
- (simulationStart: ZonedDateTime) =>
- WeatherSourceWrapper(
- params,
- coordinateSourceFunction,
- timestampPattern,
- scheme,
- resolution,
- distance
- )(simulationStart)
- case Some(Some(_: SampleParams)) =>
- // sample weather, no check required
- // coordinate source must be sample coordinate source
- // calling the function here is not an issue as the sample coordinate source is already
- // an object (= no overhead costs)
- coordinateSourceFunction() match {
- case _: SampleWeatherSource.SampleIdCoordinateSource.type =>
- // all fine
- (simulationStart: ZonedDateTime) =>
- new SampleWeatherSource()(simulationStart)
- case coordinateSource =>
- // cannot use sample weather source with other combination of weather source than sample weather source
- throw new InvalidConfigParameterException(
- s"Invalid coordinate source " +
- s"'${coordinateSource.getClass.getSimpleName}' defined for SampleWeatherSource. " +
- "Please adapt the configuration to use sample coordinate source for weather data!"
- )
- }
- case None | Some(_) =>
- throw new InvalidConfigParameterException(
- s"No weather source defined! This is currently not supported! Please provide the config parameters for one " +
- s"of the following weather sources:\n\t${supportedWeatherSources.mkString("\n\t")}"
- )
- }
-
- weatherSourceFunction
+ definedWeatherSources.headOption match {
+ case Some(
+ Some(baseCsvParams @ BaseCsvParams(csvSep, directoryPath, _))
+ ) =>
+ checkBaseCsvParams(baseCsvParams, "WeatherSource")
+ (simulationStart: ZonedDateTime) =>
+ WeatherSourceWrapper(
+ csvSep,
+ Paths.get(directoryPath),
+ coordinateSourceFunction,
+ timestampPattern,
+ scheme,
+ resolution,
+ distance,
+ )(simulationStart)
+ case Some(Some(params: CouchbaseParams)) =>
+ checkCouchbaseParams(params)
+ (simulationStart: ZonedDateTime) =>
+ WeatherSourceWrapper(
+ params,
+ coordinateSourceFunction,
+ timestampPattern,
+ scheme,
+ resolution,
+ distance,
+ )(simulationStart)
+ case Some(Some(params @ InfluxDb1xParams(database, _, url))) =>
+ checkInfluxDb1xParams("WeatherSource", url, database)
+ (simulationStart: ZonedDateTime) =>
+ WeatherSourceWrapper(
+ params,
+ coordinateSourceFunction,
+ timestampPattern,
+ scheme,
+ resolution,
+ distance,
+ )(simulationStart)
+ case Some(Some(params: SqlParams)) =>
+ checkSqlParams(params)
+ (simulationStart: ZonedDateTime) =>
+ WeatherSourceWrapper(
+ params,
+ coordinateSourceFunction,
+ timestampPattern,
+ scheme,
+ resolution,
+ distance,
+ )(simulationStart)
+ case Some(Some(_: SampleParams)) =>
+ // sample weather, no check required
+ // coordinate source must be sample coordinate source
+ // calling the function here is not an issue as the sample coordinate source is already
+ // an object (= no overhead costs)
+ coordinateSourceFunction() match {
+ case _: SampleWeatherSource.SampleIdCoordinateSource.type =>
+ // all fine
+ (simulationStart: ZonedDateTime) =>
+ new SampleWeatherSource()(simulationStart)
+ case coordinateSource =>
+ // cannot use sample weather source with other combination of weather source than sample weather source
+ throw new InvalidConfigParameterException(
+ s"Invalid coordinate source " +
+ s"'${coordinateSource.getClass.getSimpleName}' defined for SampleWeatherSource. " +
+ "Please adapt the configuration to use sample coordinate source for weather data!"
+ )
+ }
+ case None | Some(_) =>
+ throw new InvalidConfigParameterException(
+ s"No weather source defined! This is currently not supported! Please provide the config parameters for one " +
+ s"of the following weather sources:\n\t${supportedWeatherSources.mkString("\n\t")}"
+ )
+ }
}
/** Check the provided coordinate id data source configuration to ensure its
@@ -448,7 +444,7 @@ object WeatherSource {
val definedCoordSources = Vector(
coordinateSourceConfig.sampleParams,
coordinateSourceConfig.csvParams,
- coordinateSourceConfig.sqlParams
+ coordinateSourceConfig.sqlParams,
).filter(_.isDefined)
// check that only one source is defined
@@ -470,11 +466,11 @@ object WeatherSource {
() =>
new CsvIdCoordinateSource(
idCoordinateFactory,
- new CsvDataSourceAdapter(
+ new CsvDataSource(
csvSep,
Paths.get(directoryPath),
- new FileNamingStrategy()
- )
+ new FileNamingStrategy(),
+ ),
)
case Some(
Some(
@@ -483,7 +479,7 @@ object WeatherSource {
userName,
password,
schemaName,
- tableName
+ tableName,
)
)
) =>
@@ -494,7 +490,7 @@ object WeatherSource {
new SqlConnector(jdbcUrl, userName, password),
schemaName,
tableName,
- new SqlIdCoordinateFactory()
+ new SqlIdCoordinateFactory(),
)
case Some(
Some(
@@ -548,7 +544,7 @@ object WeatherSource {
WattsPerSquareMeter(0.0),
WattsPerSquareMeter(0.0),
Kelvin(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
)
def toWeatherData(
@@ -594,7 +590,7 @@ object WeatherSource {
.doubleValue()
)
case None => EMPTY_WEATHER_DATA.windVel
- }
+ },
)
}
@@ -604,7 +600,7 @@ object WeatherSource {
*/
private[weather] final case class AgentCoordinates(
latitude: Double,
- longitude: Double
+ longitude: Double,
) {
def toPoint: Point =
GeoUtils.DEFAULT_GEOMETRY_FACTORY.createPoint(
diff --git a/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala b/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala
index 2fc48cd5f9..dac52742ed 100644
--- a/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala
+++ b/src/main/scala/edu/ie3/simona/service/weather/WeatherSourceWrapper.scala
@@ -10,11 +10,11 @@ import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.io.connectors.{
CouchbaseConnector,
InfluxDbConnector,
- SqlConnector
+ SqlConnector,
}
import edu.ie3.datamodel.io.factory.timeseries.{
CosmoTimeBasedWeatherValueFactory,
- IconTimeBasedWeatherValueFactory
+ IconTimeBasedWeatherValueFactory,
}
import edu.ie3.datamodel.io.naming.FileNamingStrategy
import edu.ie3.datamodel.io.source.couchbase.CouchbaseWeatherSource
@@ -23,12 +23,12 @@ import edu.ie3.datamodel.io.source.influxdb.InfluxDbWeatherSource
import edu.ie3.datamodel.io.source.sql.SqlWeatherSource
import edu.ie3.datamodel.io.source.{
IdCoordinateSource,
- WeatherSource => PsdmWeatherSource
+ WeatherSource => PsdmWeatherSource,
}
import edu.ie3.simona.config.SimonaConfig.Simona.Input.Weather.Datasource.{
CouchbaseParams,
InfluxDb1xParams,
- SqlParams
+ SqlParams,
}
import edu.ie3.simona.exceptions.InitializationException
import edu.ie3.simona.ontology.messages.services.WeatherMessage
@@ -36,7 +36,7 @@ import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
import edu.ie3.simona.service.weather.WeatherSource.{
EMPTY_WEATHER_DATA,
WeatherScheme,
- toWeatherData
+ toWeatherData,
}
import edu.ie3.simona.service.weather.WeatherSourceWrapper.WeightSum
import edu.ie3.simona.service.weather.{WeatherSource => SimonaWeatherSource}
@@ -72,7 +72,7 @@ private[weather] final case class WeatherSourceWrapper private (
source: PsdmWeatherSource,
override val idCoordinateSource: IdCoordinateSource,
resolution: Long,
- maxCoordinateDistance: ComparableQuantity[Length]
+ maxCoordinateDistance: ComparableQuantity[Length],
)(
private implicit val simulationStart: ZonedDateTime
) extends SimonaWeatherSource
@@ -90,7 +90,7 @@ private[weather] final case class WeatherSourceWrapper private (
*/
override def getWeather(
tick: Long,
- weightedCoordinates: WeatherSource.WeightedCoordinates
+ weightedCoordinates: WeatherSource.WeightedCoordinates,
): WeatherMessage.WeatherData = {
val dateTime = tick.toDateTime
val interval = new ClosedInterval(dateTime, dateTime)
@@ -98,7 +98,7 @@ private[weather] final case class WeatherSourceWrapper private (
val results = source
.getWeather(
interval,
- coordinates
+ coordinates,
)
.asScala
.toMap
@@ -124,7 +124,7 @@ private[weather] final case class WeatherSourceWrapper private (
point, {
logger.warn(s"Received an unexpected point: $point")
0d
- }
+ },
)
/* Sum up weight and contributions */
@@ -169,8 +169,8 @@ private[weather] final case class WeatherSourceWrapper private (
diffIrrWeight,
dirIrrWeight,
tempWeight,
- windVelWeight
- )
+ windVelWeight,
+ ),
)
} match {
case (weatherData: WeatherData, weightSum: WeightSum) =>
@@ -190,7 +190,7 @@ private[weather] final case class WeatherSourceWrapper private (
*/
override def getDataTicks(
requestFrameStart: Long,
- requestFrameEnd: Long
+ requestFrameEnd: Long,
): Array[Long] =
TickUtil.getTicksInBetween(requestFrameStart, requestFrameEnd, resolution)
}
@@ -205,7 +205,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
timestampPattern: Option[String],
scheme: String,
resolution: Option[Long],
- maxCoordinateDistance: ComparableQuantity[Length]
+ maxCoordinateDistance: ComparableQuantity[Length],
)(implicit simulationStart: ZonedDateTime): WeatherSourceWrapper = {
val idCoordinateSource = idCoordinateSourceFunction()
val source = new CsvWeatherSource(
@@ -213,7 +213,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
directoryPath,
new FileNamingStrategy(),
idCoordinateSource,
- buildFactory(scheme, timestampPattern)
+ buildFactory(scheme, timestampPattern),
)
logger.info(
"Successfully initiated CsvWeatherSource as source for WeatherSourceWrapper."
@@ -222,7 +222,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
source,
idCoordinateSource,
resolution.getOrElse(DEFAULT_RESOLUTION),
- maxCoordinateDistance
+ maxCoordinateDistance,
)
}
@@ -232,13 +232,13 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
timestampPattern: Option[String],
scheme: String,
resolution: Option[Long],
- maxCoordinateDistance: ComparableQuantity[Length]
+ maxCoordinateDistance: ComparableQuantity[Length],
)(implicit simulationStart: ZonedDateTime): WeatherSourceWrapper = {
val couchbaseConnector = new CouchbaseConnector(
couchbaseParams.url,
couchbaseParams.bucketName,
couchbaseParams.userName,
- couchbaseParams.password
+ couchbaseParams.password,
)
val idCoordinateSource = idCoordinateSourceFunction()
val source = new CouchbaseWeatherSource(
@@ -247,7 +247,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
couchbaseParams.coordinateColumnName,
couchbaseParams.keyPrefix,
buildFactory(scheme, timestampPattern),
- "yyyy-MM-dd'T'HH:mm:ssxxx"
+ "yyyy-MM-dd'T'HH:mm:ssxxx",
)
logger.info(
"Successfully initiated CouchbaseWeatherSource as source for WeatherSourceWrapper."
@@ -256,7 +256,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
source,
idCoordinateSource,
resolution.getOrElse(DEFAULT_RESOLUTION),
- maxCoordinateDistance
+ maxCoordinateDistance,
)
}
@@ -266,7 +266,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
timestampPattern: Option[String],
scheme: String,
resolution: Option[Long],
- maxCoordinateDistance: ComparableQuantity[Length]
+ maxCoordinateDistance: ComparableQuantity[Length],
)(implicit simulationStart: ZonedDateTime): WeatherSourceWrapper = {
val influxDb1xConnector =
new InfluxDbConnector(influxDbParams.url, influxDbParams.database)
@@ -274,7 +274,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
val source = new InfluxDbWeatherSource(
influxDb1xConnector,
idCoordinateSource,
- buildFactory(scheme, timestampPattern)
+ buildFactory(scheme, timestampPattern),
)
logger.info(
"Successfully initiated InfluxDbWeatherSource as source for WeatherSourceWrapper."
@@ -283,7 +283,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
source,
idCoordinateSource,
resolution.getOrElse(DEFAULT_RESOLUTION),
- maxCoordinateDistance
+ maxCoordinateDistance,
)
}
@@ -293,12 +293,12 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
timestampPattern: Option[String],
scheme: String,
resolution: Option[Long],
- maxCoordinateDistance: ComparableQuantity[Length]
+ maxCoordinateDistance: ComparableQuantity[Length],
)(implicit simulationStart: ZonedDateTime): WeatherSourceWrapper = {
val sqlConnector = new SqlConnector(
sqlParams.jdbcUrl,
sqlParams.userName,
- sqlParams.password
+ sqlParams.password,
)
val idCoordinateSource = idCoordinateSourceFunction()
val source = new SqlWeatherSource(
@@ -306,7 +306,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
idCoordinateSource,
sqlParams.schemaName,
sqlParams.tableName,
- buildFactory(scheme, timestampPattern)
+ buildFactory(scheme, timestampPattern),
)
logger.info(
"Successfully initiated SqlWeatherSource as source for WeatherSourceWrapper."
@@ -315,7 +315,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
source,
idCoordinateSource,
resolution.getOrElse(DEFAULT_RESOLUTION),
- maxCoordinateDistance
+ maxCoordinateDistance,
)
}
@@ -325,7 +325,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
throw new InitializationException(
s"Error while initializing WeatherFactory for weather source wrapper: '$scheme' is not a weather scheme. Supported schemes:\n\t${WeatherScheme.values
.mkString("\n\t")}'",
- exception
+ exception,
)
case Success(WeatherScheme.ICON) =>
timestampPattern
@@ -358,19 +358,19 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
diffIrr: Double,
dirIrr: Double,
temp: Double,
- windVel: Double
+ windVel: Double,
) {
def add(
diffIrr: Double,
dirIrr: Double,
temp: Double,
- windVel: Double
+ windVel: Double,
): WeightSum =
WeightSum(
this.diffIrr + diffIrr,
this.dirIrr + dirIrr,
this.temp + temp,
- this.windVel + windVel
+ this.windVel + windVel,
)
/** Scale the given [[WeatherData]] by dividing by the sum of weights per
@@ -394,7 +394,7 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
if (this.temp !~= 0d) temp.divide(this.temp)
else EMPTY_WEATHER_DATA.temp,
if (this.windVel !~= 0d) windVel.divide(this.windVel)
- else EMPTY_WEATHER_DATA.windVel
+ else EMPTY_WEATHER_DATA.windVel,
)
}
}
diff --git a/src/main/scala/edu/ie3/simona/sim/SimMessage.scala b/src/main/scala/edu/ie3/simona/sim/SimMessage.scala
deleted file mode 100644
index 1ca7438421..0000000000
--- a/src/main/scala/edu/ie3/simona/sim/SimMessage.scala
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * © 2023. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.sim
-
-trait SimMessage
-object SimMessage {
-
- /** Tell the [[SimonaSim]] to initialize the simulation
- */
- case object InitSim extends SimMessage
-
- /** Starts simulation by activating the next (or first) tick
- *
- * @param pauseTick
- * Last tick that can be activated or completed before the simulation is
- * paused
- */
- final case class StartSimulation(
- pauseTick: Option[Long] = None
- ) extends SimMessage
-
- /** Reported back from the scheduler if an error occurred during the
- * simulation
- */
- case object SimulationFailure extends SimMessage
-
- /** Reported back from the scheduler if the simulation terminated as expected
- */
- case object SimulationSuccessful extends SimMessage
-
-}
diff --git a/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala b/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala
index 7a619552eb..f62dc5e45a 100644
--- a/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala
+++ b/src/main/scala/edu/ie3/simona/sim/SimonaSim.scala
@@ -6,274 +6,276 @@
package edu.ie3.simona.sim
-import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
-import org.apache.pekko.actor.SupervisorStrategy.Stop
-import org.apache.pekko.actor.{
- Actor,
- ActorRef,
- AllForOneStrategy,
- Props,
- Stash,
- SupervisorStrategy,
- Terminated
-}
-import com.typesafe.scalalogging.LazyLogging
import edu.ie3.simona.agent.EnvironmentRefs
+import edu.ie3.simona.api.ExtSimAdapter
import edu.ie3.simona.event.RuntimeEvent
-import edu.ie3.simona.ontology.messages.StopMessage
+import edu.ie3.simona.event.listener.{DelayedStopHelper, RuntimeEventListener}
+import edu.ie3.simona.main.RunSimona.SimonaEnded
import edu.ie3.simona.scheduler.TimeAdvancer
-import edu.ie3.simona.scheduler.TimeAdvancer.StartSimMessage
-import edu.ie3.simona.sim.SimMessage.{
- InitSim,
- SimulationFailure,
- SimulationSuccessful,
- StartSimulation
-}
-import edu.ie3.simona.sim.SimonaSim.{
- EmergencyShutdownInitiated,
- SimonaSimStateData
-}
import edu.ie3.simona.sim.setup.{ExtSimSetupData, SimonaSetup}
-
-import scala.concurrent.duration.DurationInt
-import scala.language.postfixOps
-
-/** Main entrance point to a simona simulation as top level actor. This actors
- * allows to control the simulation in the sense that messages for
- * initialization and time steps can be send to this actor and will be handled
- * accordingly. This actor does NOT report back any simulation status except of
- * if the overall simulation has been successful or not. For specific status
+import edu.ie3.util.scala.Scope
+import org.apache.pekko.actor.typed.scaladsl.adapter._
+import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
+import org.apache.pekko.actor.typed.{ActorRef, Behavior, PostStop, Terminated}
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
+
+/** Main entrance point to a simona simulation as the guardian actor. This actor
+ * starts the initialization of all actors and waits for the simulation to end.
+ * This actor does NOT report back any simulation status except of if the
+ * overall simulation has been successful or not. For specific status
* information, the user needs to pass in and subscribe to the corresponding
* listener e.g. [[edu.ie3.simona.event.listener.RuntimeEventListener]] for
* simulation status or [[edu.ie3.simona.event.listener.ResultEventListener]]
* for result events
*
- * @version 0.1
* @since 01.07.20
*/
-class SimonaSim(simonaSetup: SimonaSetup)
- extends Actor
- with LazyLogging
- with Stash {
-
- override val supervisorStrategy: SupervisorStrategy =
- AllForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
- case ex: Exception =>
- self ! EmergencyShutdownInitiated
- logger.error(
- "The simulation's guardian received an uncaught exception. - {}: \"{}\" - Start emergency shutdown.",
- ex.getClass.getSimpleName,
- ex.getMessage
- )
- Stop
- }
+object SimonaSim {
- /* start listener */
- // output listener
- val systemParticipantsListener: Seq[ActorRef] =
- simonaSetup.systemParticipantsListener(context)
-
- // runtime event listener
- val runtimeEventListener
- : org.apache.pekko.actor.typed.ActorRef[RuntimeEvent] =
- simonaSetup.runtimeEventListener(context)
-
- /* start scheduler */
- val timeAdvancer
- : org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming] =
- simonaSetup.timeAdvancer(context, self, runtimeEventListener)
- val scheduler: ActorRef = simonaSetup.scheduler(context, timeAdvancer)
-
- /* start services */
- // primary service proxy
- val primaryServiceProxy: ActorRef =
- simonaSetup.primaryServiceProxy(context, scheduler)
-
- // weather service
- val weatherService: ActorRef =
- simonaSetup.weatherService(context, scheduler)
-
- val extSimulationData: ExtSimSetupData =
- simonaSetup.extSimulations(context, scheduler)
-
- /* start grid agents */
- val gridAgents: Iterable[ActorRef] = simonaSetup.gridAgents(
- context,
- EnvironmentRefs(
- scheduler,
- runtimeEventListener.toClassic,
- primaryServiceProxy,
- weatherService,
- extSimulationData.evDataService
- ),
- systemParticipantsListener
- )
+ sealed trait Request
- /* watch all actors */
- systemParticipantsListener.foreach(context.watch)
- context.watch(runtimeEventListener.toClassic)
- context.watch(timeAdvancer.toClassic)
- context.watch(scheduler)
- context.watch(primaryServiceProxy)
- context.watch(weatherService)
- gridAgents.foreach(context.watch)
+ /** Starts the initialization and then the simulation itself */
+ final case class Start(
+ starter: ActorRef[SimonaEnded]
+ ) extends Request
- override def receive: Receive = simonaSimReceive(SimonaSimStateData())
+ /** Indicates that the simulation has ended successfully */
+ case object SimulationEnded extends Request
- def simonaSimReceive(data: SimonaSim.SimonaSimStateData): Receive = {
+ /** Creates a new [[SimonaSim]] behavior
+ *
+ * @param simonaSetup
+ * The setup instructions
+ */
+ def apply(simonaSetup: SimonaSetup): Behavior[Request] =
+ Behaviors.receivePartial { case (ctx, startMsg @ Start(starter)) =>
+ // We redirect to initializing behavior so that starter ref
+ // is available in case of a sudden termination of this actor
+ ctx.self ! startMsg
+ initializing(simonaSetup, starter)
+ }
- case InitSim =>
- // tell scheduler to process all init messages
- timeAdvancer ! StartSimMessage()
- context become simonaSimReceive(data.copy(initSimSender = sender()))
+ /** Initializing behavior that is separated from [[apply]] above only because
+ * in case of a sudden stop of this actor itself (PostStop signal), the
+ * ActorRef of the starter also needs to be available for sending
+ * [[SimonaEnded]]
+ *
+ * @param starter
+ * The starter ref that needs to be notified once simulation has ended
+ */
+ private def initializing(
+ simonaSetup: SimonaSetup,
+ starter: ActorRef[SimonaEnded],
+ ): Behavior[Request] =
+ Behaviors
+ .receivePartial[Request] { case (ctx, Start(_)) =>
+ val resultEventListeners =
+ simonaSetup.resultEventListener(ctx)
+
+ val runtimeEventListener = simonaSetup.runtimeEventListener(ctx)
+
+ val timeAdvancer =
+ simonaSetup.timeAdvancer(ctx, ctx.self, runtimeEventListener)
+ val scheduler = simonaSetup.scheduler(ctx, timeAdvancer)
+
+ /* start services */
+ // primary service proxy
+ val primaryServiceProxy =
+ simonaSetup.primaryServiceProxy(ctx, scheduler)
+
+ // weather service
+ val weatherService =
+ simonaSetup.weatherService(ctx, scheduler)
+
+ val extSimulationData: ExtSimSetupData =
+ simonaSetup.extSimulations(ctx, scheduler)
+
+ val environmentRefs = EnvironmentRefs(
+ scheduler,
+ runtimeEventListener.toClassic,
+ primaryServiceProxy,
+ weatherService,
+ extSimulationData.evDataService,
+ )
- case StartSimulation(pauseScheduleAtTick) =>
- timeAdvancer ! StartSimMessage(pauseScheduleAtTick)
+ /* start grid agents */
+ val gridAgents = simonaSetup.gridAgents(
+ ctx,
+ environmentRefs,
+ resultEventListeners,
+ )
- case msg @ (SimulationSuccessful | SimulationFailure) =>
- val simulationSuccessful = msg match {
- case SimulationSuccessful =>
- logger.info(
- "Simulation terminated successfully. Stopping children ..."
+ /* watch all actors */
+ resultEventListeners.foreach(ctx.watch)
+ ctx.watch(runtimeEventListener)
+ ctx.watch(timeAdvancer)
+ ctx.watch(scheduler)
+ ctx.watch(primaryServiceProxy.toTyped)
+ ctx.watch(weatherService.toTyped)
+ extSimulationData.extSimAdapters.map(_.toTyped).foreach(ctx.watch)
+ extSimulationData.extDataServices.values
+ .map(_.toTyped)
+ .foreach(ctx.watch)
+ gridAgents.foreach(ref => ctx.watch(ref))
+
+ // Start simulation
+ timeAdvancer ! TimeAdvancer.Start()
+
+ val otherActors = Iterable(
+ timeAdvancer,
+ scheduler,
+ primaryServiceProxy.toTyped,
+ weatherService.toTyped,
+ ) ++
+ gridAgents ++
+ extSimulationData.extDataServices.values.map(_.toTyped)
+
+ idle(
+ ActorData(
+ starter,
+ extSimulationData.extSimAdapters,
+ runtimeEventListener,
+ resultEventListeners.appended(runtimeEventListener),
+ otherActors,
)
- true
- case SimulationFailure =>
- logger.error(
- "An error occurred during the simulation. See stacktrace for details."
- )
- false
+ )
}
+ .receiveSignal { case (_, PostStop) =>
+ // Something must have gone wrong during initialization above
+ // (probably an unexpected exception thrown), there's nothing
+ // much we can do here besides notifying starter
+ starter ! SimonaEnded(successful = false)
- // stop all children
- stopAllChildrenGracefully(simulationSuccessful)
+ Behaviors.stopped
+ }
- // wait for listeners and send final message to parent
- context become waitingForListener(
- data.initSimSender,
- simulationSuccessful,
- systemParticipantsListener
+ /** Behavior that is active while the simulation is running
+ */
+ private def idle(actorData: ActorData): Behavior[Request] = Behaviors
+ .receivePartial[Request] { case (ctx, SimulationEnded) =>
+ ctx.log.info(
+ "Simulation terminated successfully. Stopping children and waiting for results to flush out..."
)
- case EmergencyShutdownInitiated =>
- logger.debug(
- "Simulation guardian is aware, that emergency shutdown has been initiated. Inform the init sender."
- )
- data.initSimSender ! SimulationFailure
- context.become(emergencyShutdownReceive)
-
- case Terminated(actorRef) =>
- logger.error(
- "An actor ({}) unexpectedly terminated. Shut down all children gracefully and report simulation " +
- "failure. See logs and possible stacktrace for details.",
- actorRef
- )
+ stopChildren(ctx, actorData, simulationSuccessful = true)
+ }
+ .receiveSignal { case (ctx, Terminated(actor)) =>
+ Scope(actorData)
+ .map(data =>
+ data.copy(
+ delayedStoppingActors =
+ data.delayedStoppingActors.toSeq.filterNot(_ == actor)
+ )
+ )
+ .map { data =>
+ ctx.log.error(
+ "An actor ({}) unexpectedly terminated. " +
+ "Stopping all children and reporting simulation failure. " +
+ "See logs and possibly stacktrace for details.",
+ actor,
+ )
- // stop all children
- stopAllChildrenGracefully(simulationSuccessful = false)
+ // also notify RuntimeEventListener that error happened
+ data.runtimeEventListener ! RuntimeEvent.Error(
+ "Simulation stopped with error."
+ )
- // wait for listeners and send final message to parent
- context become waitingForListener(
- data.initSimSender,
- successful = false,
- systemParticipantsListener
- )
- }
+ stopChildren(ctx, data, simulationSuccessful = false)
+ }
+ .get
+ }
- def emergencyShutdownReceive: Receive = {
- case EmergencyShutdownInitiated =>
- /* Nothing to do. Already know about emergency shutdown. */
+ /** Stops all children that can be stopped right away, and requests children
+ * with delayed stops to stop
+ */
+ private def stopChildren(
+ ctx: ActorContext[_],
+ actorData: ActorData,
+ simulationSuccessful: Boolean,
+ ): Behavior[Request] = {
+ actorData.otherActors.foreach { ref =>
+ ctx.unwatch(ref)
+ ctx.stop(ref)
+ }
- case Terminated(actorRef) =>
- logger.debug("'{}' successfully terminated.", actorRef)
+ actorData.extSimAdapters.foreach { ref =>
+ ctx.unwatch(ref)
+ ref ! ExtSimAdapter.Stop(simulationSuccessful)
+ }
- case unsupported =>
- logger.warn(
- "Received the following message. Simulation is in emergency shutdown mode. Will neglect that " +
- "message!\n\t{}",
- unsupported
- )
+ // if the simulation is successful, we're waiting for the delayed
+ // stopping listeners to terminate and thus do not unwatch them here
+ actorData.delayedStoppingActors.foreach(
+ _ ! DelayedStopHelper.FlushAndStop
+ )
+
+ maybeStop(
+ ctx,
+ actorData.starter,
+ actorData.delayedStoppingActors,
+ simulationSuccessful,
+ )
}
- def waitingForListener(
- initSimSender: ActorRef,
- successful: Boolean,
- remainingListeners: Seq[ActorRef]
- ): Receive = {
- case Terminated(actor) if remainingListeners.contains(actor) =>
+ /** Behavior that waits for all remaining actors with delayed stops to stop
+ */
+ private def waitingForListener(
+ starter: ActorRef[SimonaEnded],
+ remainingListeners: Seq[ActorRef[_]],
+ simulationSuccessful: Boolean,
+ ): Behavior[Request] = Behaviors.receiveSignal[Request] {
+ case (ctx, Terminated(actor)) if remainingListeners.contains(actor) =>
val updatedRemainingListeners = remainingListeners.filterNot(_ == actor)
- logger.debug(
- "Listener {} has been terminated. Remaining listeners: {}",
- actor,
- updatedRemainingListeners
- )
-
- if (updatedRemainingListeners.isEmpty) {
- logger.info(
- "The last remaining result listener has been terminated. Exiting."
- )
-
- val msg =
- if (successful) SimulationSuccessful
- else SimulationFailure
- // inform InitSim Sender
- initSimSender ! msg
- }
-
- context become waitingForListener(
- initSimSender,
- successful,
- updatedRemainingListeners
- )
+ maybeStop(ctx, starter, updatedRemainingListeners, simulationSuccessful)
}
- def stopAllChildrenGracefully(
- simulationSuccessful: Boolean
- ): Unit = {
- gridAgents.foreach { gridAgentRef =>
- context.unwatch(gridAgentRef)
- gridAgentRef ! StopMessage(simulationSuccessful)
- }
-
- context.unwatch(scheduler)
- context.stop(scheduler)
+ /** Stopping this actor and notifying starter if all actors with delayed stops
+ * have stopped
+ */
+ private def maybeStop(
+ ctx: ActorContext[_],
+ starter: ActorRef[SimonaEnded],
+ remainingListeners: Seq[ActorRef[_]],
+ simulationSuccessful: Boolean,
+ ): Behavior[Request] = {
+ if (remainingListeners.isEmpty) {
+ ctx.log.debug(
+ "All actors with delayed stops have terminated. Ending simulation."
+ )
- context.unwatch(weatherService)
- context.stop(weatherService)
+ starter ! SimonaEnded(simulationSuccessful)
- extSimulationData.extSimAdapters.foreach { ref =>
- context.unwatch(ref)
- ref ! StopMessage(simulationSuccessful)
- }
- extSimulationData.extDataServices.foreach { case (_, ref) =>
- context.unwatch(ref)
- context.stop(ref)
+ Behaviors.stopped
+ } else {
+ waitingForListener(
+ starter,
+ remainingListeners,
+ simulationSuccessful,
+ )
}
-
- // do not unwatch the result listeners, as we're waiting for their termination
- systemParticipantsListener.foreach(
- _ ! StopMessage(simulationSuccessful)
- )
-
- context.unwatch(runtimeEventListener.toClassic)
- context.stop(runtimeEventListener.toClassic)
-
- logger.debug("Stopping all listeners requested.")
}
-}
-
-object SimonaSim {
- /** Object to indicate to the scheduler itself, that an uncaught exception has
- * triggered an emergency shutdown of the simulation system
+ /** Data object that mostly holds information about children of this actor
+ *
+ * @param starter
+ * The ActorRef that started the simulation and should be notified about
+ * its end
+ * @param extSimAdapters
+ * [[ExtSimAdapter]]s need to receive a [[ExtSimAdapter.Stop]] message
+ * @param runtimeEventListener
+ * The [[RuntimeEventListener]] that possibly receives an error event
+ * @param delayedStoppingActors
+ * The actors that are stopped with delay
+ * @param otherActors
+ * All remaining children (excluding ExtSimAdapters, ResultListeners and
+ * RuntimeEventListener)
*/
- case object EmergencyShutdownInitiated
-
- private[SimonaSim] final case class SimonaSimStateData(
- initSimSender: ActorRef = ActorRef.noSender
+ private final case class ActorData(
+ starter: ActorRef[SimonaEnded],
+ extSimAdapters: Iterable[ClassicRef],
+ runtimeEventListener: ActorRef[RuntimeEventListener.Request],
+ delayedStoppingActors: Seq[ActorRef[DelayedStopHelper.StoppingMsg]],
+ otherActors: Iterable[ActorRef[_]],
)
-
- def props(simonaSetup: SimonaSetup): Props =
- Props(new SimonaSim(simonaSetup))
-
}
diff --git a/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala b/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala
index 4706a50f09..d5b34606aa 100644
--- a/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala
+++ b/src/main/scala/edu/ie3/simona/sim/setup/ExtSimSetupData.scala
@@ -11,7 +11,7 @@ import edu.ie3.simona.service.ev.ExtEvDataService
final case class ExtSimSetupData(
extSimAdapters: Iterable[ActorRef],
- extDataServices: Map[Class[_], ActorRef]
+ extDataServices: Map[Class[_], ActorRef],
) {
def evDataService: Option[ActorRef] =
diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala b/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala
index 1e319ecbcc..55d907ae78 100644
--- a/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala
+++ b/src/main/scala/edu/ie3/simona/sim/setup/SetupHelper.scala
@@ -6,14 +6,15 @@
package edu.ie3.simona.sim.setup
-import org.apache.pekko.actor.ActorRef
import com.typesafe.config.{Config => TypesafeConfig}
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.graph.SubGridGate
import edu.ie3.datamodel.models.input.container.{SubGridContainer, ThermalGrid}
import edu.ie3.datamodel.models.result.ResultEntity
+import edu.ie3.datamodel.models.result.system.FlexOptionsResult
import edu.ie3.datamodel.utils.ContainerUtils
import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
+import edu.ie3.simona.agent.grid.GridAgentMessage
import edu.ie3.simona.config.RefSystemParser.ConfigRefSystems
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.exceptions.InitializationException
@@ -23,6 +24,9 @@ import edu.ie3.simona.model.grid.RefSystem
import edu.ie3.simona.util.ConfigUtil.{GridOutputConfigUtil, OutputConfigUtil}
import edu.ie3.simona.util.ResultFileHierarchy.ResultEntityPathConfig
import edu.ie3.simona.util.{EntityMapperUtil, ResultFileHierarchy}
+import org.apache.pekko.actor.typed.ActorRef
+import edu.ie3.util.quantities.PowerSystemUnits
+import squants.electro.Kilovolts
/** Methods to support the setup of a simona simulation
*
@@ -54,15 +58,15 @@ trait SetupHelper extends LazyLogging {
*/
def buildGridAgentInitData(
subGridContainer: SubGridContainer,
- subGridToActorRef: Map[Int, ActorRef],
+ subGridToActorRef: Map[Int, ActorRef[GridAgentMessage]],
gridGates: Set[SubGridGate],
configRefSystems: ConfigRefSystems,
- thermalGrids: Seq[ThermalGrid]
+ thermalGrids: Seq[ThermalGrid],
): GridAgentInitData = {
val subGridGateToActorRef = buildGateToActorRef(
subGridToActorRef,
gridGates,
- subGridContainer.getSubnet
+ subGridContainer.getSubnet,
)
/* Find the matching reference system */
@@ -78,7 +82,7 @@ trait SetupHelper extends LazyLogging {
updatedSubGridContainer,
thermalGrids,
subGridGateToActorRef,
- refSystem
+ refSystem,
)
}
@@ -95,10 +99,10 @@ trait SetupHelper extends LazyLogging {
* A mapping from [[SubGridGate]] to corresponding actor reference
*/
def buildGateToActorRef(
- subGridToActorRefMap: Map[Int, ActorRef],
+ subGridToActorRefMap: Map[Int, ActorRef[GridAgentMessage]],
subGridGates: Set[SubGridGate],
- currentSubGrid: Int
- ): Map[SubGridGate, ActorRef] =
+ currentSubGrid: Int,
+ ): Map[SubGridGate, ActorRef[GridAgentMessage]] =
subGridGates
.groupBy(gate => (gate.superiorNode, gate.inferiorNode))
.flatMap(_._2.headOption)
@@ -110,14 +114,14 @@ trait SetupHelper extends LazyLogging {
gate -> getActorRef(
subGridToActorRefMap,
currentSubGrid,
- superiorSubGrid
+ superiorSubGrid,
)
} else if (superiorSubGrid == currentSubGrid) {
/* This is a gate to an inferior sub grid */
gate -> getActorRef(
subGridToActorRefMap,
currentSubGrid,
- inferiorSubGrid
+ inferiorSubGrid,
)
} else {
throw new GridAgentInitializationException(
@@ -140,10 +144,10 @@ trait SetupHelper extends LazyLogging {
* The actor reference of the sub grid to look for
*/
def getActorRef(
- subGridToActorRefMap: Map[Int, ActorRef],
+ subGridToActorRefMap: Map[Int, ActorRef[GridAgentMessage]],
currentSubGrid: Int,
- queriedSubGrid: Int
- ): ActorRef = {
+ queriedSubGrid: Int,
+ ): ActorRef[GridAgentMessage] = {
subGridToActorRefMap.get(queriedSubGrid) match {
case Some(hit) => hit
case _ =>
@@ -165,12 +169,12 @@ trait SetupHelper extends LazyLogging {
*/
def getRefSystem(
configRefSystems: ConfigRefSystems,
- subGridContainer: SubGridContainer
+ subGridContainer: SubGridContainer,
): RefSystem = {
val refSystem = configRefSystems
.find(
subGridContainer.getSubnet,
- Some(subGridContainer.getPredominantVoltageLevel)
+ Some(subGridContainer.getPredominantVoltageLevel),
)
.getOrElse(
throw new InitializationException(
@@ -179,15 +183,17 @@ trait SetupHelper extends LazyLogging {
)
)
- if (
- !refSystem.nominalVoltage.equals(
- subGridContainer.getPredominantVoltageLevel.getNominalVoltage
- )
+ val containerPotential = Kilovolts(
+ subGridContainer.getPredominantVoltageLevel.getNominalVoltage
+ .to(PowerSystemUnits.KILOVOLT)
+ .getValue
+ .doubleValue
)
+
+ if (refSystem.nominalVoltage != containerPotential)
logger.warn(
s"The configured RefSystem for subGrid ${subGridContainer.getSubnet} differs in its nominal voltage (${refSystem.nominalVoltage}) from the grids" +
- s"predominant voltage level nominal voltage (${subGridContainer.getPredominantVoltageLevel.getNominalVoltage}). If this is by intention and still valid, this " +
- s"warning can be just ignored!"
+ s"predominant voltage level nominal voltage ($containerPotential). If this is by intention and still valid, this warning can be just ignored!"
)
refSystem
@@ -205,7 +211,7 @@ trait SetupHelper extends LazyLogging {
*/
def buildResultFileHierarchy(
config: TypesafeConfig,
- createDirs: Boolean = true
+ createDirs: Boolean = true,
): ResultFileHierarchy = {
val simonaConfig = SimonaConfig(config)
@@ -221,12 +227,12 @@ trait SetupHelper extends LazyLogging {
modelsToWrite,
ResultSinkType(
simonaConfig.simona.output.sink,
- simonaConfig.simona.simulationName
- )
+ simonaConfig.simona.simulationName,
+ ),
),
addTimeStampToOutputDir =
simonaConfig.simona.output.base.addTimestampToOutputDir,
- createDirs = createDirs
+ createDirs = createDirs,
)
// copy config data to output directory
@@ -236,7 +242,7 @@ trait SetupHelper extends LazyLogging {
}
}
-case object SetupHelper {
+object SetupHelper {
/** Determine a comprehensive collection of all [[ResultEntity]] classes, that
* will have to be considered
@@ -257,5 +263,6 @@ case object SetupHelper {
).simulationResultIdentifiersToConsider ++ OutputConfigUtil(
outputConfig.thermal
).simulationResultIdentifiersToConsider)
- .map(notifierId => EntityMapperUtil.getResultEntityClass(notifierId))
+ .map(notifierId => EntityMapperUtil.getResultEntityClass(notifierId)) ++
+ (if (outputConfig.flex) Seq(classOf[FlexOptionsResult]) else Seq.empty)
}
diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala
index caad2d1b7b..16809499b0 100644
--- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala
+++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaSetup.scala
@@ -6,12 +6,18 @@
package edu.ie3.simona.sim.setup
-import org.apache.pekko.actor.{ActorContext, ActorRef, ActorSystem}
import edu.ie3.datamodel.graph.SubGridGate
import edu.ie3.datamodel.models.input.connector.Transformer3WInput
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.event.RuntimeEvent
+import edu.ie3.simona.agent.grid.GridAgentMessage
+import edu.ie3.simona.event.listener.{ResultEventListener, RuntimeEventListener}
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
+import edu.ie3.simona.ontology.messages.SchedulerMessage
import edu.ie3.simona.scheduler.TimeAdvancer
+import edu.ie3.simona.sim.SimonaSim
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.ActorContext
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
/** Trait that can be used to setup a customized simona simulation by providing
* implementations for all setup information required by a
@@ -30,11 +36,6 @@ trait SimonaSetup {
*/
val args: Array[String]
- /** A function, that constructs the [[ActorSystem]], the simulation shall live
- * in
- */
- val buildActorSystem: () => ActorSystem
-
/** Creates the runtime event listener
*
* @param context
@@ -43,19 +44,19 @@ trait SimonaSetup {
* An actor reference to the runtime event listener
*/
def runtimeEventListener(
- context: ActorContext
- ): org.apache.pekko.actor.typed.ActorRef[RuntimeEvent]
+ context: ActorContext[_]
+ ): ActorRef[RuntimeEventListener.Request]
- /** Creates a sequence of system participant event listeners
+ /** Creates a sequence of result event listeners
*
* @param context
* Actor context to use
* @return
- * A sequence of actor references to runtime event listeners
+ * A sequence of actor references to result event listeners
*/
- def systemParticipantsListener(
- context: ActorContext
- ): Seq[ActorRef]
+ def resultEventListener(
+ context: ActorContext[_]
+ ): Seq[ActorRef[ResultEventListener.Request]]
/** Creates a primary service proxy. The proxy is the first instance to ask
* for primary data. If necessary, it delegates the registration request to
@@ -69,9 +70,9 @@ trait SimonaSetup {
* An actor reference to the service
*/
def primaryServiceProxy(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef
/** Creates a weather service
*
@@ -84,9 +85,9 @@ trait SimonaSetup {
* the service
*/
def weatherService(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef
/** Loads external simulations and provides corresponding actors and init data
*
@@ -98,8 +99,8 @@ trait SimonaSetup {
* External simulations and their init data
*/
def extSimulations(
- context: ActorContext,
- scheduler: ActorRef
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
): ExtSimSetupData
/** Creates the time advancer
@@ -114,10 +115,10 @@ trait SimonaSetup {
* An actor reference to the time advancer
*/
def timeAdvancer(
- context: ActorContext,
- simulation: ActorRef,
- runtimeEventListener: org.apache.pekko.actor.typed.ActorRef[RuntimeEvent]
- ): org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming]
+ context: ActorContext[_],
+ simulation: ActorRef[SimonaSim.SimulationEnded.type],
+ runtimeEventListener: ActorRef[RuntimeEvent],
+ ): ActorRef[TimeAdvancer.Request]
/** Creates a scheduler service
*
@@ -129,9 +130,9 @@ trait SimonaSetup {
* An actor reference to the scheduler
*/
def scheduler(
- context: ActorContext,
- timeAdvancer: org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming]
- ): ActorRef
+ context: ActorContext[_],
+ timeAdvancer: ActorRef[TimeAdvancer.Request],
+ ): ActorRef[SchedulerMessage]
/** Creates all the needed grid agents
*
@@ -139,17 +140,17 @@ trait SimonaSetup {
* Actor context to use
* @param environmentRefs
* EnvironmentRefs to use
- * @param systemParticipantListener
+ * @param resultEventListeners
* Listeners that await events from system participants
* @return
* A mapping from actor reference to it's according initialization data to
* be used when setting up the agents
*/
def gridAgents(
- context: ActorContext,
+ context: ActorContext[_],
environmentRefs: EnvironmentRefs,
- systemParticipantListener: Seq[ActorRef]
- ): Iterable[ActorRef]
+ resultEventListeners: Seq[ActorRef[ResultEvent]],
+ ): Iterable[ActorRef[GridAgentMessage]]
/** SIMONA links sub grids connected by a three winding transformer a bit
* different. Therefore, the internal node has to be set as superior node.
@@ -163,7 +164,7 @@ trait SimonaSetup {
new SubGridGate(
transformer,
transformer.getNodeInternal,
- gate.inferiorNode
+ gate.inferiorNode,
)
case _ => gate
}
diff --git a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala
index 246fda0101..322d02bc93 100644
--- a/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala
+++ b/src/main/scala/edu/ie3/simona/sim/setup/SimonaStandaloneSetup.scala
@@ -6,29 +6,25 @@
package edu.ie3.simona.sim.setup
-import org.apache.pekko.actor.typed.scaladsl.adapter.{
- ClassicActorContextOps,
- ClassicActorRefOps,
- TypedActorRefOps
-}
-import org.apache.pekko.actor.{ActorContext, ActorRef, ActorSystem}
import com.typesafe.config.Config
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.graph.SubGridTopologyGraph
import edu.ie3.datamodel.models.input.container.{GridContainer, ThermalGrid}
import edu.ie3.datamodel.models.input.thermal.ThermalBusInput
-import edu.ie3.simona.actor.SimonaActorNaming._
+import edu.ie3.simona.actor.SimonaActorNaming.RichActorRefFactory
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.agent.grid.GridAgent
+import edu.ie3.simona.agent.grid.GridAgentMessage.CreateGridAgent
+import edu.ie3.simona.agent.grid.{GridAgent, GridAgentMessage}
import edu.ie3.simona.api.ExtSimAdapter
import edu.ie3.simona.api.data.ExtData
import edu.ie3.simona.api.data.ev.{ExtEvData, ExtEvSimulation}
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.config.{ArgsParser, RefSystemParser, SimonaConfig}
-import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.event.listener.{ResultEventListener, RuntimeEventListener}
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
import edu.ie3.simona.exceptions.agent.GridAgentInitializationException
import edu.ie3.simona.io.grid.GridProvider
+import edu.ie3.simona.ontology.messages.SchedulerMessage
import edu.ie3.simona.ontology.messages.SchedulerMessage.ScheduleActivation
import edu.ie3.simona.scheduler.{ScheduleLock, Scheduler, TimeAdvancer}
import edu.ie3.simona.service.SimonaService
@@ -38,10 +34,19 @@ import edu.ie3.simona.service.primary.PrimaryServiceProxy
import edu.ie3.simona.service.primary.PrimaryServiceProxy.InitPrimaryServiceProxyStateData
import edu.ie3.simona.service.weather.WeatherService
import edu.ie3.simona.service.weather.WeatherService.InitWeatherServiceStateData
+import edu.ie3.simona.sim.SimonaSim
import edu.ie3.simona.util.ResultFileHierarchy
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.simona.util.TickUtil.RichZonedDateTime
import edu.ie3.util.TimeUtil
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.ActorContext
+import org.apache.pekko.actor.typed.scaladsl.adapter.{
+ ClassicActorRefOps,
+ TypedActorContextOps,
+ TypedActorRefOps,
+}
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
import java.util.concurrent.LinkedBlockingQueue
import scala.jdk.CollectionConverters._
@@ -53,24 +58,24 @@ import scala.jdk.CollectionConverters._
* @since 01.07.20
*/
class SimonaStandaloneSetup(
- override val buildActorSystem: () => ActorSystem,
+ val typeSafeConfig: Config,
simonaConfig: SimonaConfig,
resultFileHierarchy: ResultFileHierarchy,
runtimeEventQueue: Option[LinkedBlockingQueue[RuntimeEvent]] = None,
- override val args: Array[String]
+ override val args: Array[String],
) extends SimonaSetup {
override def gridAgents(
- context: ActorContext,
+ context: ActorContext[_],
environmentRefs: EnvironmentRefs,
- systemParticipantListener: Seq[ActorRef]
- ): Iterable[ActorRef] = {
+ resultEventListeners: Seq[ActorRef[ResultEvent]],
+ ): Iterable[ActorRef[GridAgentMessage]] = {
/* get the grid */
val subGridTopologyGraph = GridProvider
.gridFromConfig(
simonaConfig.simona.simulationName,
- simonaConfig.simona.input.grid.datasource
+ simonaConfig.simona.input.grid.datasource,
)
.getSubGridTopologyGraph
val thermalGridsByThermalBus = GridProvider.getThermalGridsFromConfig(
@@ -86,14 +91,14 @@ class SimonaStandaloneSetup(
subGridTopologyGraph,
context,
environmentRefs,
- systemParticipantListener
+ resultEventListeners,
)
val keys = ScheduleLock.multiKey(
context,
- environmentRefs.scheduler.toTyped,
+ environmentRefs.scheduler,
INIT_SIM_TICK,
- subGridTopologyGraph.vertexSet().size
+ subGridTopologyGraph.vertexSet().size,
)
/* build the initialization data */
@@ -115,7 +120,7 @@ class SimonaStandaloneSetup(
currentSubGrid,
throw new GridAgentInitializationException(
"Was asked to setup agent for sub grid " + currentSubGrid + ", but did not found it's actor reference."
- )
+ ),
)
val thermalGrids =
getThermalGrids(subGridContainer, thermalGridsByThermalBus)
@@ -126,30 +131,30 @@ class SimonaStandaloneSetup(
subGridToActorRefMap,
subGridGates,
configRefSystems,
- thermalGrids
+ thermalGrids,
)
- currentActorRef ! GridAgent.Create(gridAgentInitData, key)
+ currentActorRef ! CreateGridAgent(gridAgentInitData, key)
currentActorRef
}
}
override def primaryServiceProxy(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef = {
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef = {
val simulationStart = TimeUtil.withDefaults.toZonedDateTime(
simonaConfig.simona.time.startDateTime
)
- val primaryServiceProxy = context.simonaActorOf(
+ val primaryServiceProxy = context.toClassic.simonaActorOf(
PrimaryServiceProxy.props(
- scheduler,
+ scheduler.toClassic,
InitPrimaryServiceProxyStateData(
simonaConfig.simona.input.primary,
- simulationStart
+ simulationStart,
),
- simulationStart
+ simulationStart,
)
)
@@ -158,31 +163,31 @@ class SimonaStandaloneSetup(
}
override def weatherService(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef = {
- val weatherService = context.simonaActorOf(
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef = {
+ val weatherService = context.toClassic.simonaActorOf(
WeatherService.props(
- scheduler,
+ scheduler.toClassic,
TimeUtil.withDefaults
.toZonedDateTime(simonaConfig.simona.time.startDateTime),
TimeUtil.withDefaults
- .toZonedDateTime(simonaConfig.simona.time.endDateTime)
+ .toZonedDateTime(simonaConfig.simona.time.endDateTime),
)
)
weatherService ! SimonaService.Create(
InitWeatherServiceStateData(
simonaConfig.simona.input.weather.datasource
),
- ScheduleLock.singleKey(context, scheduler.toTyped, INIT_SIM_TICK)
+ ScheduleLock.singleKey(context, scheduler, INIT_SIM_TICK),
)
weatherService
}
override def extSimulations(
- context: ActorContext,
- scheduler: ActorRef
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
): ExtSimSetupData = {
val jars = ExtSimLoader.scanInputFolder()
@@ -191,28 +196,28 @@ class SimonaStandaloneSetup(
val (extSimAdapters, extDataServices) =
extLinks.zipWithIndex.map { case (extLink, index) =>
// external simulation always needs at least an ExtSimAdapter
- val extSimAdapter = context.simonaActorOf(
- ExtSimAdapter.props(scheduler),
- s"$index"
+ val extSimAdapter = context.toClassic.simonaActorOf(
+ ExtSimAdapter.props(scheduler.toClassic),
+ s"$index",
)
val extSimAdapterData = new ExtSimAdapterData(extSimAdapter, args)
// send init data right away, init activation is scheduled
extSimAdapter ! ExtSimAdapter.Create(
extSimAdapterData,
- ScheduleLock.singleKey(context, scheduler.toTyped, INIT_SIM_TICK)
+ ScheduleLock.singleKey(context, scheduler, INIT_SIM_TICK),
)
// setup data services that belong to this external simulation
val (extData, extDataInit): (
Iterable[ExtData],
- Iterable[(Class[_ <: SimonaService[_]], ActorRef)]
+ Iterable[(Class[_ <: SimonaService[_]], ClassicRef)],
) =
extLink.getExtDataSimulations.asScala.zipWithIndex.map {
case (_: ExtEvSimulation, dIndex) =>
- val extEvDataService = context.simonaActorOf(
- ExtEvDataService.props(scheduler),
- s"$index-$dIndex"
+ val extEvDataService = context.toClassic.simonaActorOf(
+ ExtEvDataService.props(scheduler.toClassic),
+ s"$index-$dIndex",
)
val extEvData = new ExtEvData(extEvDataService, extSimAdapter)
@@ -220,9 +225,9 @@ class SimonaStandaloneSetup(
InitExtEvData(extEvData),
ScheduleLock.singleKey(
context,
- scheduler.toTyped,
- INIT_SIM_TICK
- )
+ scheduler,
+ INIT_SIM_TICK,
+ ),
)
(extEvData, (classOf[ExtEvDataService], extEvDataService))
@@ -230,7 +235,7 @@ class SimonaStandaloneSetup(
extLink.getExtSimulation.setup(
extSimAdapterData,
- extData.toList.asJava
+ extData.toList.asJava,
)
// starting external simulation
@@ -244,10 +249,10 @@ class SimonaStandaloneSetup(
}
override def timeAdvancer(
- context: ActorContext,
- simulation: ActorRef,
- runtimeEventListener: org.apache.pekko.actor.typed.ActorRef[RuntimeEvent]
- ): org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming] = {
+ context: ActorContext[_],
+ simulation: ActorRef[SimonaSim.SimulationEnded.type],
+ runtimeEventListener: ActorRef[RuntimeEvent],
+ ): ActorRef[TimeAdvancer.Request] = {
val startDateTime = TimeUtil.withDefaults.toZonedDateTime(
simonaConfig.simona.time.startDateTime
)
@@ -260,79 +265,77 @@ class SimonaStandaloneSetup(
simulation,
Some(runtimeEventListener),
simonaConfig.simona.time.schedulerReadyCheckWindow,
- endDateTime.toTick(startDateTime)
+ endDateTime.toTick(startDateTime),
),
- TimeAdvancer.getClass.getSimpleName
+ TimeAdvancer.getClass.getSimpleName,
)
}
override def scheduler(
- context: ActorContext,
- timeAdvancer: org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming]
- ): ActorRef =
+ context: ActorContext[_],
+ timeAdvancer: ActorRef[TimeAdvancer.Request],
+ ): ActorRef[SchedulerMessage] =
context
.spawn(
- Scheduler(
- timeAdvancer
- ),
- Scheduler.getClass.getSimpleName
+ Scheduler(timeAdvancer),
+ Scheduler.getClass.getSimpleName,
)
- .toClassic
override def runtimeEventListener(
- context: ActorContext
- ): org.apache.pekko.actor.typed.ActorRef[RuntimeEvent] =
+ context: ActorContext[_]
+ ): ActorRef[RuntimeEventListener.Request] =
context
.spawn(
RuntimeEventListener(
simonaConfig.simona.runtime.listener,
runtimeEventQueue,
- startDateTimeString = simonaConfig.simona.time.startDateTime
+ startDateTimeString = simonaConfig.simona.time.startDateTime,
),
- RuntimeEventListener.getClass.getSimpleName
+ RuntimeEventListener.getClass.getSimpleName,
)
- override def systemParticipantsListener(
- context: ActorContext
- ): Seq[ActorRef] = {
+ override def resultEventListener(
+ context: ActorContext[_]
+ ): Seq[ActorRef[ResultEventListener.Request]] = {
// append ResultEventListener as well to write raw output files
ArgsParser
.parseListenerConfigOption(simonaConfig.simona.event.listener)
.zipWithIndex
.map { case ((listenerCompanion, events), index) =>
- context.simonaActorOf(
- listenerCompanion.props(events),
- index.toString
- )
+ context.toClassic
+ .simonaActorOf(
+ listenerCompanion.props(events),
+ index.toString,
+ )
+ .toTyped
}
.toSeq :+ context
.spawn(
ResultEventListener(
resultFileHierarchy
),
- ResultEventListener.getClass.getSimpleName
+ ResultEventListener.getClass.getSimpleName,
)
- .toClassic
}
def buildSubGridToActorRefMap(
subGridTopologyGraph: SubGridTopologyGraph,
- context: ActorContext,
+ context: ActorContext[_],
environmentRefs: EnvironmentRefs,
- systemParticipantListener: Seq[ActorRef]
- ): Map[Int, ActorRef] = {
+ resultEventListeners: Seq[ActorRef[ResultEvent]],
+ ): Map[Int, ActorRef[GridAgentMessage]] = {
subGridTopologyGraph
.vertexSet()
.asScala
.map(subGridContainer => {
val gridAgentRef =
- context.simonaActorOf(
- GridAgent.props(
+ context.spawn(
+ GridAgent(
environmentRefs,
simonaConfig,
- systemParticipantListener
+ resultEventListeners,
),
- subGridContainer.getSubnet.toString
+ subGridContainer.getSubnet.toString,
)
subGridContainer.getSubnet -> gridAgentRef
})
@@ -349,7 +352,7 @@ class SimonaStandaloneSetup(
*/
private def getThermalGrids(
grid: GridContainer,
- thermalGridByBus: Map[ThermalBusInput, ThermalGrid]
+ thermalGridByBus: Map[ThermalBusInput, ThermalGrid],
): Seq[ThermalGrid] = {
grid.getSystemParticipants.getHeatPumps.asScala
.flatten(hpInput => thermalGridByBus.get(hpInput.getThermalBus))
@@ -366,13 +369,13 @@ object SimonaStandaloneSetup extends LazyLogging with SetupHelper {
typeSafeConfig: Config,
resultFileHierarchy: ResultFileHierarchy,
runtimeEventQueue: Option[LinkedBlockingQueue[RuntimeEvent]] = None,
- mainArgs: Array[String] = Array.empty[String]
+ mainArgs: Array[String] = Array.empty[String],
): SimonaStandaloneSetup =
new SimonaStandaloneSetup(
- () => ActorSystem("simona", typeSafeConfig),
+ typeSafeConfig,
SimonaConfig(typeSafeConfig),
resultFileHierarchy,
runtimeEventQueue,
- mainArgs
+ mainArgs,
)
}
diff --git a/src/main/scala/edu/ie3/simona/util/CollectionUtils.scala b/src/main/scala/edu/ie3/simona/util/CollectionUtils.scala
index 835f048cf6..6402ea7b2a 100644
--- a/src/main/scala/edu/ie3/simona/util/CollectionUtils.scala
+++ b/src/main/scala/edu/ie3/simona/util/CollectionUtils.scala
@@ -106,7 +106,7 @@ object CollectionUtils {
*/
def closestKeyValuePairs[A <: Quantity[A], O <: Quantity[O]](
map: Map[A, O],
- key: A
+ key: A,
): Seq[(A, O)] = {
import scala.collection.immutable.TreeMap
implicit val ordering: Double.IeeeOrdering.type =
@@ -115,7 +115,7 @@ object CollectionUtils {
Seq(
treeMap.rangeTo(key).lastOption,
- treeMap.rangeFrom(key).headOption
+ treeMap.rangeFrom(key).headOption,
).flatten.distinct
.map { case (k, v) => (k, v) }
}
diff --git a/src/main/scala/edu/ie3/simona/util/ConfigUtil.scala b/src/main/scala/edu/ie3/simona/util/ConfigUtil.scala
index 0e3a8e412b..10acb7ddaf 100644
--- a/src/main/scala/edu/ie3/simona/util/ConfigUtil.scala
+++ b/src/main/scala/edu/ie3/simona/util/ConfigUtil.scala
@@ -10,13 +10,13 @@ import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.io.connectors.{
CouchbaseConnector,
InfluxDbConnector,
- SqlConnector
+ SqlConnector,
}
import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
Transformer2WResult,
- Transformer3WResult
+ Transformer3WResult,
}
import edu.ie3.datamodel.models.result.{NodeResult, ResultEntity}
import edu.ie3.simona.config.SimonaConfig
@@ -39,7 +39,7 @@ object ConfigUtil {
final case class ParticipantConfigUtil private (
private val configs: Map[UUID, SimonaConfig.BaseRuntimeConfig],
- private val defaultConfigs: Map[Class[_], BaseRuntimeConfig]
+ private val defaultConfigs: Map[Class[_], BaseRuntimeConfig],
) {
/** Queries for a [[BaseRuntimeConfig]] of type [[T]], that applies for the
@@ -88,7 +88,7 @@ object ConfigUtil {
subConfig.fixedFeedIn.individualConfigs,
subConfig.pv.individualConfigs,
subConfig.evcs.individualConfigs,
- subConfig.wec.individualConfigs
+ subConfig.wec.individualConfigs,
).reduceOption(_ ++ _).getOrElse(Seq.empty)
),
Seq(
@@ -97,8 +97,8 @@ object ConfigUtil {
subConfig.pv.defaultConfig,
subConfig.evcs.defaultConfig,
subConfig.wec.defaultConfig,
- subConfig.hp.defaultConfig
- ).map { conf => conf.getClass -> conf }.toMap
+ subConfig.hp.defaultConfig,
+ ).map { conf => conf.getClass -> conf }.toMap,
)
}
@@ -127,8 +127,8 @@ object ConfigUtil {
private val defaultConfig: NotifierConfig,
private val configs: Map[
NotifierIdentifier.Value,
- NotifierConfig
- ]
+ NotifierConfig,
+ ],
) {
def getOrDefault(
notifierId: NotifierIdentifier.Value
@@ -147,7 +147,7 @@ object ConfigUtil {
NotifierIdentifier.values -- configs.flatMap {
case (
notifierId,
- NotifierConfig(resultInfo, _)
+ NotifierConfig(resultInfo, _, _),
) if !resultInfo =>
Some(notifierId)
case _ => None
@@ -157,7 +157,7 @@ object ConfigUtil {
configs.flatMap {
case (
notifierId,
- NotifierConfig(resultInfo, _)
+ NotifierConfig(resultInfo, _, _),
) if resultInfo =>
Some(notifierId)
case _ => None
@@ -178,24 +178,30 @@ object ConfigUtil {
case ParticipantBaseOutputConfig(
_,
simulationResult,
- powerRequestReply
+ flexResult,
+ powerRequestReply,
) =>
- NotifierConfig(simulationResult, powerRequestReply)
+ NotifierConfig(simulationResult, powerRequestReply, flexResult)
}
val configMap = subConfig.individualConfigs.map {
case ParticipantBaseOutputConfig(
notifier,
simulationResult,
- powerRequestReply
+ flexResult,
+ powerRequestReply,
) =>
try {
val id = NotifierIdentifier(notifier)
- id -> NotifierConfig(simulationResult, powerRequestReply)
+ id -> NotifierConfig(
+ simulationResult,
+ powerRequestReply,
+ flexResult,
+ )
} catch {
case e: NoSuchElementException =>
throw new InvalidConfigParameterException(
s"Cannot parse $notifier to known result event notifier.",
- e
+ e,
)
}
}.toMap
@@ -207,18 +213,26 @@ object ConfigUtil {
): OutputConfigUtil = {
val defaultConfig = subConfig.defaultConfig match {
case SimpleOutputConfig(_, simulationResult) =>
- NotifierConfig(simulationResult, powerRequestReply = false)
+ NotifierConfig(
+ simulationResult,
+ powerRequestReply = false,
+ flexResult = false,
+ )
}
val configMap = subConfig.individualConfigs.map {
case SimpleOutputConfig(notifier, simulationResult) =>
try {
val id = NotifierIdentifier(notifier)
- id -> NotifierConfig(simulationResult, powerRequestReply = false)
+ id -> NotifierConfig(
+ simulationResult,
+ powerRequestReply = false,
+ flexResult = false,
+ )
} catch {
case e: NoSuchElementException =>
throw new InvalidConfigParameterException(
s"Cannot parse $notifier to known result event notifier.",
- e
+ e,
)
}
}.toMap
@@ -281,7 +295,7 @@ object ConfigUtil {
*/
def checkBaseCsvParams(
params: SimonaConfig.BaseCsvParams,
- csvParamsName: String
+ csvParamsName: String,
): Unit = params match {
case BaseCsvParams(csvSep, directoryPath, _) =>
if (!(csvSep.equals(";") || csvSep.equals(",")))
@@ -301,7 +315,7 @@ object ConfigUtil {
def checkCsvParams(
csvParamsName: String,
csvSep: String,
- folderPath: String
+ folderPath: String,
): Unit = {
if (!(csvSep.equals(";") || csvSep.equals(",")))
throw new InvalidConfigParameterException(
@@ -356,7 +370,7 @@ object ConfigUtil {
case Failure(exception) =>
throw new IllegalArgumentException(
s"Unable to reach configured SQL database with url '${sql.jdbcUrl}' and user name '${sql.userName}'. Exception: $exception",
- exception
+ exception,
)
case Success(connection) =>
val validConnection = connection.isValid(5000)
@@ -406,13 +420,13 @@ object ConfigUtil {
couchbase.url,
couchbase.bucketName,
couchbase.userName,
- couchbase.password
+ couchbase.password,
)
) match {
case Failure(exception) =>
throw new IllegalArgumentException(
s"Unable to reach configured Couchbase database with url '${couchbase.url}', bucket '${couchbase.bucketName}' and user name '${couchbase.userName}'. Exception: $exception",
- exception
+ exception,
)
case Success(connector) =>
val validConnection = connector.isConnectionValid
@@ -431,7 +445,7 @@ object ConfigUtil {
def checkInfluxDb1xParams(
influxDb1xParamsName: String,
url: String,
- database: String
+ database: String,
): Unit = {
Try(
new InfluxDbConnector(url, database).isConnectionValid
@@ -439,7 +453,7 @@ object ConfigUtil {
case Failure(exception) =>
throw new IllegalArgumentException(
s"Unable to reach configured influxDb1x with url '$url' for '$influxDb1xParamsName' configuration and database '$database'. Exception: $exception",
- exception
+ exception,
)
case Success(validConnection) if !validConnection =>
throw new IllegalArgumentException(
@@ -454,7 +468,7 @@ object ConfigUtil {
def checkKafkaParams(
kafkaParams: KafkaParams,
- topics: Seq[String]
+ topics: Seq[String],
): Unit = {
try {
UUID.fromString(kafkaParams.runId)
@@ -462,7 +476,7 @@ object ConfigUtil {
case e: IllegalArgumentException =>
throw new InvalidConfigParameterException(
s"The UUID '${kafkaParams.runId}' cannot be parsed as it is invalid.",
- e
+ e,
)
}
@@ -477,17 +491,17 @@ object ConfigUtil {
case Failure(ke: KafkaException) =>
throw new InvalidConfigParameterException(
s"Exception creating kafka client for broker ${kafkaParams.bootstrapServers}.",
- ke
+ ke,
)
case Failure(ee: ExecutionException) =>
throw new InvalidConfigParameterException(
s"Connection with kafka broker ${kafkaParams.bootstrapServers} failed.",
- ee
+ ee,
)
case Failure(other) =>
throw new InvalidConfigParameterException(
s"Checking kafka config failed with unexpected exception.",
- other
+ other,
)
case Success(missingTopics) if missingTopics.nonEmpty =>
throw new InvalidConfigParameterException(
diff --git a/src/main/scala/edu/ie3/simona/util/EntityMapperUtil.scala b/src/main/scala/edu/ie3/simona/util/EntityMapperUtil.scala
index a6e3398dcc..e52316c88a 100644
--- a/src/main/scala/edu/ie3/simona/util/EntityMapperUtil.scala
+++ b/src/main/scala/edu/ie3/simona/util/EntityMapperUtil.scala
@@ -25,7 +25,7 @@ case object EntityMapperUtil {
ChpPlant -> classOf[ChpResult],
Storage -> classOf[StorageResult],
Hp -> classOf[HpResult],
- House -> classOf[ThermalHouseResult]
+ House -> classOf[ThermalHouseResult],
)
/** Get the classes of [[ResultEntity]], that are issued by the notifier, that
@@ -43,6 +43,6 @@ case object EntityMapperUtil {
notifierId,
throw new NoSuchElementException(
s"Cannot determine result entity class of notifier $notifierId"
- )
+ ),
)
}
diff --git a/src/main/scala/edu/ie3/simona/util/ReceiveDataMap.scala b/src/main/scala/edu/ie3/simona/util/ReceiveDataMap.scala
new file mode 100644
index 0000000000..1f7fca229e
--- /dev/null
+++ b/src/main/scala/edu/ie3/simona/util/ReceiveDataMap.scala
@@ -0,0 +1,62 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.util
+
+/** Map that holds data that an actor is expected to receive over time and
+ * efficiently determines if all expected data has been received.
+ * @param expectedKeys
+ * The keys for which data is expected
+ * @param receivedData
+ * The map holding all received data so far
+ * @tparam K
+ * The type of the key
+ * @tparam V
+ * The type of the value
+ */
+final case class ReceiveDataMap[K, V](
+ private val expectedKeys: Set[K],
+ receivedData: Map[K, V],
+) {
+ def isComplete: Boolean = expectedKeys.isEmpty
+
+ def nonComplete: Boolean = expectedKeys.nonEmpty
+
+ def addData(
+ key: K,
+ value: V,
+ ): ReceiveDataMap[K, V] = {
+
+ if (!expectedKeys.contains(key))
+ throw new RuntimeException(
+ s"Received value $value for key $key, but no data has been expected for this key."
+ )
+
+ copy(
+ expectedKeys = expectedKeys.excl(key),
+ receivedData.updated(key, value),
+ )
+ }
+
+}
+
+object ReceiveDataMap {
+
+ def apply[K, V](
+ expectedKeys: Set[K]
+ ): ReceiveDataMap[K, V] =
+ ReceiveDataMap(
+ expectedKeys = expectedKeys,
+ receivedData = Map.empty,
+ )
+
+ def empty[K, V]: ReceiveDataMap[K, V] =
+ ReceiveDataMap(
+ expectedKeys = Set.empty,
+ receivedData = Map.empty,
+ )
+
+}
diff --git a/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala b/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala
index 6f27310673..171c5835a2 100644
--- a/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala
+++ b/src/main/scala/edu/ie3/simona/util/ResultFileHierarchy.scala
@@ -13,7 +13,7 @@ import com.typesafe.config.{ConfigRenderOptions, Config => TypesafeConfig}
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.datamodel.io.naming.{
EntityPersistenceNamingStrategy,
- FileNamingStrategy
+ FileNamingStrategy,
}
import edu.ie3.datamodel.models.result.ResultEntity
import edu.ie3.simona.exceptions.FileHierarchyException
@@ -39,7 +39,7 @@ final case class ResultFileHierarchy(
private val resultEntityPathConfig: ResultEntityPathConfig,
private val configureLogger: String => Unit = LogbackConfiguration.default,
private val addTimeStampToOutputDir: Boolean = true,
- private val createDirs: Boolean = false
+ private val createDirs: Boolean = false,
) extends LazyLogging {
private val fileSeparator: String = File.separator
@@ -76,8 +76,8 @@ final case class ResultFileHierarchy(
resultEntityClass,
csv,
rawOutputDataDir,
- fileSeparator
- )
+ fileSeparator,
+ ),
)
)
.toMap
@@ -101,7 +101,7 @@ final case class ResultFileHierarchy(
graphOutputDir,
kpiOutputDir,
tmpDir,
- logOutputDir
+ logOutputDir,
)
// needs to be the latest call because otherwise the values are null as they are not initialized yet
@@ -152,7 +152,7 @@ object ResultFileHierarchy extends LazyLogging {
*/
final case class ResultEntityPathConfig(
resultEntitiesToConsider: Set[Class[_ <: ResultEntity]],
- resultSinkType: ResultSinkType
+ resultSinkType: ResultSinkType,
)
/** @param modelClass
@@ -171,7 +171,7 @@ object ResultFileHierarchy extends LazyLogging {
modelClass: Class[_ <: ResultEntity],
csvSink: Csv,
rawOutputDataDir: String,
- fileSeparator: String
+ fileSeparator: String,
): String = {
val fileEnding =
if (csvSink.fileFormat.startsWith("."))
@@ -180,7 +180,7 @@ object ResultFileHierarchy extends LazyLogging {
val namingStrategy = new FileNamingStrategy(
new EntityPersistenceNamingStrategy(
csvSink.filePrefix,
- csvSink.fileSuffix
+ csvSink.fileSuffix,
)
)
val filename =
@@ -209,7 +209,7 @@ object ResultFileHierarchy extends LazyLogging {
*/
def prepareDirectories(
config: TypesafeConfig,
- resultFileHierarchy: ResultFileHierarchy
+ resultFileHierarchy: ResultFileHierarchy,
): Unit = {
// create output directories if they are not present yet
if (!runOutputDirExists(resultFileHierarchy))
@@ -217,7 +217,7 @@ object ResultFileHierarchy extends LazyLogging {
logger.info(
"Processing configs for simulation: {}.",
- config.getString("simona.simulationName")
+ config.getString("simona.simulationName"),
)
val outFile =
diff --git a/src/main/scala/edu/ie3/simona/util/TickUtil.scala b/src/main/scala/edu/ie3/simona/util/TickUtil.scala
index c95fc3b987..34f16d8e6e 100644
--- a/src/main/scala/edu/ie3/simona/util/TickUtil.scala
+++ b/src/main/scala/edu/ie3/simona/util/TickUtil.scala
@@ -19,8 +19,7 @@ import java.time.temporal.ChronoUnit
object TickUtil {
/** Provides conversions from ZonedDateTime into ticks (actually seconds) */
- implicit class RichZonedDateTime(private val zdt: ZonedDateTime)
- extends AnyVal {
+ implicit class RichZonedDateTime(private val zdt: ZonedDateTime) {
/** Calculates the difference between this date time and the provided date
* time in ticks (= actual seconds)
@@ -33,7 +32,7 @@ object TickUtil {
/** Provides conversions from ticks (seconds) into instances of
* [[ZonedDateTime]]
*/
- implicit class TickLong(private val tick: Long) extends AnyVal {
+ implicit class TickLong(private val tick: Long) {
/** Calculates the current [[ZonedDateTime]] based on this tick */
def toDateTime(implicit startDateTime: ZonedDateTime): ZonedDateTime =
@@ -46,7 +45,7 @@ object TickUtil {
/** Calculate the length for the time interval */
def durationUntil(
otherTick: Long,
- tickDuration: Time = Seconds(1d)
+ tickDuration: Time = Seconds(1d),
): Time =
tickDuration * (otherTick - tick).toDouble
diff --git a/src/main/scala/edu/ie3/util/scala/DoubleUtils.scala b/src/main/scala/edu/ie3/util/scala/DoubleUtils.scala
deleted file mode 100644
index 67bc7cf7cf..0000000000
--- a/src/main/scala/edu/ie3/util/scala/DoubleUtils.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * © 2022. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.util.scala
-
-@deprecated("Use implementation in power system utils package")
-object DoubleUtils {
- implicit class ImplicitDouble(d: Double) {
- def ~=(other: Double)(implicit precision: Double): Boolean =
- (d - other).abs <= precision
- def !~=(other: Double)(implicit precision: Double): Boolean =
- (d - other).abs > precision
- }
-}
diff --git a/src/main/scala/edu/ie3/util/scala/OperationInterval.scala b/src/main/scala/edu/ie3/util/scala/OperationInterval.scala
index fd786bae26..880f441460 100644
--- a/src/main/scala/edu/ie3/util/scala/OperationInterval.scala
+++ b/src/main/scala/edu/ie3/util/scala/OperationInterval.scala
@@ -34,6 +34,6 @@ final case class OperationInterval(start: java.lang.Long, end: java.lang.Long)
def getEnd: Long = getUpper
}
-case object OperationInterval {
+object OperationInterval {
def apply(start: Long, end: Long) = new OperationInterval(start, end)
}
diff --git a/src/main/scala/edu/ie3/util/scala/ReflectionTools.scala b/src/main/scala/edu/ie3/util/scala/ReflectionTools.scala
index d37280e16d..6ed62dc580 100644
--- a/src/main/scala/edu/ie3/util/scala/ReflectionTools.scala
+++ b/src/main/scala/edu/ie3/util/scala/ReflectionTools.scala
@@ -17,11 +17,9 @@ import scala.util.Try
object ReflectionTools {
def identifyValidCompanions(classNames: Iterable[String]): Iterable[Any] = {
- val validCompanionObjects =
- classNames.flatMap(name => resolveClassNameToCompanion(name)).collect {
- case companion: Any => companion
- }
- validCompanionObjects
+ classNames.flatMap(name => resolveClassNameToCompanion(name)).collect {
+ case companion: Any => companion
+ }
}
def resolveClassNameToCompanion(className: String): Option[Any] = {
@@ -33,10 +31,9 @@ object ReflectionTools {
)
)
val mirror = universe.runtimeMirror(getClass.getClassLoader)
- val companionObj = Try(
+ Try(
mirror.reflectModule(mirror.moduleSymbol(clazz)).instance
).toOption
- companionObj
}
/** Determine the field and their value of the provided object instance
@@ -54,7 +51,7 @@ object ReflectionTools {
*/
def classFieldToVal[A](a: A)(implicit
tt: TypeTag[A],
- ct: ClassTag[A]
+ ct: ClassTag[A],
): Map[universe.MethodSymbol, Any] = {
val members = tt.tpe.members.collect {
case m if m.isMethod && m.asMethod.isCaseAccessor => m.asMethod
diff --git a/src/main/scala/edu/ie3/util/scala/Scope.scala b/src/main/scala/edu/ie3/util/scala/Scope.scala
new file mode 100644
index 0000000000..0ed4889c17
--- /dev/null
+++ b/src/main/scala/edu/ie3/util/scala/Scope.scala
@@ -0,0 +1,44 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.util.scala
+
+/** Class that introduces functional ''mapping'' if there's an object that is
+ * not wrapped in a monad, such as [[Either]] or [[Option]].
+ *
+ * This class is useful when code should be divided into multiple pieces that
+ * each have their own scope, so that objects of the same class are not
+ * confused. For example, in the following code snippet, ''a'', ''b'', ''c''
+ * and ''d'' can be easily confused, which can lead to errors that are hard to
+ * trace.
+ *
+ * {{{
+ * val a: Foo = create()
+ * val b: Foo = transform(a)
+ * val c: Foo = b.doStuff()
+ * val d: Foo = maybeBar.calculate(c)
+ * }}}
+ *
+ * When using a [[Scope]], the variables cannot be confused anymore:
+ *
+ * {{{
+ * val d: Foo = Scope(create())
+ * .map(transform)
+ * .map(_.doStuff)
+ * .map(maybeBar.calculate)
+ * .get
+ * }}}
+ *
+ * @param obj
+ * The object that is held
+ * @tparam T
+ * The type of the object
+ */
+final case class Scope[T](private val obj: T) {
+ def map[B](f: T => B): Scope[B] = Scope(f(obj))
+
+ def get: T = obj
+}
diff --git a/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala b/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala
index bdf18abdb2..5aeaf9591a 100644
--- a/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala
+++ b/src/main/scala/edu/ie3/util/scala/collection/immutable/PrioritySwitchBiSet.scala
@@ -42,7 +42,7 @@ final case class PrioritySwitchBiSet[K, V](
private val orderedValues: immutable.Vector[V],
private val valueToIndex: Map[V, Int],
private val queue: immutable.SortedMap[K, immutable.SortedSet[Int]],
- private val back: Map[V, K]
+ private val back: Map[V, K],
) {
/** Get the first key of the queue, if the queue is not empty.
@@ -107,7 +107,7 @@ final case class PrioritySwitchBiSet[K, V](
copy(
queue = updatedQueue,
- back = updatedBack
+ back = updatedBack,
)
}
.getOrElse(this)
@@ -120,9 +120,9 @@ final case class PrioritySwitchBiSet[K, V](
(
copy(
orderedValues = orderedValues.appended(value),
- valueToIndex = valueToIndex.updated(value, newIndex)
+ valueToIndex = valueToIndex.updated(value, newIndex),
),
- newIndex
+ newIndex,
)
case i =>
(this, i)
@@ -134,7 +134,7 @@ final case class PrioritySwitchBiSet[K, V](
updatedStruct.copy(
queue = queue.updated(key, updatedSet),
- back = back.updated(value, key)
+ back = back.updated(value, key),
)
}
@@ -165,7 +165,7 @@ final case class PrioritySwitchBiSet[K, V](
(
firstValue,
- copy(queue = updatedQueue, back = back.removed(firstValue))
+ copy(queue = updatedQueue, back = back.removed(firstValue)),
)
}
}
@@ -208,6 +208,6 @@ object PrioritySwitchBiSet {
Vector.empty,
Map.empty,
immutable.SortedMap.empty,
- Map.empty
+ Map.empty,
)
}
diff --git a/src/main/scala/edu/ie3/util/scala/collection/mutable/PriorityMultiBiSet.scala b/src/main/scala/edu/ie3/util/scala/collection/mutable/PriorityMultiBiSet.scala
index d25e20a2d1..bd6bd8feab 100644
--- a/src/main/scala/edu/ie3/util/scala/collection/mutable/PriorityMultiBiSet.scala
+++ b/src/main/scala/edu/ie3/util/scala/collection/mutable/PriorityMultiBiSet.scala
@@ -32,7 +32,7 @@ import scala.collection.{SortedSet, mutable}
final case class PriorityMultiBiSet[K, V] private (
private val queue: mutable.SortedSet[K],
private val table: mutable.HashMap[K, mutable.Set[V]],
- private val back: mutable.HashMap[V, K]
+ private val back: mutable.HashMap[V, K],
) {
/** Get the first key of the queue, if the queue is not empty. Runs in O(1).
@@ -161,7 +161,7 @@ object PriorityMultiBiSet {
PriorityMultiBiSet(
mutable.SortedSet.empty[K],
mutable.HashMap[K, mutable.Set[V]](),
- mutable.HashMap[V, K]()
+ mutable.HashMap[V, K](),
)
/** Creates and returns an empty PriorityMultiQueue for given types. The
@@ -184,17 +184,17 @@ object PriorityMultiBiSet {
*/
def empty[K: Ordering, V](
initialKeyCapacity: Int,
- loadFactor: Double = mutable.HashMap.defaultLoadFactor
+ loadFactor: Double = mutable.HashMap.defaultLoadFactor,
): PriorityMultiBiSet[K, V] =
PriorityMultiBiSet(
mutable.SortedSet.empty[K],
new mutable.HashMap[K, mutable.Set[V]](
initialKeyCapacity,
- loadFactor
+ loadFactor,
),
new mutable.HashMap[V, K](
initialKeyCapacity,
- loadFactor
- )
+ loadFactor,
+ ),
)
}
diff --git a/src/main/scala/edu/ie3/util/scala/io/CsvDataSourceAdapter.java b/src/main/scala/edu/ie3/util/scala/io/CsvDataSourceAdapter.java
deleted file mode 100644
index 16ea4883b6..0000000000
--- a/src/main/scala/edu/ie3/util/scala/io/CsvDataSourceAdapter.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * © 2023. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.util.scala.io;
-
-import edu.ie3.datamodel.io.naming.FileNamingStrategy;
-import edu.ie3.datamodel.io.source.csv.CsvDataSource;
-import java.nio.file.Path;
-
-public class CsvDataSourceAdapter extends CsvDataSource {
-
- public CsvDataSourceAdapter(
- String csvSep, Path folderPath, FileNamingStrategy fileNamingStrategy) {
- super(csvSep, folderPath, fileNamingStrategy);
- }
-}
diff --git a/src/main/scala/edu/ie3/util/scala/io/GraphicDataCleaner.scala b/src/main/scala/edu/ie3/util/scala/io/GraphicDataCleaner.scala
index 7737599d18..0e1515df69 100644
--- a/src/main/scala/edu/ie3/util/scala/io/GraphicDataCleaner.scala
+++ b/src/main/scala/edu/ie3/util/scala/io/GraphicDataCleaner.scala
@@ -31,20 +31,20 @@ object GraphicDataCleaner {
/* setup */
val dataSource: CsvDataSource =
- new CsvDataSourceAdapter(csvSep, baseFolder, fileNamingStrategy)
+ new CsvDataSource(csvSep, baseFolder, fileNamingStrategy)
val csvTypeSource: TypeSource =
new TypeSource(dataSource)
val csvRawGridSource: RawGridSource = new RawGridSource(
csvTypeSource,
- dataSource
+ dataSource,
)
val csvGraphicSource: GraphicSource = new GraphicSource(
csvTypeSource,
csvRawGridSource,
- dataSource
+ dataSource,
)
/* read - by default, the csvGraphicSource only returns valid, that means elements with all
diff --git a/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala b/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala
index e129c8dff6..f7d6707254 100644
--- a/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala
+++ b/src/main/scala/edu/ie3/util/scala/io/ScalaReflectionSerde.scala
@@ -9,7 +9,7 @@ package edu.ie3.util.scala.io
import com.sksamuel.avro4s.RecordFormat
import io.confluent.kafka.streams.serdes.avro.{
GenericAvroDeserializer,
- GenericAvroSerializer
+ GenericAvroSerializer,
}
import org.apache.kafka.common.serialization.{Deserializer, Serializer}
@@ -24,7 +24,7 @@ object ScalaReflectionSerde {
override def configure(
configs: java.util.Map[String, _],
- isKey: Boolean
+ isKey: Boolean,
): Unit = inner.configure(configs, isKey)
override def serialize(topic: String, maybeData: T): Array[Byte] =
@@ -43,7 +43,7 @@ object ScalaReflectionSerde {
override def configure(
configs: java.util.Map[String, _],
- isKey: Boolean
+ isKey: Boolean,
): Unit = inner.configure(configs, isKey)
override def deserialize(topic: String, maybeData: Array[Byte]): T =
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/EnergyDensity.scala b/src/main/scala/edu/ie3/util/scala/quantities/EnergyDensity.scala
index 56806ea8da..2f0ae95ff2 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/EnergyDensity.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/EnergyDensity.scala
@@ -20,7 +20,7 @@ import scala.util.Try
*/
final class EnergyDensity private (
val value: Double,
- val unit: EnergyDensityUnit
+ val unit: EnergyDensityUnit,
) extends Quantity[EnergyDensity] {
def dimension: EnergyDensity.type = EnergyDensity
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/EnergyPrice.scala b/src/main/scala/edu/ie3/util/scala/quantities/EnergyPrice.scala
index 96a32d408a..eddd58a99b 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/EnergyPrice.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/EnergyPrice.scala
@@ -17,7 +17,7 @@ import scala.util.Try
final class EnergyPrice private (
val value: Double,
- val unit: EnergyPriceUnit
+ val unit: EnergyPriceUnit,
) extends squants.Quantity[EnergyPrice] {
def dimension: EnergyPrice.type = EnergyPrice
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/QuantityUtil.scala b/src/main/scala/edu/ie3/util/scala/quantities/QuantityUtil.scala
index d5aea08fdd..4ca6d188fb 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/QuantityUtil.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/QuantityUtil.scala
@@ -61,7 +61,7 @@ object QuantityUtil {
] with TimeIntegral[Q]](
values: Map[Long, Q],
windowStart: Long,
- windowEnd: Long
+ windowEnd: Long,
): Try[Q] = {
if (windowStart == windowEnd)
Failure(
@@ -76,7 +76,7 @@ object QuantityUtil {
integrate[Q, QI](
values,
windowStart,
- windowEnd
+ windowEnd,
) / Seconds(windowEnd - windowStart)
}
}
@@ -102,7 +102,7 @@ object QuantityUtil {
] with TimeIntegral[Q]](
values: Map[Long, Q],
windowStart: Long,
- windowEnd: Long
+ windowEnd: Long,
): QI = {
/** Case class to hold current state of integration
@@ -117,7 +117,7 @@ object QuantityUtil {
final case class IntegrationState(
currentIntegral: QI,
lastTick: Long,
- lastValue: Q
+ lastValue: Q,
)
/* Determine the starting and ending value for the integral */
@@ -142,12 +142,12 @@ object QuantityUtil {
IntegrationState(
startValue * Hours(0),
windowStart,
- startValue
+ startValue,
)
) {
case (
IntegrationState(currentIntegral, lastTick, lastValue),
- (tick, value)
+ (tick, value),
) =>
/* Calculate the partial integral over the last know value since it's occurrence and the instance when the newest value comes in */
val duration = Seconds(tick - lastTick)
@@ -172,7 +172,7 @@ object QuantityUtil {
*/
private def startingValue[Q <: squants.Quantity[Q]](
values: Map[Long, Q],
- windowStart: Long
+ windowStart: Long,
): Q = {
values
.filter { case (tick, _) =>
@@ -206,7 +206,7 @@ object QuantityUtil {
*/
private def endingValue[Q <: Quantity[Q]](
values: Map[Long, Q],
- windowEnd: Long
+ windowEnd: Long,
): (Long, Q) = {
values
.filter { case (tick, _) =>
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/ReactivePower.scala b/src/main/scala/edu/ie3/util/scala/quantities/ReactivePower.scala
index 02458bcd89..aaddb36edf 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/ReactivePower.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/ReactivePower.scala
@@ -7,14 +7,14 @@
package edu.ie3.util.scala.quantities
import squants.energy._
-import squants.time.{Hours, Time, TimeDerivative, TimeIntegral}
+import squants.time.{Hours, Time, TimeIntegral}
import squants._
import scala.util.Try
final class ReactivePower private (
val value: Double,
- val unit: ReactivePowerUnit
+ val unit: ReactivePowerUnit,
) extends Quantity[ReactivePower]
with TimeIntegral[PowerRamp] {
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/ScalaNumberSystem.scala b/src/main/scala/edu/ie3/util/scala/quantities/ScalaNumberSystem.scala
index 8b4f5e1eb3..238668e67a 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/ScalaNumberSystem.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/ScalaNumberSystem.scala
@@ -29,7 +29,7 @@ final class ScalaNumberSystem extends DefaultNumberSystem {
override def divideAndRemainder(
x: Number,
y: Number,
- roundRemainderTowardsZero: Boolean
+ roundRemainderTowardsZero: Boolean,
): Array[Number] = {
val signX = signum(x)
val signY = signum(y)
@@ -84,8 +84,4 @@ final class ScalaNumberSystem extends DefaultNumberSystem {
override def isLessThanOne(number: Number): Boolean =
number.doubleValue < 1d
-
- override def isInteger(number: Number): Boolean =
- super.isInteger(number)
-
}
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/SpecificHeatCapacity.scala b/src/main/scala/edu/ie3/util/scala/quantities/SpecificHeatCapacity.scala
index a9e2c24011..e63ec421f5 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/SpecificHeatCapacity.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/SpecificHeatCapacity.scala
@@ -20,7 +20,7 @@ import scala.util.Try
*/
final class SpecificHeatCapacity private (
val value: Double,
- val unit: SpecificHeatCapacityUnit
+ val unit: SpecificHeatCapacityUnit,
) extends Quantity[SpecificHeatCapacity] {
def dimension: SpecificHeatCapacity.type = SpecificHeatCapacity
@@ -36,7 +36,7 @@ final class SpecificHeatCapacity private (
*/
def calcEnergyDensity(
temperatureA: Temperature,
- temperatureB: Temperature
+ temperatureB: Temperature,
): EnergyDensity =
KilowattHoursPerCubicMeter(
this.toKilowattHoursPerKelvinCubicMeters * math.abs(
@@ -59,7 +59,7 @@ final class SpecificHeatCapacity private (
def calcEnergy(
temperatureA: Temperature,
temperatureB: Temperature,
- volume: Volume
+ volume: Volume,
): Energy =
KilowattHours(
this.toKilowattHoursPerKelvinCubicMeters * math.abs(
diff --git a/src/main/scala/edu/ie3/util/scala/quantities/ThermalConductance.scala b/src/main/scala/edu/ie3/util/scala/quantities/ThermalConductance.scala
index 1d26ec1f35..ce8f9d4293 100644
--- a/src/main/scala/edu/ie3/util/scala/quantities/ThermalConductance.scala
+++ b/src/main/scala/edu/ie3/util/scala/quantities/ThermalConductance.scala
@@ -17,7 +17,7 @@ import scala.util.Try
*/
final class ThermalConductance private (
val value: Double,
- val unit: ThermalConductanceUnit
+ val unit: ThermalConductanceUnit,
) extends Quantity[ThermalConductance] {
def dimension: ThermalConductance.type = ThermalConductance
@@ -36,7 +36,7 @@ final class ThermalConductance private (
def thermalConductanceToEnergy(
temperatureInner: Temperature,
temperatureOuter: Temperature,
- time: squants.Time
+ time: squants.Time,
): Energy = WattHours(
this.toWattsPerKelvin * (temperatureInner.toKelvinScale - temperatureOuter.toKelvinScale) * time.toHours
)
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/BMModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/BMModelTest.groovy
index 6d6779194c..e77782307d 100644
--- a/src/test/groovy/edu/ie3/simona/model/participant/BMModelTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/participant/BMModelTest.groovy
@@ -6,30 +6,28 @@
package edu.ie3.simona.model.participant
-import squants.market.EUR$
-import edu.ie3.util.scala.quantities.EuroPerKilowatthour$
-
import static edu.ie3.util.quantities.PowerSystemUnits.*
import static tech.units.indriya.unit.Units.PERCENT
import edu.ie3.datamodel.models.input.NodeInput
import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
import edu.ie3.datamodel.models.input.system.type.BmTypeInput
-import squants.energy.Megawatts$
-import squants.thermal.Celsius$
+import edu.ie3.simona.model.participant.ModelState.ConstantState$
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.util.scala.OperationInterval
+import edu.ie3.util.scala.quantities.EuroPerKilowatthour$
+import edu.ie3.util.scala.quantities.Sq
import scala.Some
import spock.lang.Shared
import spock.lang.Specification
import squants.energy.Kilowatts$
-import edu.ie3.util.scala.quantities.Sq
+import squants.energy.Megawatts$
+import squants.market.EUR$
+import squants.thermal.Celsius$
import tech.units.indriya.quantity.Quantities
import java.time.ZonedDateTime
-
-
/**
* Test class that tries to cover all special cases of the current implementation of the {@link BMModel}
*
@@ -240,7 +238,7 @@ class BMModelTest extends Specification {
bmModel._lastPower = new Some(Sq.create(lastPower, Kilowatts$.MODULE$))
when: "the power from the grid is calculated"
- def powerCalc = bmModel.calculateActivePower(relevantData)
+ def powerCalc = bmModel.calculateActivePower(ConstantState$.MODULE$, relevantData)
then: "compare in kilowatts"
powerCalc - Sq.create(powerSol, Kilowatts$.MODULE$) < Sq.create(1e-12d, Kilowatts$.MODULE$)
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/ChpModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/ChpModelTest.groovy
index e601dbb64a..34372cdbed 100644
--- a/src/test/groovy/edu/ie3/simona/model/participant/ChpModelTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/participant/ChpModelTest.groovy
@@ -21,8 +21,8 @@ import edu.ie3.datamodel.models.input.thermal.ThermalBusInput
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.model.participant.ChpModel.ChpState
import edu.ie3.simona.model.thermal.CylindricalThermalStorage
-import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.KilowattHoursPerKelvinCubicMeters$
+import edu.ie3.util.TimeUtil
import edu.ie3.util.scala.quantities.Sq
import spock.lang.Shared
import spock.lang.Specification
@@ -224,10 +224,14 @@ class ChpModelTest extends Specification {
when:
def thermalStorage = buildThermalStorage(storageInput, 90)
def chpModelCaseClass = buildChpModel(thermalStorage)
+ def startDate = TimeUtil.withDefaults.toZonedDateTime("2021-01-01 00:00:00")
+ def endDate = startDate.plusSeconds(86400L)
def chpModelCaseObject = ChpModel.apply(
chpInput,
- OperationInterval.apply(0L, 86400L),
+ startDate,
+ endDate,
null,
+ 1.0,
thermalStorage)
then:
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/EvcsModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/EvcsModelTest.groovy
deleted file mode 100644
index eca95f9342..0000000000
--- a/src/test/groovy/edu/ie3/simona/model/participant/EvcsModelTest.groovy
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * © 2021. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.model.participant
-
-import static edu.ie3.util.quantities.PowerSystemUnits.*
-
-import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
-import edu.ie3.datamodel.models.input.system.type.evcslocation.EvcsLocationType
-import edu.ie3.simona.api.data.ev.model.EvModel
-import edu.ie3.simona.model.participant.control.QControl
-import edu.ie3.simona.test.common.model.MockEvModel
-import edu.ie3.util.scala.OperationInterval
-import edu.ie3.util.scala.quantities.Sq
-import spock.lang.Shared
-import spock.lang.Specification
-import squants.energy.*
-import squants.time.*
-import tech.units.indriya.quantity.Quantities
-
-import scala.collection.immutable.Set
-
-class EvcsModelTest extends Specification {
-
- static final double TESTING_TOLERANCE = 1e-10
-
- @Shared
- double scalingFactor = 1.0d
- @Shared
- int chargingPoints = 2
-
- def getStandardModel(Power sRated) {
- return new EvcsModel(
- UUID.fromString("06a14909-366e-4e94-a593-1016e1455b30"),
- "Evcs Model Test",
- OperationInterval.apply(0L, 86400L),
- scalingFactor,
- QControl.apply(new CosPhiFixed("cosPhiFixed:{(0.0,1.0)}")),
- sRated,
- 1d,
- chargingPoints,
- EvcsLocationType.HOME
- )
- }
-
- def "Test charge"() {
- given:
- EvcsModel evcsModel = getStandardModel(
- Sq.create(evcsSRated, Kilowatts$.MODULE$)
- )
- EvModel evModel = new MockEvModel(
- UUID.fromString("73c041c7-68e9-470e-8ca2-21fd7dbd1797"),
- "TestEv",
- Quantities.getQuantity(evSRated, KILOWATT),
- Quantities.getQuantity(evEStorage, KILOWATTHOUR),
- Quantities.getQuantity(evStoredEnergy.doubleValue(), KILOWATTHOUR)
- )
- def chargingTime = Sq.create(
- durationMins, Minutes$.MODULE$
- )
-
- when:
- def res = evcsModel.charge(evModel, chargingTime)
-
- then:
- res._1().value().doubleValue() =~ solChargedEnergy.doubleValue()
- Sq.create(res._2().storedEnergy.value.doubleValue(), KilowattHours$.MODULE$) =~ Sq.create(solStoredEnergy, KilowattHours$.MODULE$)
-
- where:
- evcsSRated | evSRated | evEStorage | evStoredEnergy | durationMins || solStoredEnergy | solChargedEnergy
- 100d | 10d | 20d | 0d | 60d || 10d | 10d // charge a bit
- 100d | 100d | 20d | 0d | 60d || 20d | 20d // charge to full
- 100d | 100d | 80d | 30d | 30d || 80d | 50d // charge to full with non-empty start
- 100d | 10d | 20d | 20d | 60d || 20d | 0d // already full
- }
-
- def "Test calcActivePowerAndEvSoc"() {
- given:
- def evsRated = 100d
-
- EvcsModel evcsModel = getStandardModel(Sq.create(evsRated, Kilowatts$.MODULE$))
- EvModel ev1Model = new MockEvModel(
- UUID.fromString("73c041c7-68e9-470e-8ca2-21fd7dbd1797"),
- "TestEv1",
- Quantities.getQuantity(ev1SRated, KILOWATT),
- Quantities.getQuantity(50d, KILOWATTHOUR),
- Quantities.getQuantity(ev1StoredEnergy, KILOWATTHOUR)
- )
- EvModel ev2Model = new MockEvModel(
- UUID.fromString("5e86454d-3434-4d92-856e-2f62dd1d0d90"),
- "TestEv2",
- Quantities.getQuantity(ev2SRated, KILOWATT),
- Quantities.getQuantity(50d, KILOWATTHOUR),
- Quantities.getQuantity(ev2StoredEnergy, KILOWATTHOUR)
- )
- Set mySet = new Set.Set2(ev1Model, ev2Model)
- def data = new EvcsModel.EvcsRelevantData(durationTicks, mySet)
-
- when:
- def res = evcsModel.calculateActivePowerAndEvSoc(data)
-
- then:
- Sq.create(res._1().toKilowatts(), KilowattHours$.MODULE$) =~ solPower
- res._2().size() == 2
- Sq.create(res._2().head().storedEnergy.value.doubleValue(), KilowattHours$.MODULE$) =~ solEv1Stored
- Sq.create(res._2().last().storedEnergy.value.doubleValue(), KilowattHours$.MODULE$) =~ solEv2Stored
-
- where:
- ev1SRated | ev1StoredEnergy | ev2SRated | ev2StoredEnergy | durationTicks || solPower | solEv1Stored | solEv2Stored
- 10d | 0d | 10d | 0d | 3600L || 20d | 10d | 10d // well below evcs sRated
- 10d | 0d | 10d | 0d | 900L || 20d | 2.5d | 2.5d
- 50d | 0d | 50d | 0d | 7200L || 50d | 50d | 50d
- 50d | 0d | 50d | 0d | 1800L || 100d | 25d | 25d // hitting evcs sRated exactly
- 100d | 0d | 25d | 0d | 1800L || 100d | 50d | 0d // going above evcs sRated
- 50d | 25d | 50d | 25d | 1800L || 100d | 50d | 50d // with non-zero start
- 50d | 45d | 50d | 35d | 3600L || 20d | 50d | 50d
- 200d | 25d | 50d | 50d | 3600L || 25d | 50d | 50d
- }
-}
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/FixedFeedModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/FixedFeedModelTest.groovy
index e501b09c70..3333fdae99 100644
--- a/src/test/groovy/edu/ie3/simona/model/participant/FixedFeedModelTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/participant/FixedFeedModelTest.groovy
@@ -77,6 +77,6 @@ class FixedFeedModelTest extends Specification {
)
then:
- actualModel.calculateActivePower(CalcRelevantData.FixedRelevantData$.MODULE$) =~ expectedPower
+ actualModel.calculateActivePower(ModelState.ConstantState$.MODULE$, CalcRelevantData.FixedRelevantData$.MODULE$) =~ expectedPower
}
}
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/PvModelIT.groovy b/src/test/groovy/edu/ie3/simona/model/participant/PvModelIT.groovy
index 52a7fcca1d..0fd8cfee48 100644
--- a/src/test/groovy/edu/ie3/simona/model/participant/PvModelIT.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/participant/PvModelIT.groovy
@@ -94,7 +94,7 @@ class PvModelIT extends Specification implements PvModelITHelper {
Dimensionless voltage = Sq.create(1.414213562d, Each$.MODULE$)
"collect the results and calculate the difference between the provided results and the calculated ones"
- double calc = model.calculatePower(0L, voltage, neededData).p().toMegawatts()
+ double calc = model.calculatePower(0L, voltage, ModelState.ConstantState$.MODULE$, neededData).p().toMegawatts()
double sol = resultsMap.get(dateTime).get(modelId).toMegawatts()
testRes.add(Math.abs(calc - sol))
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/WecModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/WecModelTest.groovy
index 9bef96267e..1855245f02 100644
--- a/src/test/groovy/edu/ie3/simona/model/participant/WecModelTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/participant/WecModelTest.groovy
@@ -6,9 +6,6 @@
package edu.ie3.simona.model.participant
-import squants.energy.Kilowatts$
-import squants.space.SquareMeters$
-
import static edu.ie3.util.quantities.PowerSystemUnits.*
import static edu.ie3.datamodel.models.StandardUnits.*
import static edu.ie3.simona.model.participant.WecModel.WecRelevantData
@@ -33,6 +30,8 @@ import squants.Each$
import squants.motion.MetersPerSecond$
import squants.motion.Pascals$
+import squants.energy.Kilowatts$
+import squants.space.SquareMeters$
import squants.thermal.Celsius$
@@ -121,25 +120,25 @@ class WecModelTest extends Specification {
new Some (Sq.create(101325d, Pascals$.MODULE$)))
when:
- def result = wecModel.calculateActivePower(wecData)
+ def result = wecModel.calculateActivePower(ModelState.ConstantState$.MODULE$, wecData)
then:
Math.abs((result.toWatts() - power.doubleValue())) < TOLERANCE
where:
- velocity | power
- 1.0d | 0
- 2.0d | -2948.80958
- 3.0d | -24573.41320
- 7.0d | -522922.23257
- 9.0d | -1140000
- 13.0d | -1140000
- 15.0d | -1140000
- 19.0d | -1140000
- 23.0d | -1140000
- 27.0d | -1140000
- 34.0d | -24573.39638
- 40.0d | 0
+ velocity || power
+ 1.0d || 0
+ 2.0d || -2948.80958
+ 3.0d || -24573.41320
+ 7.0d || -522922.23257
+ 9.0d || -1140000
+ 13.0d || -1140000
+ 15.0d || -1140000
+ 19.0d || -1140000
+ 23.0d || -1140000
+ 27.0d || -1140000
+ 34.0d || -24573.39638
+ 40.0d || 0
}
@Unroll
@@ -150,7 +149,7 @@ class WecModelTest extends Specification {
Sq.create(temperature, Celsius$.MODULE$), new Some( Sq.create(101325d, Pascals$.MODULE$)))
when:
- def result = wecModel.calculateActivePower(wecData)
+ def result = wecModel.calculateActivePower(ModelState.ConstantState$.MODULE$, wecData)
then:
result.toWatts() =~ power
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/load/FixedLoadModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/load/FixedLoadModelTest.groovy
index 1d45f021b5..cd0f9034e9 100644
--- a/src/test/groovy/edu/ie3/simona/model/participant/load/FixedLoadModelTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/participant/load/FixedLoadModelTest.groovy
@@ -19,6 +19,7 @@ import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.ModelState
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.util.TimeUtil
import spock.lang.Specification
@@ -104,7 +105,7 @@ class FixedLoadModelTest extends Specification {
then:
for (cnt in 0..10000) {
- abs((dut.calculateActivePower(FixedLoadModel.FixedLoadRelevantData$.MODULE$)).toWatts().doubleValue()
+ abs((dut.calculateActivePower(ModelState.ConstantState$.MODULE$, FixedLoadModel.FixedLoadRelevantData$.MODULE$)).toWatts().doubleValue()
- (expectedPower).toMegawatts().doubleValue()) < wattTolerance
}
@@ -131,7 +132,7 @@ class FixedLoadModelTest extends Specification {
reference
)
- abs(dut.calculateActivePower(relevantData).toWatts() - ((expectedPower * scale).doubleValue())) < wattTolerance
+ abs((dut.calculateActivePower(ModelState.ConstantState$.MODULE$, relevantData)).toWatts() - (expectedPower * scale).doubleValue()) < wattTolerance
}
where:
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/load/ProfileLoadModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/load/ProfileLoadModelTest.groovy
deleted file mode 100644
index b155a34eae..0000000000
--- a/src/test/groovy/edu/ie3/simona/model/participant/load/ProfileLoadModelTest.groovy
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * © 2020. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.model.participant.load
-
-import static edu.ie3.datamodel.models.profile.BdewStandardLoadProfile.*
-import static edu.ie3.simona.model.participant.load.LoadReference.ActivePower
-import static edu.ie3.simona.model.participant.load.LoadReference.EnergyConsumption
-import static edu.ie3.util.quantities.PowerSystemUnits.*
-import static org.apache.commons.math3.util.FastMath.abs
-
-import edu.ie3.datamodel.models.OperationTime
-import edu.ie3.datamodel.models.input.NodeInput
-import edu.ie3.datamodel.models.input.OperatorInput
-import edu.ie3.datamodel.models.input.system.LoadInput
-import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
-import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
-import edu.ie3.simona.model.SystemComponent
-import edu.ie3.simona.model.participant.control.QControl
-import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel
-import edu.ie3.util.TimeUtil
-import spock.lang.Specification
-import squants.energy.KilowattHours$
-import squants.energy.Kilowatts$
-import squants.energy.Watts$
-import edu.ie3.util.scala.quantities.Sq
-import squants.time.Minutes$
-import tech.units.indriya.quantity.Quantities
-
-import java.time.temporal.ChronoUnit
-import java.util.stream.Collectors
-
-
-
-class ProfileLoadModelTest extends Specification {
- def loadInput =
- new LoadInput(
- UUID.fromString("4eeaf76a-ec17-4fc3-872d-34b7d6004b03"),
- "testLoad",
- OperatorInput.NO_OPERATOR_ASSIGNED,
- OperationTime.notLimited(),
- new NodeInput(
- UUID.fromString("e5c1cde5-c161-4a4f-997f-fcf31fecbf57"),
- "TestNodeInputModel",
- OperatorInput.NO_OPERATOR_ASSIGNED,
- OperationTime.notLimited(),
- Quantities.getQuantity(1d, PU),
- false,
- NodeInput.DEFAULT_GEO_POSITION,
- GermanVoltageLevelUtils.LV,
- -1
- ),
- new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
- H0,
- false,
- Quantities.getQuantity(3000d, KILOWATTHOUR),
- Quantities.getQuantity(282.74d, VOLTAMPERE),
- 0.95
- )
-
- def simulationStartDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def simulationEndDate = TimeUtil.withDefaults.toZonedDateTime("2019-12-31 23:59:00")
- def foreSeenOperationInterval =
- SystemComponent.determineOperationInterval(
- simulationStartDate,
- simulationEndDate,
- loadInput.operationTime
- )
- def wattTolerance = 1 // Equals to 1 W power
-
- def "A profile load model should be instantiated from valid input correctly"() {
- when:
- def actual = ProfileLoadModel.apply(
- loadInput.copy().loadprofile(profile).build(),
- foreSeenOperationInterval,
- 1.0,
- reference)
-
- then:
- abs((actual.sRated().toWatts() * actual.cosPhiRated()).toDouble() - expectedsRated.doubleValue()) < wattTolerance
-
- where:
- profile | reference || expectedsRated
- H0 | new ActivePower(Sq.create(268.6d, Watts$.MODULE$)) || 268.6d
- H0 | new EnergyConsumption(Sq.create(3000d, KilowattHours$.MODULE$)) || 805.8089d
- L0 | new ActivePower(Sq.create(268.6d, Watts$.MODULE$)) || 268.6d
- L0 | new EnergyConsumption(Sq.create(3000d, KilowattHours$.MODULE$)) || 721.2d
- G0 | new ActivePower(Sq.create(268.6d, Watts$.MODULE$)) || 268.6d
- G0 | new EnergyConsumption(Sq.create(3000d, KilowattHours$.MODULE$)) || 721.2d
- }
-
- def "A profile load model should reach the targeted maximum power within a year"() {
- given:
- def startDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def dut = new ProfileLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- 1.0,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.sRated.to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- profile,
- new ActivePower(Sq.create(268.6d, Watts$.MODULE$))
- )
- def relevantData = (0..35040).stream().map({ cnt ->
- new ProfileLoadModel.ProfileRelevantData(
- startDate.plus(cnt * 15, ChronoUnit.MINUTES))
- }).collect(Collectors.toSet())
-
- when:
- def max = relevantData.stream().mapToDouble({ data ->
- dut.calculateActivePower(data).toMegawatts().doubleValue()
- }).max().getAsDouble()
-
- then:
- abs(max - expectedMax) < wattTolerance
-
- where:
- profile || expectedMax
- H0 || 268.0029932985852E-6
- L0 || 268.0029932985852E-6
- G0 || 268.0029932985852E-6
- }
-
- def "A profile load model should account for the (global) scaling factor correctly when scaling to maximum power within a year"() {
- given:
- def startDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def dut = new ProfileLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- globalScaling,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.getsRated().to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- H0,
- new ActivePower(Sq.create(268.6d, Watts$.MODULE$))
- )
- def relevantDatas = (0..35040).stream().map({ cnt ->
- new ProfileLoadModel.ProfileRelevantData(
- startDate.plus(cnt * 15, ChronoUnit.MINUTES))
- }).collect(Collectors.toSet())
-
- when:
- def max = relevantDatas.stream().mapToDouble({ relevantData ->
- dut.calculateActivePower(relevantData).toMegawatts().doubleValue()
- }).max().asDouble
-
- then:
- abs(max - expectedMax) < wattTolerance
-
- where:
- globalScaling || expectedMax
- 0.25 || 67.00074832464630E-6
- 0.5 || 134.0014966492930E-6
- 0.75 || 201.0022449739390E-6
- 1.0 || 268.0029932985852E-6
- 1.25 || 335.0037416232310E-6
- 1.5 || 402.0044899478780E-6
- 1.75 || 469.0052382725240E-6
- 2.0 || 536.0059865971700E-6
- }
-
- def "A profile load model should reach the targeted annual energy consumption"() {
- given:
- /* Test against a permissible deviation of 2 %. As per official documentation of the bdew load profiles
- * [https://www.bdew.de/media/documents/2000131_Anwendung-repraesentativen_Lastprofile-Step-by-step.pdf] 1.5 %
- * are officially permissible. But, as we currently do not take (bank) holidays into account, we cannot reach
- * this accuracy. */
- def testingTolerance = 0.02
- def startDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def dut = new ProfileLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- 1.0,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.getsRated().to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- profile,
- new EnergyConsumption(Sq.create(3000d, KilowattHours$.MODULE$))
- )
- def relevantDatas = (0..35040).stream().map({ cnt ->
- new ProfileLoadModel.ProfileRelevantData(
- startDate.plus(cnt * 15, ChronoUnit.MINUTES))
- }).collect(Collectors.toSet())
-
- when:
- def annualEnergy = relevantDatas.stream().mapToDouble({ relevantData ->
- ((dut.calculateActivePower(relevantData).$times(Sq.create(15d, Minutes$.MODULE$)).toKilowattHours()))
- }).sum()
-
- then:
- abs(annualEnergy - expectedEnergy) / expectedEnergy < testingTolerance
-
- where:
- profile || expectedEnergy
- H0 || 3000d
- L0 || 3000d
- G0 || 3000d
- }
-
- def "A profile load model should account for the (global) scaling factor correctly when scaling to annual energy consumption"() {
- given:
- /* Test against a permissible deviation of 2 %. As per official documentation of the bdew load profiles
- * [https://www.bdew.de/media/documents/2000131_Anwendung-repraesentativen_Lastprofile-Step-by-step.pdf] 1.5 %
- * are officially permissible. But, as we currently do not take (bank) holidays into account, we cannot reach
- * this accuracy. */
- def testingTolerance = 0.02
- def startDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def dut = new ProfileLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- globalScaling,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.getsRated().to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- H0,
- new EnergyConsumption(Sq.create(3000d, KilowattHours$.MODULE$))
- )
- def relevantDatas = (0..35040).stream().map({ cnt ->
- new ProfileLoadModel.ProfileRelevantData(
- startDate.plus(cnt * 15, ChronoUnit.MINUTES))
- }).collect(Collectors.toSet())
-
- when:
- def annualEnergy = relevantDatas.stream().mapToDouble({ relevantData ->
- ((dut.calculateActivePower(relevantData).$times(Sq.create(15d, Minutes$.MODULE$)).toKilowattHours()))
- }).sum()
-
- then:
- abs(annualEnergy - expectedEnergy) / expectedEnergy < testingTolerance
-
- where:
- globalScaling || expectedEnergy
- 0.25 || 750d
- 0.5 || 1500d
- 0.75 || 2250d
- 1.0 || 3000d
- 1.25 || 3750d
- 1.5 || 4500d
- 1.75 || 5250d
- 2.0 || 6000d
- }
-}
diff --git a/src/test/groovy/edu/ie3/simona/model/participant/load/RandomLoadModelTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/load/RandomLoadModelTest.groovy
deleted file mode 100644
index 9ea389751c..0000000000
--- a/src/test/groovy/edu/ie3/simona/model/participant/load/RandomLoadModelTest.groovy
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * © 2020. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.model.participant.load
-
-import static edu.ie3.datamodel.models.profile.BdewStandardLoadProfile.H0
-import static edu.ie3.simona.model.participant.load.LoadReference.ActivePower
-import static edu.ie3.simona.model.participant.load.LoadReference.EnergyConsumption
-import static edu.ie3.util.quantities.PowerSystemUnits.*
-import static org.apache.commons.math3.util.FastMath.abs
-
-import edu.ie3.datamodel.models.OperationTime
-import edu.ie3.datamodel.models.input.NodeInput
-import edu.ie3.datamodel.models.input.OperatorInput
-import edu.ie3.datamodel.models.input.system.LoadInput
-import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
-import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
-import edu.ie3.simona.model.SystemComponent
-import edu.ie3.simona.model.participant.control.QControl
-import edu.ie3.simona.model.participant.load.random.RandomLoadModel
-import edu.ie3.simona.model.participant.load.random.RandomLoadParameters
-import edu.ie3.util.TimeUtil
-import squants.energy.*
-import spock.lang.Specification
-
-import squants.time.Minutes$
-import tech.units.indriya.quantity.Quantities
-import edu.ie3.util.scala.quantities.Sq
-
-import java.time.temporal.ChronoUnit
-import java.util.stream.Collectors
-
-class RandomLoadModelTest extends Specification {
- def loadInput = new LoadInput(
- UUID.fromString("4eeaf76a-ec17-4fc3-872d-34b7d6004b03"),
- "testLoad",
- OperatorInput.NO_OPERATOR_ASSIGNED,
- OperationTime.notLimited(),
- new NodeInput(
- UUID.fromString("e5c1cde5-c161-4a4f-997f-fcf31fecbf57"),
- "TestNodeInputModel",
- OperatorInput.NO_OPERATOR_ASSIGNED,
- OperationTime.notLimited(),
- Quantities.getQuantity(1d, PU),
- false,
- NodeInput.DEFAULT_GEO_POSITION,
- GermanVoltageLevelUtils.LV,
- -1
- ),
- new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
- H0,
- false,
- Quantities.getQuantity(3000d, KILOWATTHOUR),
- Quantities.getQuantity(282.74d, VOLTAMPERE),
- 0.95
- )
-
- def simulationStartDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def simulationEndDate = TimeUtil.withDefaults.toZonedDateTime("2019-12-31 23:59:00")
- def foreSeenOperationInterval =
- SystemComponent.determineOperationInterval(
- simulationStartDate,
- simulationEndDate,
- loadInput.operationTime
- )
- def testingTolerance = 1e-6 // Equals to 1 W power
-
- def "A random load model should be instantiated from valid input correctly"() {
- when:
- def actual = RandomLoadModel.apply(
- loadInput,
- foreSeenOperationInterval,
- 1.0,
- reference
- )
-
- then:
- abs(actual.sRated().toWatts().doubleValue() - (expSRated.value().doubleValue())) < testingTolerance
-
- where:
- reference || expSRated
- new ActivePower(Sq.create(268.6d, Watts$.MODULE$)) || Sq.create(311.0105263157895d, Watts$.MODULE$)
- new EnergyConsumption(Sq.create(2000d, KilowattHours$.MODULE$)) || Sq.create(513.8717370343667d, Watts$.MODULE$)
- }
-
- def "A random load model is able to deliver the correct distribution on request"() {
- given:
- def dut = new RandomLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- 1.0,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.sRated.to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- new ActivePower(Sq.create(268.6d, Watts$.MODULE$))
- )
- /* Working day, 61th quarter hour */
- def queryDate = TimeUtil.withDefaults.toZonedDateTime('2019-07-19 15:21:00')
- def expectedParams = new RandomLoadParameters(0.405802458524704, 0.0671483352780342, 0.0417016632854939)
-
- when:
- /* First query leeds to generation of distribution */
- def firstHit = dut.getGevDistribution(queryDate)
-
- then:
- firstHit.with {
- assert k == expectedParams.k()
- assert mu == expectedParams.my()
- assert sigma == expectedParams.sigma()
- }
-
- when:
- /* Second query is only look up in storage */
- def secondHit = dut.getGevDistribution(queryDate)
-
- then:
- secondHit == firstHit
- }
-
- def "A random load model should approx. reach the targeted annual energy consumption"() {
- given:
- def startDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def dut = new RandomLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- 1.0,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.sRated.to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- new EnergyConsumption(Sq.create(3000d, KilowattHours$.MODULE$))
- )
- def relevantDatas = (0..35040).stream().map({ cnt ->
- new RandomLoadModel.RandomRelevantData(
- startDate.plus(cnt * 15, ChronoUnit.MINUTES))
- }).collect(Collectors.toSet())
-
- when:
- def avgEnergy = (0..10).parallelStream().mapToDouble( { runCnt ->
- relevantDatas.parallelStream().mapToDouble( { relevantData ->
- (dut.calculateActivePower(relevantData).$times(Sq.create(15d, Minutes$.MODULE$))).toKilowattHours()
- }).sum()
- }).average().orElse(0d)
-
- then:
- abs(avgEnergy - 3000) / 3000 < 0.01
- }
-
- def "A random load model should approx. reach the targeted maximum power"() {
- given:
- def startDate = TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
- def dut = new RandomLoadModel(
- loadInput.uuid,
- loadInput.id,
- foreSeenOperationInterval,
- 1.0,
- QControl.apply(loadInput.qCharacteristics),
- Sq.create(loadInput.sRated.to(KILOWATT).value.doubleValue(), Kilowatts$.MODULE$),
- loadInput.cosPhiRated,
- new ActivePower(Sq.create(268.6d, Watts$.MODULE$))
- )
- def relevantDatas = (0..35040).stream().map({ cnt ->
- new RandomLoadModel.RandomRelevantData(
- startDate.plus(cnt * 15, ChronoUnit.MINUTES))
- }).collect(Collectors.toSet())
-
- when:
- def powers = (0..10).parallelStream().flatMap({ runCnt ->
- relevantDatas.stream().parallel().map({ data ->
- dut.calculateActivePower(data).toWatts().doubleValue()
- })
- }).sorted().toArray() as Double[]
- def quantilePower = get95Quantile(powers)
-
- then:
- abs(quantilePower - 268.6) / 268.6 < 0.01
- }
-
- def get95Quantile(Double[] sortedArray) {
- def quantIdx = sortedArray.length * 0.95 as int
- sortedArray[quantIdx]
- }
-}
\ No newline at end of file
diff --git a/src/test/groovy/edu/ie3/simona/model/thermal/CylindricalThermalStorageTest.groovy b/src/test/groovy/edu/ie3/simona/model/thermal/CylindricalThermalStorageTest.groovy
index 1513aa2b34..af5ffef8e5 100644
--- a/src/test/groovy/edu/ie3/simona/model/thermal/CylindricalThermalStorageTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/thermal/CylindricalThermalStorageTest.groovy
@@ -6,9 +6,11 @@
package edu.ie3.simona.model.thermal
+import static edu.ie3.util.quantities.PowerSystemUnits.KILOWATTHOUR
+import static tech.units.indriya.quantity.Quantities.getQuantity
+
import edu.ie3.datamodel.models.StandardUnits
import edu.ie3.datamodel.models.input.thermal.CylindricalStorageInput
-import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.quantities.KilowattHoursPerKelvinCubicMeters$
import edu.ie3.util.scala.quantities.Sq
import spock.lang.Shared
@@ -17,12 +19,11 @@ import squants.energy.KilowattHours$
import squants.energy.Kilowatts$
import squants.space.CubicMeters$
import squants.thermal.Celsius$
-import tech.units.indriya.unit.Units
-
-import static tech.units.indriya.quantity.Quantities.getQuantity
class CylindricalThermalStorageTest extends Specification {
+ static final double TESTING_TOLERANCE = 1e-10
+
@Shared
CylindricalStorageInput storageInput
@@ -31,32 +32,28 @@ class CylindricalThermalStorageTest extends Specification {
UUID.randomUUID(),
"ThermalStorage",
null,
- getQuantity(100d, StandardUnits.VOLUME),
- getQuantity(20d, StandardUnits.VOLUME),
- getQuantity(30d, StandardUnits.TEMPERATURE),
- getQuantity(40d, StandardUnits.TEMPERATURE),
- getQuantity(1.15d, StandardUnits.SPECIFIC_HEAT_CAPACITY))
+ getQuantity(100, StandardUnits.VOLUME),
+ getQuantity(20, StandardUnits.VOLUME),
+ getQuantity(30, StandardUnits.TEMPERATURE),
+ getQuantity(40, StandardUnits.TEMPERATURE),
+ getQuantity(1.15, StandardUnits.SPECIFIC_HEAT_CAPACITY))
}
static def buildThermalStorage(CylindricalStorageInput storageInput, Double volume) {
- def storedEnergy =
- CylindricalThermalStorage.volumeToEnergy(
- Sq.create(volume, CubicMeters$.MODULE$),
+ def storedEnergy = CylindricalThermalStorage.volumeToEnergy(Sq.create(volume, CubicMeters$.MODULE$),
Sq.create(storageInput.c.value.doubleValue(), KilowattHoursPerKelvinCubicMeters$.MODULE$),
Sq.create(storageInput.inletTemp.value.doubleValue(), Celsius$.MODULE$),
- Sq.create(storageInput.returnTemp.value.doubleValue(), Celsius$.MODULE$)
- )
+ Sq.create(storageInput.returnTemp.value.doubleValue(), Celsius$.MODULE$))
def thermalStorage = CylindricalThermalStorage.apply(storageInput, storedEnergy)
return thermalStorage
}
def vol2Energy(Double volume) {
- return CylindricalThermalStorage.volumeToEnergy(
+ return CylindricalThermalStorage.volumeToEnergy( // FIXME below: get values in units with to..()
Sq.create(volume, CubicMeters$.MODULE$),
- Sq.create(storageInput.c.to(PowerSystemUnits.KILOWATTHOUR_PER_KELVIN_TIMES_CUBICMETRE).value.doubleValue(), KilowattHoursPerKelvinCubicMeters$.MODULE$),
- Sq.create(storageInput.inletTemp.to(Units.CELSIUS).value.doubleValue(), Celsius$.MODULE$),
- Sq.create(storageInput.returnTemp.to(Units.CELSIUS).value.doubleValue(), Celsius$.MODULE$)
- )
+ Sq.create(storageInput.c.value.doubleValue(), KilowattHoursPerKelvinCubicMeters$.MODULE$),
+ Sq.create(storageInput.inletTemp.value.doubleValue(), Celsius$.MODULE$),
+ Sq.create(storageInput.returnTemp.value.doubleValue(), Celsius$.MODULE$))
}
def "Check storage level operations:"() {
@@ -64,26 +61,43 @@ class CylindricalThermalStorageTest extends Specification {
def storage = buildThermalStorage(storageInput, 70)
when:
- def initialLevel = storage._storedEnergy()
- storage._storedEnergy_$eq(vol2Energy(50))
- def newLevel1 = storage._storedEnergy()
- def surplus = storage.tryToStoreAndReturnRemainder(vol2Energy(55)).get()
- def newLevel2 = storage._storedEnergy()
- def isCovering = storage.isDemandCoveredByStorage(Sq.create(5d, KilowattHours$.MODULE$))
- def lack = storage.tryToTakeAndReturnLack(vol2Energy(95)).get()
- def newLevel3 = storage._storedEnergy()
- def notCovering = storage.isDemandCoveredByStorage(Sq.create(1d, KilowattHours$.MODULE$))
+ def initialLevel =
+ getQuantity(storage._storedEnergy().toKilowattHours(), KILOWATTHOUR)
+ storage._storedEnergy_$eq(vol2Energy(50d),)
+ def newLevel1 = getQuantity(storage._storedEnergy().toKilowattHours(), KILOWATTHOUR)
+ def surplus = storage.tryToStoreAndReturnRemainder(
+ vol2Energy(55d))
+ def newLevel2 = getQuantity(storage._storedEnergy().toKilowattHours(), KILOWATTHOUR)
+ def isCovering = storage.isDemandCoveredByStorage(Sq.create(5, KilowattHours$.MODULE$))
+ def lack =
+ storage.tryToTakeAndReturnLack(
+ vol2Energy(95d)
+ )
+ def newLevel3 = getQuantity(storage._storedEnergy().toKilowattHours(), KILOWATTHOUR)
+ def notCovering = storage.isDemandCoveredByStorage(Sq.create(1, KilowattHours$.MODULE$))
+
then:
- initialLevel =~ vol2Energy(70d)
- newLevel1 =~ vol2Energy(50d)
+ initialLevel.value.doubleValue() =~ vol2Energy(70d).toKilowattHours()
+ newLevel1.value.doubleValue() =~ vol2Energy(50d).toKilowattHours()
surplus =~ vol2Energy(5d)
- newLevel2 =~ vol2Energy(100d)
+ newLevel2.value.doubleValue() =~ vol2Energy(100d).toKilowattHours()
lack =~ vol2Energy(15d)
- newLevel3 =~ vol2Energy(20d)
+ newLevel3.value.doubleValue() =~ vol2Energy(20d).toKilowattHours()
isCovering
!notCovering
}
+ def "Check converting methods:"() {
+ given:
+ def storage = buildThermalStorage(storageInput, 70)
+
+ when:
+ def usableThermalEnergy = storage.usableThermalEnergy()
+
+ then:
+ Math.abs(usableThermalEnergy.toKilowattHours() - 5 * 115) < TESTING_TOLERANCE
+ }
+
def "Check apply, validation and build method:"() {
when:
def storage = buildThermalStorage(storageInput, 70)
@@ -98,24 +112,24 @@ class CylindricalThermalStorageTest extends Specification {
def "Check mutable state update:"() {
when:
- def storage = buildThermalStorage(storageInput, 70d)
+ def storage = buildThermalStorage(storageInput, 70)
def lastState = new ThermalStorage.ThermalStorageState(tick, Sq.create(storedEnergy, KilowattHours$.MODULE$), Sq.create(qDot, Kilowatts$.MODULE$))
def result = storage.updateState(newTick, Sq.create(newQDot, Kilowatts$.MODULE$), lastState)
then:
- result._1().storedEnergy() =~ Sq.create(expectedStoredEnergy, KilowattHours$.MODULE$)
+ Math.abs(result._1().storedEnergy().toKilowattHours() - expectedStoredEnergy.doubleValue()) < TESTING_TOLERANCE
result._2.defined
result._2.get() == expectedThreshold
where:
- tick | storedEnergy | qDot | newTick | newQDot || expectedStoredEnergy | expectedThreshold
- 0L | 250.0d | 10.0d | 3600L | 42.0d || 260.0d | new ThermalStorage.ThermalStorageThreshold.StorageFull(79885L)
- 0L | 250.0d | 10.0d | 3600L | -42.0d || 260.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(6171L)
- 0L | 250.0d | -10.0d | 3600L | 42.0d || 240.0d | new ThermalStorage.ThermalStorageThreshold.StorageFull(81600L)
- 0L | 250.0d | -10.0d | 3600L | -42.0d || 240.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(4457L)
- 0L | 250.0d | -10.0d | 3600L | -42.0d || 240.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(4457L)
- 0L | 1000.0d | 149.0d | 3600L | 5000.0d || 1149.0d | new ThermalStorage.ThermalStorageThreshold.StorageFull(3600L)
- 0L | 240.0d | -9.0d | 3600L | -5000.0d || 231.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(3600L)
+ tick | storedEnergy | qDot | newTick | newQDot || expectedStoredEnergy | expectedThreshold
+ 0L | 250.0d | 10.0d | 3600L | 42.0d || 260.0d | new ThermalStorage.ThermalStorageThreshold.StorageFull(79886L)
+ 0L | 250.0d | 10.0d | 3600L | -42.0d || 260.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(6171L)
+ 0L | 250.0d | -10.0d | 3600L | 42.0d || 240.0d | new ThermalStorage.ThermalStorageThreshold.StorageFull(81600L)
+ 0L | 250.0d | -10.0d | 3600L | -42.0d || 240.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(4457L)
+ 0L | 250.0d | -10.0d | 3600L | -42.0d || 240.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(4457L)
+ 0L | 1000.0d | 149.0d | 3600L | 5000.0d || 1149.0d | new ThermalStorage.ThermalStorageThreshold.StorageFull(3601L)
+ 0L | 240.0d | -9.0d | 3600L | -5000.0d || 231.0d | new ThermalStorage.ThermalStorageThreshold.StorageEmpty(3601L)
}
def "Check mutable state update, if no threshold is reached:"() {
@@ -125,7 +139,7 @@ class CylindricalThermalStorageTest extends Specification {
def result = storage.updateState(newTick, Sq.create(newQDot, Kilowatts$.MODULE$), lastState)
then:
- result._1().storedEnergy() =~ Sq.create(expectedStoredEnergy, KilowattHours$.MODULE$)
+ Math.abs(result._1().storedEnergy().toKilowattHours() - expectedStoredEnergy.doubleValue()) < TESTING_TOLERANCE
result._2.empty
where:
diff --git a/src/test/groovy/edu/ie3/simona/model/thermal/ThermalHouseTest.groovy b/src/test/groovy/edu/ie3/simona/model/thermal/ThermalHouseTest.groovy
index 2b66f3b9f6..f23d532b03 100644
--- a/src/test/groovy/edu/ie3/simona/model/thermal/ThermalHouseTest.groovy
+++ b/src/test/groovy/edu/ie3/simona/model/thermal/ThermalHouseTest.groovy
@@ -21,7 +21,6 @@ import squants.thermal.*
import squants.time.*
-
class ThermalHouseTest extends Specification {
@Shared
@@ -49,21 +48,21 @@ class ThermalHouseTest extends Specification {
when:
Temperature innerTemp = Sq.create(innerTemperature, Celsius$.MODULE$)
def isHigher = thermalHouse.isInnerTemperatureTooHigh(innerTemp)
- def isLower = thermalHouse.isInnerTemperatureTooLow(innerTemp)
+ def isLower = thermalHouse.isInnerTemperatureTooLow(innerTemp, thermalHouse.lowerBoundaryTemperature())
then:
isHigher == isTooHigh
isLower == isTooLow
where:
- innerTemperature || isTooHigh | isTooLow
- 17d || false | true
- 17.98d || false | true
- 18d || false | true
- 20d || false | false
- 22d || true | false
- 22.02d || true | false
- 23d || true | false
+ innerTemperature || isTooHigh | isTooLow
+ 17d || false | true
+ 17.98d || false | true
+ 18d || false | true
+ 20d || false | false
+ 22d || true | false
+ 22.02d || true | false
+ 23d || true | false
}
def "Calculation of thermal energy change and new inner temperature is performed correctly"() {
@@ -79,11 +78,11 @@ class ThermalHouseTest extends Specification {
def newInnerTemperature = thermalHouse.calcNewInnerTemperature(innerTemperature, innerTemperatureChange)
then:
- Sq.create(100d, KilowattHours$.MODULE$) - thermalEnergyGain < Sq.create(TOLERANCE, KilowattHours$.MODULE$)
- Sq.create(10d, KilowattHours$.MODULE$) - thermalEnergyLoss < Sq.create(TOLERANCE, KilowattHours$.MODULE$)
- Sq.create(90d, KilowattHours$.MODULE$) - thermalEnergyChange < Sq.create(TOLERANCE, KilowattHours$.MODULE$)
- Sq.create(9d, Kelvin$.MODULE$) - innerTemperatureChange < Sq.create(TOLERANCE, Kelvin$.MODULE$)
- Sq.create(29d, Celsius$.MODULE$) - newInnerTemperature < Sq.create(TOLERANCE, Celsius$.MODULE$)
+ Math.abs(100d - thermalEnergyGain.toKilowattHours()) < TOLERANCE
+ Math.abs(10d - thermalEnergyLoss.toKilowattHours()) < TOLERANCE
+ Math.abs(90d - thermalEnergyChange.toKilowattHours()) < TOLERANCE
+ Math.abs(9d - innerTemperatureChange.toKelvinScale()) < TOLERANCE
+ Math.abs(29d - newInnerTemperature.toCelsiusScale()) < TOLERANCE
}
def "Comprising function to calculate new inner temperature works as expected"() {
@@ -122,7 +121,7 @@ class ThermalHouseTest extends Specification {
thermalHouse.operatorInput() == thermalHouseInput.operator
thermalHouse.operationTime() == thermalHouseInput.operationTime
thermalHouse.bus() == thermalHouseInput.thermalBus
- thermalHouse.ethLosses().value().doubleValue() == thermalHouseInput.ethLosses.to(KILOWATT_PER_KELVIN).value.doubleValue() * 1000
+ thermalHouse.ethLosses().toWattsPerKelvin() == thermalHouseInput.ethLosses.to(KILOWATT_PER_KELVIN).value.doubleValue() * 1000
(thermalHouse.ethCapa().$times(Sq.create(1d, Kelvin$.MODULE$))).toKilowattHours() == thermalHouseInput.ethCapa.to(KILOWATTHOUR_PER_KELVIN).value.doubleValue()
thermalHouse.lowerBoundaryTemperature() == Sq.create(18, Celsius$.MODULE$)
thermalHouse.upperBoundaryTemperature() == Sq.create(22, Celsius$.MODULE$)
diff --git a/src/test/groovy/edu/ie3/simona/test/common/model/MockParticipant.groovy b/src/test/groovy/edu/ie3/simona/test/common/model/MockParticipant.groovy
index 804626e253..57a8d51ab6 100644
--- a/src/test/groovy/edu/ie3/simona/test/common/model/MockParticipant.groovy
+++ b/src/test/groovy/edu/ie3/simona/test/common/model/MockParticipant.groovy
@@ -8,16 +8,17 @@ package edu.ie3.simona.test.common.model
import edu.ie3.simona.agent.participant.data.Data
import edu.ie3.simona.model.participant.CalcRelevantData
-
+import edu.ie3.simona.model.participant.ModelState
import edu.ie3.simona.model.participant.SystemParticipant
import edu.ie3.simona.model.participant.control.QControl
-
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.Sq
+import scala.Tuple2
import squants.Dimensionless
import squants.energy.*
-class MockParticipant extends SystemParticipant {
+class MockParticipant extends SystemParticipant {
MockParticipant(
UUID uuid,
@@ -40,12 +41,22 @@ class MockParticipant extends SystemParticipant sRated;
+ private final ComparableQuantity sRatedAC;
+ private final ComparableQuantity sRatedDC;
private final ComparableQuantity eStorage;
private final ComparableQuantity storedEnergy;
+ private final Long departureTick;
public MockEvModel(
UUID uuid,
String id,
- ComparableQuantity sRated,
+ ComparableQuantity sRatedAC,
+ ComparableQuantity sRatedDC,
ComparableQuantity eStorage,
- ComparableQuantity storedEnergy) {
+ ComparableQuantity storedEnergy,
+ Long departureTick) {
this.uuid = uuid;
this.id = id;
- this.sRated = sRated;
+ this.sRatedAC = sRatedAC;
+ this.sRatedDC = sRatedDC;
this.eStorage = eStorage;
this.storedEnergy = storedEnergy;
+ this.departureTick = departureTick;
}
public MockEvModel(
- UUID uuid, String id, ComparableQuantity sRated, ComparableQuantity eStorage) {
+ UUID uuid,
+ String id,
+ ComparableQuantity sRatedAC,
+ ComparableQuantity sRatedDC,
+ ComparableQuantity eStorage,
+ Long departureTick) {
this.uuid = uuid;
this.id = id;
- this.sRated = sRated;
+ this.sRatedAC = sRatedAC;
+ this.sRatedDC = sRatedDC;
this.eStorage = eStorage;
this.storedEnergy = Quantities.getQuantity(0d, PowerSystemUnits.KILOWATTHOUR);
+ this.departureTick = departureTick;
}
@Override
@@ -56,12 +69,12 @@ public String getId() {
@Override
public ComparableQuantity getSRatedAC() {
- return sRated;
+ return sRatedAC;
}
@Override
public ComparableQuantity getSRatedDC() {
- return sRated;
+ return sRatedDC;
}
@Override
@@ -76,12 +89,16 @@ public ComparableQuantity getStoredEnergy() {
@Override
public Long getDepartureTick() {
- return 0L;
+ return departureTick;
}
@Override
public MockEvModel copyWith(ComparableQuantity newStoredEnergy) {
- return new MockEvModel(uuid, id, sRated, eStorage, newStoredEnergy);
+ return new MockEvModel(uuid, id, sRatedAC, sRatedDC, eStorage, newStoredEnergy, departureTick);
+ }
+
+ public MockEvModel copyWithDeparture(Long departureTick) {
+ return new MockEvModel(uuid, id, sRatedAC, sRatedDC, eStorage, storedEnergy, departureTick);
}
@Override
@@ -91,13 +108,15 @@ public boolean equals(Object o) {
MockEvModel that = (MockEvModel) o;
return uuid.equals(that.uuid)
&& id.equals(that.id)
- && sRated.equals(that.sRated)
+ && sRatedAC.equals(that.sRatedAC)
+ && sRatedDC.equals(that.sRatedDC)
&& eStorage.equals(that.eStorage)
- && storedEnergy.equals(that.storedEnergy);
+ && storedEnergy.equals(that.storedEnergy)
+ && departureTick.equals(that.departureTick);
}
@Override
public int hashCode() {
- return Objects.hash(uuid, id, sRated, eStorage, storedEnergy);
+ return Objects.hash(uuid, id, sRatedAC, sRatedDC, eStorage, storedEnergy, departureTick);
}
}
diff --git a/src/test/java/testutils/TestObjectFactory.java b/src/test/java/testutils/TestObjectFactory.java
index c565347bcc..12ca8f6a23 100644
--- a/src/test/java/testutils/TestObjectFactory.java
+++ b/src/test/java/testutils/TestObjectFactory.java
@@ -37,7 +37,7 @@ public static NodeInput buildNodeInput(
boolean isSlack, CommonVoltageLevel voltageLvl, int subnet) {
return new NodeInput(
UUID.randomUUID(),
- "TEST_NODE_" + TEST_OBJECT_COUNTER,
+ "TEST_NODE_" + TEST_OBJECT_COUNTER++,
OperatorInput.NO_OPERATOR_ASSIGNED,
OperationTime.notLimited(),
Quantities.getQuantity(1d, PU),
@@ -103,7 +103,7 @@ public static LineTypeInput buildLineTypeInput(VoltageLevel voltageLvl) {
public static SwitchInput buildSwitchInput(NodeInput nodeA, NodeInput nodeB) {
return new SwitchInput(
UUID.randomUUID(),
- "TEST_SWITCH" + TEST_OBJECT_COUNTER,
+ "TEST_SWITCH" + TEST_OBJECT_COUNTER++,
OperatorInput.NO_OPERATOR_ASSIGNED,
OperationTime.notLimited(),
nodeA,
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
index b2c5260199..79b160e703 100644
--- a/src/test/resources/logback-test.xml
+++ b/src/test/resources/logback-test.xml
@@ -1,27 +1,25 @@
-
+
%highlight%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
-
- OFF
-
+
test/logs/simona/simona_tests.log
- test/logs/simona/archive/simona_tests-%d{yyyyMMdd'T'HHmmss}-${bySecond}.log
-
+ test/logs/simona/archive/simona_tests-%d{yyyyMMdd'T'HHmmss}.log
+
10
3GB
@@ -32,8 +30,30 @@
DEBUG
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/scala/edu/ie3/simona/agent/ValueStoreSpec.scala b/src/test/scala/edu/ie3/simona/agent/ValueStoreSpec.scala
index 546a123bd6..58f545e21d 100644
--- a/src/test/scala/edu/ie3/simona/agent/ValueStoreSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/ValueStoreSpec.scala
@@ -7,9 +7,10 @@
package edu.ie3.simona.agent
import edu.ie3.simona.test.common.UnitSpec
-import org.scalatest.PrivateMethodTester
-class ValueStoreSpec extends UnitSpec with PrivateMethodTester {
+import scala.collection.SortedMap
+
+class ValueStoreSpec extends UnitSpec {
"An empty value store" should {
val emptyValueStore: ValueStore[String] = ValueStore[String](Long.MaxValue)
@@ -42,7 +43,7 @@ class ValueStoreSpec extends UnitSpec with PrivateMethodTester {
"A filled value store" should {
val filledValueStore: ValueStore[String] = ValueStore[String](
5,
- Map(1L -> "One", 2L -> "Two", 3L -> "Three", 4L -> "Four")
+ SortedMap(1L -> "One", 2L -> "Two", 3L -> "Three", 4L -> "Four"),
)
"be properly instantiated" in {
@@ -52,7 +53,7 @@ class ValueStoreSpec extends UnitSpec with PrivateMethodTester {
1L -> "One",
2L -> "Two",
3L -> "Three",
- 4L -> "Four"
+ 4L -> "Four",
)
filledValueStore.maxTickSpan shouldBe 5
}
@@ -72,7 +73,7 @@ class ValueStoreSpec extends UnitSpec with PrivateMethodTester {
"return an empty map on request of tick window" in {
filledValueStore.get(2L, 3L) shouldBe Map(
2L -> "Two",
- 3L -> "Three"
+ 3L -> "Three",
)
}
@@ -86,7 +87,7 @@ class ValueStoreSpec extends UnitSpec with PrivateMethodTester {
3L -> "Three",
4L -> "Four",
5L -> "Five",
- 6L -> "Six"
+ 6L -> "Six",
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala
index 5beb87d7b0..d1715cb356 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmCenGridSpec.scala
@@ -6,35 +6,32 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorSystem
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.testkit.TestProbe
-import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.models.input.container.ThermalGrid
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.agent.grid.GridAgent.FinishGridSimulationTrigger
import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
-import edu.ie3.simona.agent.state.GridAgentState.SimulateGrid
+import edu.ie3.simona.agent.grid.GridAgentMessage._
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
import edu.ie3.simona.event.ResultEvent.PowerFlowResultEvent
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
import edu.ie3.simona.model.grid.RefSystem
-import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.test.common.model.grid.DbfsTestGrid
-import edu.ie3.simona.test.common.{
- ConfigTestData,
- TestKitWithShutdown,
- TestSpawnerClassic
-}
+import edu.ie3.simona.test.common.{ConfigTestData, TestSpawnerTyped}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.scala.quantities.Megavars
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
import squants.electro.Kilovolts
import squants.energy.Megawatts
@@ -49,29 +46,21 @@ import scala.language.postfixOps
* interaction or cover this behaviour by another (integration) test!
*/
class DBFSAlgorithmCenGridSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "DBFSAlgorithmCenGridSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- )
+ extends ScalaTestWithActorTestKit
with DBFSMockGridAgents
with ConfigTestData
with DbfsTestGrid
- with TestSpawnerClassic {
+ with TestSpawnerTyped {
- private val scheduler = TestProbe("scheduler")
- private val runtimeEvents = TestProbe("runtimeEvents")
+ private val scheduler: TestProbe[SchedulerMessage] = TestProbe("scheduler")
+ private val runtimeEvents: TestProbe[RuntimeEvent] =
+ TestProbe("runtimeEvents")
private val primaryService = TestProbe("primaryService")
private val weatherService = TestProbe("weatherService")
private val superiorGridAgent = SuperiorGA(
TestProbe("superiorGridAgent_1000"),
- Seq(supNodeA.getUuid, supNodeB.getUuid)
+ Seq(supNodeA.getUuid, supNodeB.getUuid),
)
private val inferiorGrid11 =
@@ -82,27 +71,27 @@ class DBFSAlgorithmCenGridSpec
private val inferiorGrid13 = InferiorGA(
TestProbe("inferiorGridAgent_13"),
- Seq(node3.getUuid, node4.getUuid)
+ Seq(node3.getUuid, node4.getUuid),
)
private val environmentRefs = EnvironmentRefs(
scheduler = scheduler.ref,
runtimeEventListener = runtimeEvents.ref,
- primaryServiceProxy = primaryService.ref,
- weather = weatherService.ref,
- evDataService = None
+ primaryServiceProxy = primaryService.ref.toClassic,
+ weather = weatherService.ref.toClassic,
+ evDataService = None,
)
- val resultListener: TestProbe = TestProbe("resultListener")
+ val resultListener: TestProbe[ResultEvent] = TestProbe("resultListener")
"A GridAgent actor in center position with async test" should {
val centerGridAgent =
- system.actorOf(
- GridAgent.props(
+ testKit.spawn(
+ GridAgent(
environmentRefs,
simonaConfig,
- listener = Iterable(resultListener.ref)
+ listener = Iterable(resultListener.ref),
)
)
@@ -126,44 +115,33 @@ class DBFSAlgorithmCenGridSpec
hvGridContainer,
Seq.empty[ThermalGrid],
subGridGateToActorRef,
- RefSystem("2000 MVA", "110 kV")
+ RefSystem("2000 MVA", "110 kV"),
)
- val key =
- ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
- scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled
+ val key = ScheduleLock.singleKey(TSpawner, scheduler.ref, INIT_SIM_TICK)
+ // lock activation scheduled
+ scheduler.expectMessageType[ScheduleActivation]
- centerGridAgent ! GridAgent.Create(
+ centerGridAgent ! CreateGridAgent(
gridAgentInitData,
- key
- )
- scheduler.expectMsg(
- ScheduleActivation(centerGridAgent.toTyped, INIT_SIM_TICK, Some(key))
+ key,
)
- scheduler.send(centerGridAgent, Activation(INIT_SIM_TICK))
- scheduler.expectMsg(
- Completion(
- centerGridAgent.toTyped,
- Some(3600)
- )
- )
+ val scheduleActivationMsg =
+ scheduler.expectMessageType[ScheduleActivation]
+ scheduleActivationMsg.tick shouldBe INIT_SIM_TICK
+ scheduleActivationMsg.unlockKey shouldBe Some(key)
+ val gridAgentActivation = scheduleActivationMsg.actor
+ centerGridAgent ! WrappedActivation(Activation(INIT_SIM_TICK))
+ scheduler.expectMessage(Completion(gridAgentActivation, Some(3600)))
}
- s"go to $SimulateGrid when it receives an activity start trigger" in {
+ s"go to SimulateGrid when it receives an activity start trigger" in {
- scheduler.send(
- centerGridAgent,
- Activation(3600)
- )
+ centerGridAgent ! WrappedActivation(Activation(3600))
- scheduler.expectMsg(
- Completion(
- centerGridAgent.toTyped,
- Some(3600)
- )
- )
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(3600)
}
s"start the simulation when activation is sent" in {
@@ -171,7 +149,7 @@ class DBFSAlgorithmCenGridSpec
val firstSweepNo = 0
// send the start grid simulation trigger
- scheduler.send(centerGridAgent, Activation(3600))
+ centerGridAgent ! WrappedActivation(Activation(3600))
/* We expect one grid power request message per inferior grid */
@@ -188,7 +166,6 @@ class DBFSAlgorithmCenGridSpec
// normally the inferior grid agents ask for the slack voltage as well to do their power flow calculations
// we simulate this behaviour now by doing the same for our three inferior grid agents
-
inferiorGrid11.requestSlackVoltage(centerGridAgent, firstSweepNo)
inferiorGrid12.requestSlackVoltage(centerGridAgent, firstSweepNo)
@@ -204,9 +181,9 @@ class DBFSAlgorithmCenGridSpec
ExchangeVoltage(
node1.getUuid,
Kilovolts(110d),
- Kilovolts(0d)
+ Kilovolts(0d),
)
- )
+ ),
)
inferiorGrid12.expectSlackVoltageProvision(
@@ -215,9 +192,9 @@ class DBFSAlgorithmCenGridSpec
ExchangeVoltage(
node2.getUuid,
Kilovolts(110d),
- Kilovolts(0d)
+ Kilovolts(0d),
)
- )
+ ),
)
inferiorGrid13.expectSlackVoltageProvision(
@@ -226,75 +203,69 @@ class DBFSAlgorithmCenGridSpec
ExchangeVoltage(
node3.getUuid,
Kilovolts(110d),
- Kilovolts(0d)
+ Kilovolts(0d),
),
ExchangeVoltage(
node4.getUuid,
Kilovolts(110d),
- Kilovolts(0d)
- )
- )
+ Kilovolts(0d),
+ ),
+ ),
)
// we now answer the request of our centerGridAgent
// with three fake grid power messages and one fake slack voltage message
- inferiorGrid11.gaProbe.send(
- firstPowerRequestSender11,
+ firstPowerRequestSender11 ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGrid11.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
)
- inferiorGrid12.gaProbe.send(
- firstPowerRequestSender12,
+ firstPowerRequestSender12 ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGrid12.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
)
- inferiorGrid13.gaProbe.send(
- firstPowerRequestSender13,
+ firstPowerRequestSender13 ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGrid13.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
)
- superiorGridAgent.gaProbe.send(
- firstSlackVoltageRequestSender,
- ProvideSlackVoltageMessage(
- firstSweepNo,
- Seq(
- ExchangeVoltage(
- supNodeA.getUuid,
- Kilovolts(380d),
- Kilovolts(0d)
- ),
- ExchangeVoltage(
- supNodeB.getUuid,
- Kilovolts(380d),
- Kilovolts(0d)
- )
- )
- )
+ firstSlackVoltageRequestSender ! ProvideSlackVoltageMessage(
+ firstSweepNo,
+ Seq(
+ ExchangeVoltage(
+ supNodeA.getUuid,
+ Kilovolts(380d),
+ Kilovolts(0d),
+ ),
+ ExchangeVoltage(
+ supNodeB.getUuid,
+ Kilovolts(380d),
+ Kilovolts(0d),
+ ),
+ ),
)
// power flow calculation should run now. After it's done,
@@ -307,13 +278,13 @@ class DBFSAlgorithmCenGridSpec
ExchangePower(
supNodeA.getUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
ExchangePower(
supNodeB.getUuid,
Megawatts(0.160905770717798),
- Megavars(-1.4535602349123878)
- )
+ Megavars(-1.4535602349123878),
+ ),
)
)
@@ -327,23 +298,20 @@ class DBFSAlgorithmCenGridSpec
superiorGridAgent.expectSlackVoltageRequest(secondSweepNo)
// the superior grid would answer with updated slack voltage values
- superiorGridAgent.gaProbe.send(
- secondSlackAskSender,
- ProvideSlackVoltageMessage(
- secondSweepNo,
- Seq(
- ExchangeVoltage(
- supNodeB.getUuid,
- Kilovolts(374.22694614463d), // 380 kV @ 10°
- Kilovolts(65.9863075134335d) // 380 kV @ 10°
- ),
- ExchangeVoltage( // this one should currently be ignored anyways
- supNodeA.getUuid,
- Kilovolts(380d),
- Kilovolts(0d)
- )
- )
- )
+ secondSlackAskSender ! ProvideSlackVoltageMessage(
+ secondSweepNo,
+ Seq(
+ ExchangeVoltage(
+ supNodeB.getUuid,
+ Kilovolts(374.22694614463d), // 380 kV @ 10°
+ Kilovolts(65.9863075134335d), // 380 kV @ 10°
+ ),
+ ExchangeVoltage( // this one should currently be ignored anyways
+ supNodeA.getUuid,
+ Kilovolts(380d),
+ Kilovolts(0d),
+ ),
+ ),
)
// After the intermediate power flow calculation, we expect one grid power
@@ -377,9 +345,9 @@ class DBFSAlgorithmCenGridSpec
ExchangeVoltage(
node1.getUuid,
Kilovolts(108.487669651919932d),
- Kilovolts(19.101878551141232d)
+ Kilovolts(19.101878551141232d),
)
- )
+ ),
)
inferiorGrid12.expectSlackVoltageProvision(
@@ -388,9 +356,9 @@ class DBFSAlgorithmCenGridSpec
ExchangeVoltage(
node2.getUuid,
Kilovolts(108.449088870497683d),
- Kilovolts(19.10630456834157630d)
+ Kilovolts(19.10630456834157630d),
)
- )
+ ),
)
inferiorGrid13.expectSlackVoltageProvision(
@@ -399,52 +367,50 @@ class DBFSAlgorithmCenGridSpec
ExchangeVoltage(
node3.getUuid,
Kilovolts(108.470028019077087d),
- Kilovolts(19.104403047662570d)
+ Kilovolts(19.104403047662570d),
),
ExchangeVoltage(
node4.getUuid,
Kilovolts(108.482524607256866d),
- Kilovolts(19.1025584700935336d)
- )
- )
+ Kilovolts(19.1025584700935336d),
+ ),
+ ),
)
// we now answer the requests of our centerGridAgent
// with three fake grid power message
- inferiorGrid11.gaProbe.send(
- secondPowerRequestSender11,
+
+ secondPowerRequestSender11 ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGrid11.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
)
- inferiorGrid12.gaProbe.send(
- secondPowerRequestSender12,
+ secondPowerRequestSender12 ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGrid12.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
)
- inferiorGrid13.gaProbe.send(
- secondPowerRequestSender13,
+ secondPowerRequestSender13 ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGrid13.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
@@ -456,39 +422,34 @@ class DBFSAlgorithmCenGridSpec
ExchangePower(
supNodeA.getUuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
ExchangePower(
supNodeB.getUuid,
Megawatts(0.16090577067051856),
- Megavars(-1.4535602358772026)
- )
+ Megavars(-1.4535602358772026),
+ ),
)
)
// normally the slack node would send a FinishGridSimulationTrigger to all
// connected inferior grids, because the slack node is just a mock, we imitate this behavior
- superiorGridAgent.gaProbe.send(
- centerGridAgent,
- FinishGridSimulationTrigger(3600)
- )
+ centerGridAgent ! FinishGridSimulationTrigger(3600)
// after a FinishGridSimulationTrigger is send the inferior grids, they themselves will send the
// Trigger forward the trigger to their connected inferior grids. Therefore the inferior grid
// agent should receive a FinishGridSimulationTrigger
- inferiorGrid11.gaProbe.expectMsg(FinishGridSimulationTrigger(3600))
- inferiorGrid12.gaProbe.expectMsg(FinishGridSimulationTrigger(3600))
- inferiorGrid13.gaProbe.expectMsg(FinishGridSimulationTrigger(3600))
-
- // after all grids have received a FinishGridSimulationTrigger, the scheduler should receive a CompletionMessage
- scheduler.expectMsg(
- Completion(
- centerGridAgent.toTyped,
- Some(7200)
- )
- )
+ inferiorGrid11.gaProbe.expectMessage(FinishGridSimulationTrigger(3600))
+
+ inferiorGrid12.gaProbe.expectMessage(FinishGridSimulationTrigger(3600))
+
+ inferiorGrid13.gaProbe.expectMessage(FinishGridSimulationTrigger(3600))
+
+ // after all grids have received a FinishGridSimulationTrigger, the scheduler should receive a Completion
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(7200)
- resultListener.expectMsgPF() {
+ val resultMessage = resultListener.expectMessageType[ResultEvent]
+ resultMessage match {
case powerFlowResultEvent: PowerFlowResultEvent =>
// we expect results for 4 nodes, 5 lines and 2 transformer2ws
powerFlowResultEvent.nodeResults.size shouldBe 4
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala
index 85b4bb041a..5e24639625 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmFailedPowerFlowSpec.scala
@@ -6,37 +6,34 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorSystem
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.testkit.{ImplicitSender, TestProbe}
-import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.models.input.container.ThermalGrid
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.agent.grid.GridAgent.FinishGridSimulationTrigger
import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
-import edu.ie3.simona.agent.state.GridAgentState.SimulateGrid
+import edu.ie3.simona.agent.grid.GridAgentMessage._
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
import edu.ie3.simona.model.grid.RefSystem
-import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
import edu.ie3.simona.ontology.messages.PowerMessage.{
FailedPowerFlow,
- ProvideGridPowerMessage
+ ProvideGridPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.test.common.model.grid.DbfsTestGrid
-import edu.ie3.simona.test.common.{
- ConfigTestData,
- TestKitWithShutdown,
- TestSpawnerClassic
-}
+import edu.ie3.simona.test.common.{ConfigTestData, TestSpawnerTyped}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.scala.quantities.Megavars
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
import squants.electro.Kilovolts
import squants.energy.Megawatts
@@ -44,30 +41,21 @@ import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
class DBFSAlgorithmFailedPowerFlowSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "DBFSAlgorithmSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- )
+ extends ScalaTestWithActorTestKit
with DBFSMockGridAgents
with ConfigTestData
- with ImplicitSender
with DbfsTestGrid
- with TestSpawnerClassic {
+ with TestSpawnerTyped {
- private val scheduler = TestProbe("scheduler")
- private val runtimeEvents = TestProbe("runtimeEvents")
+ private val scheduler: TestProbe[SchedulerMessage] = TestProbe("scheduler")
+ private val runtimeEvents: TestProbe[RuntimeEvent] =
+ TestProbe("runtimeEvents")
private val primaryService = TestProbe("primaryService")
private val weatherService = TestProbe("weatherService")
private val superiorGridAgent = SuperiorGA(
TestProbe("superiorGridAgent_1000"),
- Seq(supNodeA.getUuid)
+ Seq(supNodeA.getUuid),
)
private val inferiorGridAgent =
@@ -76,21 +64,21 @@ class DBFSAlgorithmFailedPowerFlowSpec
private val environmentRefs = EnvironmentRefs(
scheduler = scheduler.ref,
runtimeEventListener = runtimeEvents.ref,
- primaryServiceProxy = primaryService.ref,
- weather = weatherService.ref,
- evDataService = None
+ primaryServiceProxy = primaryService.ref.toClassic,
+ weather = weatherService.ref.toClassic,
+ evDataService = None,
)
- val resultListener: TestProbe = TestProbe("resultListener")
+ val resultListener: TestProbe[ResultEvent] = TestProbe("resultListener")
"A GridAgent actor in center position with async test" should {
val centerGridAgent =
- system.actorOf(
- GridAgent.props(
+ testKit.spawn(
+ GridAgent(
environmentRefs,
simonaConfig,
- listener = Iterable(resultListener.ref)
+ listener = Iterable(resultListener.ref),
)
)
@@ -111,53 +99,43 @@ class DBFSAlgorithmFailedPowerFlowSpec
hvGridContainerPF,
Seq.empty[ThermalGrid],
subGridGateToActorRef,
- RefSystem("2000 MVA", "110 kV")
+ RefSystem("2000 MVA", "110 kV"),
)
val key =
- ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
- scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled
+ ScheduleLock.singleKey(TSpawner, scheduler.ref, INIT_SIM_TICK)
+ // lock activation scheduled
+ scheduler.expectMessageType[ScheduleActivation]
- centerGridAgent ! GridAgent.Create(
+ centerGridAgent ! CreateGridAgent(
gridAgentInitData,
- key
- )
- scheduler.expectMsg(
- ScheduleActivation(centerGridAgent.toTyped, INIT_SIM_TICK, Some(key))
+ key,
)
- scheduler.send(centerGridAgent, Activation(INIT_SIM_TICK))
- scheduler.expectMsg(
- Completion(
- centerGridAgent.toTyped,
- Some(3600)
- )
- )
+ val scheduleActivationMsg =
+ scheduler.expectMessageType[ScheduleActivation]
+ scheduleActivationMsg.tick shouldBe INIT_SIM_TICK
+ scheduleActivationMsg.unlockKey shouldBe Some(key)
+ val gridAgentActivation = scheduleActivationMsg.actor
+ centerGridAgent ! WrappedActivation(Activation(INIT_SIM_TICK))
+ scheduler.expectMessage(Completion(gridAgentActivation, Some(3600)))
}
- s"go to $SimulateGrid when it receives an activation" in {
+ s"go to SimulateGrid when it receives an activation" in {
// send init data to agent
- scheduler.send(
- centerGridAgent,
- Activation(3600)
- )
+ centerGridAgent ! WrappedActivation(Activation(3600))
// we expect a completion message
- scheduler.expectMsg(
- Completion(
- centerGridAgent.toTyped,
- Some(3600)
- )
- )
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(3600)
}
s"start the simulation when an activation is sent is sent, handle failed power flow if it occurs" in {
val sweepNo = 0
// send the start grid simulation trigger
- scheduler.send(centerGridAgent, Activation(3600))
+ centerGridAgent ! WrappedActivation(Activation(3600))
// we expect a request for grid power values here for sweepNo $sweepNo
val powerRequestSender = inferiorGridAgent.expectGridPowerRequest()
@@ -179,38 +157,34 @@ class DBFSAlgorithmFailedPowerFlowSpec
ExchangeVoltage(
node1.getUuid,
Kilovolts(110d),
- Kilovolts(0d)
+ Kilovolts(0d),
)
- )
+ ),
)
// we now answer the request of our centerGridAgent
// with a fake grid power message and one fake slack voltage message
- inferiorGridAgent.gaProbe.send(
- powerRequestSender,
+ powerRequestSender ! WrappedPowerMessage(
ProvideGridPowerMessage(
inferiorGridAgent.nodeUuids.map(nodeUuid =>
ExchangePower(
nodeUuid,
Megawatts(1000.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
)
)
- superiorGridAgent.gaProbe.send(
- slackVoltageRequestSender,
- ProvideSlackVoltageMessage(
- sweepNo,
- Seq(
- ExchangeVoltage(
- supNodeA.getUuid,
- Kilovolts(380d),
- Kilovolts(0d)
- )
+ slackVoltageRequestSender ! ProvideSlackVoltageMessage(
+ sweepNo,
+ Seq(
+ ExchangeVoltage(
+ supNodeA.getUuid,
+ Kilovolts(380d),
+ Kilovolts(0d),
)
- )
+ ),
)
// power flow calculation should run now. After it's done,
@@ -221,27 +195,22 @@ class DBFSAlgorithmFailedPowerFlowSpec
// the requested power is to high for the grid to handle, therefore the superior grid agent
// receives a FailedPowerFlow message
// wait 30 seconds max for power flow to finish
- superiorGridAgent.gaProbe.expectMsg(30 seconds, FailedPowerFlow)
+ superiorGridAgent.gaProbe.expectMessage(
+ 30 seconds,
+ WrappedPowerMessage(FailedPowerFlow),
+ )
// normally the slack node would send a FinishGridSimulationTrigger to all
// connected inferior grids, because the slack node is just a mock, we imitate this behavior
- superiorGridAgent.gaProbe.send(
- centerGridAgent,
- FinishGridSimulationTrigger(3600)
- )
+ centerGridAgent ! FinishGridSimulationTrigger(3600)
// after a FinishGridSimulationTrigger is send to the inferior grids, they themselves will
// forward the trigger to their connected inferior grids. Therefore the inferior grid agent
// should receive a FinishGridSimulationTrigger
- inferiorGridAgent.gaProbe.expectMsg(FinishGridSimulationTrigger(3600))
+ inferiorGridAgent.gaProbe.expectMessage(FinishGridSimulationTrigger(3600))
- // after all grids have received a FinishGridSimulationTrigger, the scheduler should receive a CompletionMessage
- scheduler.expectMsg(
- Completion(
- centerGridAgent.toTyped,
- Some(7200)
- )
- )
+ // after all grids have received a FinishGridSimulationTrigger, the scheduler should receive a Completion
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(7200)
resultListener.expectNoMessage()
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala
index f104f20bbf..4bcbe85b42 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmParticipantSpec.scala
@@ -6,93 +6,86 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.typed.scaladsl.adapter.{
- ClassicActorRefOps,
- TypedActorRefOps
-}
-import org.apache.pekko.actor.{ActorRef, ActorSystem}
-import org.apache.pekko.testkit.{ImplicitSender, TestProbe}
-import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.graph.SubGridGate
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.agent.grid.GridAgent.FinishGridSimulationTrigger
import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
-import edu.ie3.simona.agent.state.GridAgentState.SimulateGrid
+import edu.ie3.simona.agent.grid.GridAgentMessage.{
+ CreateGridAgent,
+ FinishGridSimulationTrigger,
+ WrappedActivation,
+}
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
import edu.ie3.simona.model.grid.RefSystem
-import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.ontology.messages.services.ServiceMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationFailedMessage
+import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.test.common.model.grid.DbfsTestGridWithParticipants
-import edu.ie3.simona.test.common.{
- ConfigTestData,
- TestKitWithShutdown,
- TestSpawnerClassic
-}
+import edu.ie3.simona.test.common.{ConfigTestData, TestSpawnerTyped}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.scala.quantities.Megavars
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
import squants.electro.Kilovolts
import squants.energy.Megawatts
import scala.language.postfixOps
class DBFSAlgorithmParticipantSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "DBFSAlgorithmSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- )
+ extends ScalaTestWithActorTestKit
with DBFSMockGridAgents
with ConfigTestData
- with ImplicitSender
with DbfsTestGridWithParticipants
- with TestSpawnerClassic {
+ with TestSpawnerTyped {
- private val scheduler = TestProbe("scheduler")
- private val runtimeEvents = TestProbe("runtimeEvents")
- private val primaryService = TestProbe("primaryService")
+ private val scheduler: TestProbe[SchedulerMessage] = TestProbe("scheduler")
+ private val runtimeEvents: TestProbe[RuntimeEvent] =
+ TestProbe("runtimeEvents")
+ private val primaryService: TestProbe[ServiceMessage] =
+ TestProbe("primaryService")
private val weatherService = TestProbe("weatherService")
private val environmentRefs = EnvironmentRefs(
scheduler = scheduler.ref,
runtimeEventListener = runtimeEvents.ref,
- primaryServiceProxy = primaryService.ref,
- weather = weatherService.ref,
- evDataService = None
+ primaryServiceProxy = primaryService.ref.toClassic,
+ weather = weatherService.ref.toClassic,
+ evDataService = None,
)
- protected val resultListener: TestProbe = TestProbe("resultListener")
+ protected val resultListener: TestProbe[ResultEvent] =
+ TestProbe("resultListener")
private val superiorGridAgent = SuperiorGA(
TestProbe("superiorGridAgent_1000"),
- Seq(supNodeA.getUuid)
+ Seq(supNodeA.getUuid),
)
"Test participant" should {
- val gridAgentWithParticipants = system.actorOf(
- GridAgent.props(
+ val gridAgentWithParticipants = testKit.spawn(
+ GridAgent(
environmentRefs,
simonaConfig,
- Iterable(resultListener.ref)
+ Iterable(resultListener.ref),
)
)
s"initialize itself when it receives an init activation" in {
// this subnet has 1 superior grid (ehv) and 3 inferior grids (mv). Map the gates to test probes accordingly
- val subGridGateToActorRef: Map[SubGridGate, ActorRef] =
+ val subGridGateToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]] =
hvSubGridGates.map { gate =>
gate -> superiorGridAgent.ref
}.toMap
@@ -101,78 +94,58 @@ class DBFSAlgorithmParticipantSpec
hvGridContainer,
Seq.empty,
subGridGateToActorRef,
- RefSystem("2000 MVA", "110 kV")
+ RefSystem("2000 MVA", "110 kV"),
)
val key =
- ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
- scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled
-
- gridAgentWithParticipants ! GridAgent.Create(gridAgentInitData, key)
- scheduler.expectMsg(
- ScheduleActivation(
- gridAgentWithParticipants.toTyped,
- INIT_SIM_TICK,
- Some(key)
- )
- )
+ ScheduleLock.singleKey(TSpawner, scheduler.ref, INIT_SIM_TICK)
+ scheduler
+ .expectMessageType[ScheduleActivation] // lock activation scheduled
- // send init data to agent and expect a CompletionMessage
- scheduler.send(gridAgentWithParticipants, Activation(INIT_SIM_TICK))
-
- val loadAgent =
- scheduler.expectMsgPF() {
- case ScheduleActivation(
- loadAgent,
- INIT_SIM_TICK,
- _
- ) =>
- loadAgent
- }
-
- scheduler.expectMsg(
- Completion(
- gridAgentWithParticipants.toTyped,
- Some(3600)
- )
- )
+ gridAgentWithParticipants ! CreateGridAgent(gridAgentInitData, key)
+
+ val scheduleActivationMsg =
+ scheduler.expectMessageType[ScheduleActivation]
+ scheduleActivationMsg.tick shouldBe INIT_SIM_TICK
+ scheduleActivationMsg.unlockKey shouldBe Some(key)
+ val gridAgentActivation = scheduleActivationMsg.actor
+
+ // send init data to agent and expect a Completion
+ gridAgentWithParticipants ! WrappedActivation(Activation(INIT_SIM_TICK))
- scheduler.send(loadAgent.toClassic, Activation(INIT_SIM_TICK))
+ val scheduleLoadAgentMsg = scheduler.expectMessageType[ScheduleActivation]
+ scheduleLoadAgentMsg.tick shouldBe INIT_SIM_TICK
+ val loadAgent = scheduleLoadAgentMsg.actor
- primaryService.expectMsg(
+ scheduler.expectMessage(Completion(gridAgentActivation, Some(3600)))
+
+ loadAgent ! Activation(INIT_SIM_TICK)
+
+ primaryService.expectMessage(
PrimaryServiceRegistrationMessage(load1.getUuid)
)
- primaryService.send(loadAgent.toClassic, RegistrationFailedMessage)
+ loadAgent.toClassic ! RegistrationFailedMessage(
+ primaryService.ref.toClassic
+ )
- scheduler.expectMsg(Completion(loadAgent, Some(0)))
+ scheduler.expectMessage(Completion(loadAgent, Some(0)))
// triggering the loadAgent's calculation
- scheduler.send(
- loadAgent.toClassic,
- Activation(0)
- )
- // the load agent should send a CompletionMessage
- scheduler.expectMsg(Completion(loadAgent, None))
+ loadAgent ! Activation(0)
+
+ // the load agent should send a Completion
+ scheduler.expectMessage(Completion(loadAgent, None))
}
- s"go to $SimulateGrid when it receives an activity start trigger" in {
+ s"go to SimulateGrid when it receives an activity start trigger" in {
// send init data to agent
- scheduler.send(
- gridAgentWithParticipants,
- Activation(3600)
- )
+ gridAgentWithParticipants ! WrappedActivation(Activation(3600))
// we expect a completion message
- scheduler.expectMsg(
- Completion(
- gridAgentWithParticipants.toTyped,
- Some(3600)
- )
- )
-
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(3600)
}
s"check the request asset power message indirectly" in {
@@ -181,7 +154,7 @@ class DBFSAlgorithmParticipantSpec
// send the start grid simulation trigger
// the gird agent should send a RequestAssetPowerMessage to the load agent
- scheduler.send(gridAgentWithParticipants, Activation(3600))
+ gridAgentWithParticipants ! WrappedActivation(Activation(3600))
// we expect a request for voltage values of our slack node
// (voltages are requested by our agent under test from the superior grid)
@@ -190,18 +163,15 @@ class DBFSAlgorithmParticipantSpec
// we now answer the request of our gridAgentsWithParticipants
// with a fake slack voltage message
- superiorGridAgent.gaProbe.send(
- firstSlackVoltageRequestSender,
- ProvideSlackVoltageMessage(
- firstSweepNo,
- Seq(
- ExchangeVoltage(
- supNodeA.getUuid,
- Kilovolts(380d),
- Kilovolts(0d)
- )
+ firstSlackVoltageRequestSender ! ProvideSlackVoltageMessage(
+ firstSweepNo,
+ Seq(
+ ExchangeVoltage(
+ supNodeA.getUuid,
+ Kilovolts(380d),
+ Kilovolts(0d),
)
- )
+ ),
)
// power flow calculation should run now. After it's done,
@@ -209,7 +179,7 @@ class DBFSAlgorithmParticipantSpec
// hence we ask for them and expect a corresponding response
superiorGridAgent.requestGridPower(
gridAgentWithParticipants,
- firstSweepNo
+ firstSweepNo,
)
// the gridAgentWithParticipants has received an AssetPowerChangedMessage
@@ -219,7 +189,7 @@ class DBFSAlgorithmParticipantSpec
ExchangePower(
supNodeA.getUuid,
Megawatts(135.90837346741768),
- Megavars(60.98643348675892)
+ Megavars(60.98643348675892),
)
)
)
@@ -230,7 +200,7 @@ class DBFSAlgorithmParticipantSpec
superiorGridAgent.requestGridPower(
gridAgentWithParticipants,
- secondSweepNo
+ secondSweepNo,
)
// the agent now should ask for updated slack voltages from the superior grid
@@ -238,18 +208,15 @@ class DBFSAlgorithmParticipantSpec
superiorGridAgent.expectSlackVoltageRequest(secondSweepNo)
// the superior grid would answer with updated slack voltage values
- superiorGridAgent.gaProbe.send(
- secondSlackAskSender,
- ProvideSlackVoltageMessage(
- secondSweepNo,
- Seq(
- ExchangeVoltage(
- supNodeA.getUuid,
- Kilovolts(374.2269461446d),
- Kilovolts(65.9863075134d)
- )
+ secondSlackAskSender ! ProvideSlackVoltageMessage(
+ secondSweepNo,
+ Seq(
+ ExchangeVoltage(
+ supNodeA.getUuid,
+ Kilovolts(374.2269461446d),
+ Kilovolts(65.9863075134d),
)
- )
+ ),
)
// here the gridAgentWithParticipants has received a second AssetPowerUnchangedMessage
@@ -259,24 +226,16 @@ class DBFSAlgorithmParticipantSpec
ExchangePower(
supNodeA.getUuid,
Megawatts(135.90837346741768),
- Megavars(60.98643348675892)
+ Megavars(60.98643348675892),
)
)
)
// normally the superior grid agent would send a FinishGridSimulationTrigger to the inferior grid agent after the convergence
// (here we do it by hand)
- superiorGridAgent.gaProbe.send(
- gridAgentWithParticipants,
- FinishGridSimulationTrigger(3600L)
- )
+ gridAgentWithParticipants ! FinishGridSimulationTrigger(3600L)
- scheduler.expectMsg(
- Completion(
- gridAgentWithParticipants.toTyped,
- Some(7200)
- )
- )
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(7200)
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala
index ca8687a44b..1c2907a411 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSAlgorithmSupGridSpec.scala
@@ -6,38 +6,41 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.actor.{ActorRef, ActorSystem}
-import org.apache.pekko.testkit.{ImplicitSender, TestProbe}
-import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.graph.SubGridGate
import edu.ie3.datamodel.models.input.container.ThermalGrid
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.agent.grid.GridAgent.FinishGridSimulationTrigger
import edu.ie3.simona.agent.grid.GridAgentData.GridAgentInitData
-import edu.ie3.simona.agent.state.GridAgentState.SimulateGrid
+import edu.ie3.simona.agent.grid.GridAgentMessage.{
+ CreateGridAgent,
+ FinishGridSimulationTrigger,
+ WrappedActivation,
+ WrappedPowerMessage,
+}
import edu.ie3.simona.event.ResultEvent.PowerFlowResultEvent
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
import edu.ie3.simona.model.grid.RefSystem
-import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
import edu.ie3.simona.ontology.messages.PowerMessage.{
ProvideGridPowerMessage,
- RequestGridPowerMessage
+ RequestGridPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
+import edu.ie3.simona.ontology.messages.services.ServiceMessage
+import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.ScheduleLock
import edu.ie3.simona.test.common.model.grid.DbfsTestGrid
-import edu.ie3.simona.test.common.{
- ConfigTestData,
- TestKitWithShutdown,
- TestSpawnerClassic,
- UnitSpec
-}
+import edu.ie3.simona.test.common.{ConfigTestData, TestSpawnerTyped, UnitSpec}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.scala.quantities.Megavars
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
import squants.energy.Megawatts
import java.util.UUID
@@ -50,49 +53,41 @@ import scala.language.postfixOps
* [[GridAgent]] are simulated by the TestKit.
*/
class DBFSAlgorithmSupGridSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "DBFSAlgorithmSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- )
+ extends ScalaTestWithActorTestKit
with UnitSpec
with ConfigTestData
- with ImplicitSender
with DbfsTestGrid
- with TestSpawnerClassic {
+ with TestSpawnerTyped {
- private val scheduler: TestProbe = TestProbe("scheduler")
- private val runtimeEvents = TestProbe("runtimeEvents")
- private val primaryService: TestProbe = TestProbe("primaryService")
- private val weatherService: TestProbe = TestProbe("weatherService")
- private val hvGrid: TestProbe = TestProbe("hvGrid")
+ private val scheduler: TestProbe[SchedulerMessage] = TestProbe("scheduler")
+ private val runtimeEvents: TestProbe[RuntimeEvent] =
+ TestProbe("runtimeEvents")
+ private val primaryService: TestProbe[ServiceMessage] =
+ TestProbe("primaryService")
+ private val weatherService = TestProbe("weatherService")
+ private val hvGrid: TestProbe[GridAgentMessage] = TestProbe("hvGrid")
private val environmentRefs = EnvironmentRefs(
scheduler = scheduler.ref,
runtimeEventListener = runtimeEvents.ref,
- primaryServiceProxy = primaryService.ref,
- weather = weatherService.ref,
- evDataService = None
+ primaryServiceProxy = primaryService.ref.toClassic,
+ weather = weatherService.ref.toClassic,
+ evDataService = None,
)
- val resultListener: TestProbe = TestProbe("resultListener")
+ val resultListener: TestProbe[ResultEvent] = TestProbe("resultListener")
"A GridAgent actor in superior position with async test" should {
- val superiorGridAgentFSM: ActorRef = system.actorOf(
- GridAgent.props(
+ val superiorGridAgentFSM: ActorRef[GridAgentMessage] = testKit.spawn(
+ GridAgent(
environmentRefs,
simonaConfig,
- listener = Iterable(resultListener.ref)
+ listener = Iterable(resultListener.ref),
)
)
s"initialize itself when it receives an init activation" in {
- val subnetGatesToActorRef: Map[SubGridGate, ActorRef] =
+ val subnetGatesToActorRef: Map[SubGridGate, ActorRef[GridAgentMessage]] =
ehvSubGridGates.map(gate => gate -> hvGrid.ref).toMap
val gridAgentInitData =
@@ -100,34 +95,32 @@ class DBFSAlgorithmSupGridSpec
ehvGridContainer,
Seq.empty[ThermalGrid],
subnetGatesToActorRef,
- RefSystem("5000 MVA", "380 kV")
+ RefSystem("5000 MVA", "380 kV"),
)
val key =
- ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
- scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled
-
- superiorGridAgentFSM ! GridAgent.Create(gridAgentInitData, key)
- scheduler.expectMsg(
- ScheduleActivation(
- superiorGridAgentFSM.toTyped,
- INIT_SIM_TICK,
- Some(key)
- )
- )
+ ScheduleLock.singleKey(TSpawner, scheduler.ref, INIT_SIM_TICK)
+ // lock activation scheduled
+ scheduler.expectMessageType[ScheduleActivation]
+
+ superiorGridAgentFSM ! CreateGridAgent(gridAgentInitData, key)
- scheduler.send(superiorGridAgentFSM, Activation(INIT_SIM_TICK))
- scheduler.expectMsg(Completion(superiorGridAgentFSM.toTyped, Some(3600)))
+ val scheduleActivationMsg =
+ scheduler.expectMessageType[ScheduleActivation]
+ scheduleActivationMsg.tick shouldBe INIT_SIM_TICK
+ scheduleActivationMsg.unlockKey shouldBe Some(key)
+ val gridAgentActivation = scheduleActivationMsg.actor
+ superiorGridAgentFSM ! WrappedActivation(Activation(INIT_SIM_TICK))
+ scheduler.expectMessage(Completion(gridAgentActivation, Some(3600)))
}
- s"go to $SimulateGrid when it receives an activity start trigger" in {
+ s"go to SimulateGrid when it receives an activity start trigger" in {
// send init data to agent
- scheduler.send(superiorGridAgentFSM, Activation(3600))
+ superiorGridAgentFSM ! WrappedActivation(Activation(3600))
// we expect a completion message
- scheduler.expectMsg(Completion(superiorGridAgentFSM.toTyped, Some(3600)))
-
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(3600)
}
s"start the simulation, do 2 sweeps and should end afterwards when no deviation on nodal " +
@@ -139,31 +132,35 @@ class DBFSAlgorithmSupGridSpec
Vector(UUID.fromString("9fe5fa33-6d3b-4153-a829-a16f4347bc4e"))
// send the start grid simulation trigger
- scheduler.send(superiorGridAgentFSM, Activation(3600))
+ superiorGridAgentFSM ! WrappedActivation(Activation(3600))
// we expect a request for grid power values here for sweepNo $sweepNo
- hvGrid.expectMsgPF() {
- case requestGridPowerMessage: RequestGridPowerMessage =>
+ val message = hvGrid.expectMessageType[WrappedPowerMessage]
+
+ val lastSender = message match {
+ case WrappedPowerMessage(
+ requestGridPowerMessage: RequestGridPowerMessage
+ ) =>
requestGridPowerMessage.currentSweepNo shouldBe sweepNo
requestGridPowerMessage.nodeUuids should contain allElementsOf requestedConnectionNodeUuids
+
+ requestGridPowerMessage.sender
case x =>
fail(
s"Invalid message received when expecting a request for grid power values! Message was $x"
)
-
}
// we return with a fake grid power message
// / as we are using the ask pattern, we cannot send it to the grid agent directly but have to send it to the
// / ask sender
- hvGrid.send(
- hvGrid.lastSender,
+ lastSender ! WrappedPowerMessage(
ProvideGridPowerMessage(
requestedConnectionNodeUuids.map { uuid =>
ExchangePower(
uuid,
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
}
)
@@ -172,17 +169,21 @@ class DBFSAlgorithmSupGridSpec
// we expect a completion message here and that the agent goes back to simulate grid
// and waits until the newly scheduled StartGridSimulationTrigger is send
// wait 30 seconds max for power flow to finish
- scheduler.expectMsgPF(30 seconds) {
+ scheduler.expectMessageType[Completion](130 seconds) match {
case Completion(_, Some(3600)) =>
// we expect another completion message when the agent is in SimulateGrid again
case Completion(_, Some(7200)) =>
// agent should be in Idle again and listener should contain power flow result data
- resultListener.expectMsgPF() {
+ val resultMessage = resultListener.expectMessageType[ResultEvent]
+
+ resultMessage match {
case powerFlowResultEvent: PowerFlowResultEvent =>
powerFlowResultEvent.nodeResults.headOption match {
case Some(value) =>
value.getvMag().getValue shouldBe 1
value.getvAng().getValue shouldBe 0
+ case None =>
+ fail(s"Expected a result but got none.")
}
// due to the fact that the used grid does not contain anything besides the one ehv node
@@ -196,7 +197,7 @@ class DBFSAlgorithmSupGridSpec
// no failed power flow
runtimeEvents.expectNoMessage()
- hvGrid.expectMsg(FinishGridSimulationTrigger(3600))
+ hvGrid.expectMessage(FinishGridSimulationTrigger(3600))
case x =>
fail(
@@ -219,33 +220,31 @@ class DBFSAlgorithmSupGridSpec
Array(
(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
(
Megawatts(0.1),
- Megavars(0.1)
+ Megavars(0.1),
),
(
Megawatts(0.0),
- Megavars(0.1)
+ Megavars(0.1),
),
(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
(
Megawatts(0.0),
- Megavars(0.0)
- )
+ Megavars(0.0),
+ ),
)
// bring agent in simulate grid state
- scheduler.send(superiorGridAgentFSM, Activation(3600))
+ superiorGridAgentFSM ! WrappedActivation(Activation(3600))
// we expect a completion message
- scheduler.expectMsg(
- Completion(superiorGridAgentFSM.toTyped, Some(3600))
- )
+ scheduler.expectMessageType[Completion].newTick shouldBe Some(3600)
// go on with testing the sweep behaviour
for (sweepNo <- 0 to maxNumberOfTestSweeps) {
@@ -254,13 +253,19 @@ class DBFSAlgorithmSupGridSpec
Vector(UUID.fromString("9fe5fa33-6d3b-4153-a829-a16f4347bc4e"))
// send the start grid simulation trigger
- scheduler.send(superiorGridAgentFSM, Activation(3600))
+ superiorGridAgentFSM ! WrappedActivation(Activation(3600))
// we expect a request for grid power values here for sweepNo $sweepNo
- hvGrid.expectMsgPF() {
- case requestGridPowerMessage: RequestGridPowerMessage =>
+ val message = hvGrid.expectMessageType[GridAgentMessage]
+
+ val lastSender = message match {
+ case WrappedPowerMessage(
+ requestGridPowerMessage: RequestGridPowerMessage
+ ) =>
requestGridPowerMessage.currentSweepNo shouldBe sweepNo
requestGridPowerMessage.nodeUuids should contain allElementsOf requestedConnectionNodeUuids
+
+ requestGridPowerMessage.sender
case x =>
fail(
s"Invalid message received when expecting a request for grid power values! Message was $x"
@@ -270,14 +275,13 @@ class DBFSAlgorithmSupGridSpec
// we return with a fake grid power message
// / as we are using the ask pattern, we cannot send it to the grid agent directly but have to send it to the
// / ask sender
- hvGrid.send(
- hvGrid.lastSender,
+ lastSender ! WrappedPowerMessage(
ProvideGridPowerMessage(
requestedConnectionNodeUuids.map { uuid =>
ExchangePower(
uuid,
deviations(sweepNo)._1,
- deviations(sweepNo)._2
+ deviations(sweepNo)._2,
)
}
)
@@ -288,18 +292,23 @@ class DBFSAlgorithmSupGridSpec
// Simulate Grid
// wait 30 seconds max for power flow to finish
- scheduler.expectMsgPF(30 seconds) {
+ scheduler.expectMessageType[Completion](30 seconds) match {
case Completion(_, Some(3600)) =>
// when we received a FinishGridSimulationTrigger (as inferior grid agent)
// we expect another completion message then as well (scheduler view)
case Completion(_, Some(7200)) =>
// after doing cleanup stuff, our agent should go back to idle again and listener should contain power flow result data
- resultListener.expectMsgPF() {
+ val resultMessage =
+ resultListener.expectMessageType[ResultEvent]
+
+ resultMessage match {
case powerFlowResultEvent: PowerFlowResultEvent =>
powerFlowResultEvent.nodeResults.headOption match {
case Some(value) =>
value.getvMag().getValue shouldBe 1
value.getvAng().getValue shouldBe 0
+ case None =>
+ fail(s"Expected a result but got none.")
}
// due to the fact that the used grid does not contain anything besides the one ehv node
@@ -313,7 +322,7 @@ class DBFSAlgorithmSupGridSpec
// no failed power flow
runtimeEvents.expectNoMessage()
- hvGrid.expectMsg(FinishGridSimulationTrigger(3600))
+ hvGrid.expectMessage(FinishGridSimulationTrigger(3600))
case x =>
fail(
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/DBFSMockGridAgents.scala b/src/test/scala/edu/ie3/simona/agent/grid/DBFSMockGridAgents.scala
index 0f19e2055d..27c542611c 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/DBFSMockGridAgents.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/DBFSMockGridAgents.scala
@@ -6,20 +6,21 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorRef
-import org.apache.pekko.testkit.TestProbe
+import edu.ie3.simona.agent.grid.GridAgentMessage.WrappedPowerMessage
+import edu.ie3.simona.agent.grid.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import edu.ie3.simona.agent.grid.VoltageMessage.{
+ ProvideSlackVoltageMessage,
+ RequestSlackVoltageMessage,
+}
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
import edu.ie3.simona.ontology.messages.PowerMessage.{
ProvideGridPowerMessage,
- RequestGridPowerMessage
-}
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
-import edu.ie3.simona.ontology.messages.VoltageMessage.{
- ProvideSlackVoltageMessage,
- RequestSlackVoltageMessage
+ RequestGridPowerMessage,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.scala.quantities.{Megavars, ReactivePower}
+import org.apache.pekko.actor.testkit.typed.scaladsl.TestProbe
+import org.apache.pekko.actor.typed.ActorRef
import squants.Power
import squants.electro.Volts
import squants.energy.Megawatts
@@ -40,41 +41,44 @@ trait DBFSMockGridAgents extends UnitSpec {
: squants.electro.ElectricPotential = Volts(1e-6)
sealed trait GAActorAndModel {
- val gaProbe: TestProbe
+ val gaProbe: TestProbe[GridAgentMessage]
val nodeUuids: Seq[UUID]
- def ref: ActorRef = gaProbe.ref
+ def ref: ActorRef[GridAgentMessage] = gaProbe.ref
}
final case class InferiorGA(
- override val gaProbe: TestProbe,
- override val nodeUuids: Seq[UUID]
+ override val gaProbe: TestProbe[GridAgentMessage],
+ override val nodeUuids: Seq[UUID],
) extends GAActorAndModel {
- def expectGridPowerRequest(): ActorRef = {
- gaProbe
- .expectMsgType[RequestGridPowerMessage]
- .nodeUuids should contain allElementsOf nodeUuids
+ def expectGridPowerRequest(): ActorRef[GridAgentMessage] = {
+ gaProbe.expectMessageType[GridAgentMessage] match {
+ case WrappedPowerMessage(
+ requestGridPowerMessage: RequestGridPowerMessage
+ ) =>
+ requestGridPowerMessage.nodeUuids should contain allElementsOf nodeUuids
- gaProbe.lastSender
+ requestGridPowerMessage.sender
+ }
}
def expectSlackVoltageProvision(
expectedSweepNo: Int,
- expectedExchangedVoltages: Seq[ExchangeVoltage]
+ expectedExchangedVoltages: Seq[ExchangeVoltage],
): Unit = {
- inside(gaProbe.expectMsgType[ProvideSlackVoltageMessage]) {
- case ProvideSlackVoltageMessage(sweepNo, exchangedVoltages) =>
- sweepNo shouldBe expectedSweepNo
+ gaProbe.expectMessageType[GridAgentMessage] match {
+ case msg: ProvideSlackVoltageMessage =>
+ msg.currentSweepNo shouldBe expectedSweepNo
- exchangedVoltages.size shouldBe expectedExchangedVoltages.size
+ msg.nodalSlackVoltages.size shouldBe expectedExchangedVoltages.size
expectedExchangedVoltages.foreach { expectedVoltage =>
- exchangedVoltages.find(
+ msg.nodalSlackVoltages.find(
_.nodeUuid == expectedVoltage.nodeUuid
) match {
case Some(ExchangeVoltage(_, actualE, actualF)) =>
- actualE ~= Volts(3d)
- actualF ~= expectedVoltage.f
+ actualE should approximate(expectedVoltage.e)
+ actualF should approximate(expectedVoltage.f)
case None =>
fail(
s"Expected ExchangeVoltage with node UUID ${expectedVoltage.nodeUuid} " +
@@ -85,45 +89,46 @@ trait DBFSMockGridAgents extends UnitSpec {
}
}
- def requestSlackVoltage(receiver: ActorRef, sweepNo: Int): Unit =
- gaProbe.send(
- receiver,
- RequestSlackVoltageMessage(sweepNo, nodeUuids)
- )
+ def requestSlackVoltage(
+ receiver: ActorRef[GridAgentMessage],
+ sweepNo: Int,
+ ): Unit =
+ receiver ! RequestSlackVoltageMessage(sweepNo, nodeUuids, gaProbe.ref)
}
final case class SuperiorGA(
- override val gaProbe: TestProbe,
- override val nodeUuids: Seq[UUID]
+ override val gaProbe: TestProbe[GridAgentMessage],
+ override val nodeUuids: Seq[UUID],
) extends GAActorAndModel {
- def expectSlackVoltageRequest(expectedSweepNo: Int): ActorRef = {
- inside(
- gaProbe
- .expectMsgType[RequestSlackVoltageMessage]
- ) {
- case RequestSlackVoltageMessage(msgSweepNo: Int, msgUuids: Seq[UUID]) =>
- msgSweepNo shouldBe expectedSweepNo
- msgUuids should have size nodeUuids.size
- msgUuids should contain allElementsOf nodeUuids
- }
+ def expectSlackVoltageRequest(
+ expectedSweepNo: Int
+ ): ActorRef[GridAgentMessage] = {
+ gaProbe.expectMessageType[GridAgentMessage] match {
+ case requestSlackVoltageMessage: RequestSlackVoltageMessage =>
+ requestSlackVoltageMessage.currentSweepNo shouldBe expectedSweepNo
+ requestSlackVoltageMessage.nodeUuids should have size nodeUuids.size
+ requestSlackVoltageMessage.nodeUuids should contain allElementsOf nodeUuids
- gaProbe.lastSender
+ requestSlackVoltageMessage.sender
+ }
}
def expectGridPowerProvision(
expectedExchangedPowers: Seq[ExchangePower],
- maxDuration: FiniteDuration = 30 seconds
+ maxDuration: FiniteDuration = 30 seconds,
): Unit = {
- inside(gaProbe.expectMsgType[ProvideGridPowerMessage](maxDuration)) {
- case ProvideGridPowerMessage(exchangedPower) =>
- exchangedPower should have size expectedExchangedPowers.size
+ gaProbe.expectMessageType[GridAgentMessage](maxDuration) match {
+ case WrappedPowerMessage(msg: ProvideGridPowerMessage) =>
+ msg.nodalResidualPower should have size expectedExchangedPowers.size
expectedExchangedPowers.foreach { expectedPower =>
- exchangedPower.find(_.nodeUuid == expectedPower.nodeUuid) match {
+ msg.nodalResidualPower.find(
+ _.nodeUuid == expectedPower.nodeUuid
+ ) match {
case Some(ExchangePower(_, actualP, actualQ)) =>
- (actualP ~= expectedPower.p) shouldBe true
- (actualQ ~= expectedPower.q) shouldBe true
+ actualP should approximate(expectedPower.p)
+ actualQ should approximate(expectedPower.q)
case None =>
fail(
s"Expected ExchangePower with node UUID ${expectedPower.nodeUuid} " +
@@ -131,17 +136,15 @@ trait DBFSMockGridAgents extends UnitSpec {
)
}
}
-
}
}
- def requestGridPower(receiver: ActorRef, sweepNo: Int): Unit = {
- gaProbe.send(
- receiver,
- RequestGridPowerMessage(
- sweepNo,
- nodeUuids
- )
+ def requestGridPower(
+ receiver: ActorRef[GridAgentMessage],
+ sweepNo: Int,
+ ): Unit = {
+ receiver ! WrappedPowerMessage(
+ RequestGridPowerMessage(sweepNo, nodeUuids, gaProbe.ref)
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentDataHelperSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/GridAgentDataHelperSpec.scala
index 76a438267d..3e2170cbdc 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentDataHelperSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/GridAgentDataHelperSpec.scala
@@ -16,7 +16,7 @@ object GridAgentDataHelperSpec {
final case class TestGridData(
subgridId: Int,
- subgridGates: Vector[SubGridGate]
+ subgridGates: Vector[SubGridGate],
) extends GridAgentDataHelper {
def getSuperiorGridGates: Vector[SubGridGate] =
@@ -44,31 +44,31 @@ class GridAgentDataHelperSpec extends UnitSpec with SubGridGateMokka {
UUID.fromString("107a3c34-d890-4f92-95e6-beb10975d9d3"),
1,
UUID.fromString("0292fbe9-63b7-4580-9920-2c7c24032abc"),
- 100
+ 100,
)
val superiorSubGridGate2: SubGridGate = build2wSubGridGate(
UUID.fromString("97e957fb-b1c0-4ae0-bfe0-329ed85616b4"),
2,
UUID.fromString("f05e40a4-8d48-44c3-bbe9-bebf10022fc0"),
- 100
+ 100,
)
val centerSubGridGate1: SubGridGate = build2wSubGridGate(
UUID.fromString("37ff2b4d-de5c-4036-b0b2-f343f839182c"),
100,
UUID.fromString("960deb7c-cf50-45d8-8563-5fdd713d90f2"),
- 1000
+ 1000,
)
val centerSubGridGate2: SubGridGate = build2wSubGridGate(
UUID.fromString("73a2ec29-9054-4e8d-8f0d-a945434c89d8"),
100,
UUID.fromString("b17a0ec9-9567-42c8-990f-186888c6eb37"),
- 2000
+ 2000,
)
val centerSubGridGate3: SubGridGate = build2wSubGridGate(
UUID.fromString("44065395-07bf-48ef-9df4-9e82c705946d"),
100,
UUID.fromString("3bcda4b0-2d1a-44f5-95c1-a63ce1d40bed"),
- 3000
+ 3000,
)
val superiorGridGates = Vector(superiorSubGridGate1)
val centerGridGates = Vector(
@@ -76,7 +76,7 @@ class GridAgentDataHelperSpec extends UnitSpec with SubGridGateMokka {
superiorSubGridGate2,
centerSubGridGate1,
centerSubGridGate2,
- centerSubGridGate3
+ centerSubGridGate3,
)
val inferiorGridGates = Vector(centerSubGridGate1)
@@ -99,7 +99,7 @@ class GridAgentDataHelperSpec extends UnitSpec with SubGridGateMokka {
superiorGridAgent.getSuperiorGridNodeUuids shouldBe Vector.empty[String]
centerGridAgent.getSuperiorGridNodeUuids shouldBe Vector(
UUID.fromString("107a3c34-d890-4f92-95e6-beb10975d9d3"),
- UUID.fromString("97e957fb-b1c0-4ae0-bfe0-329ed85616b4")
+ UUID.fromString("97e957fb-b1c0-4ae0-bfe0-329ed85616b4"),
)
inferiorGridAgent.getSuperiorGridNodeUuids shouldBe Vector(
UUID.fromString("37ff2b4d-de5c-4036-b0b2-f343f839182c")
@@ -114,7 +114,7 @@ class GridAgentDataHelperSpec extends UnitSpec with SubGridGateMokka {
centerGridAgent.getInferiorGridGates shouldBe Vector(
centerSubGridGate1,
centerSubGridGate2,
- centerSubGridGate3
+ centerSubGridGate3,
)
inferiorGridAgent.getInferiorGridGates shouldBe Vector.empty[SubGridGate]
@@ -133,7 +133,7 @@ class GridAgentDataHelperSpec extends UnitSpec with SubGridGateMokka {
superiorGridAgent.getSuperiorGridGates shouldBe Vector.empty[SubGridGate]
centerGridAgent.getSuperiorGridGates shouldBe Vector(
superiorSubGridGate1,
- superiorSubGridGate2
+ superiorSubGridGate2,
)
inferiorGridAgent.getSuperiorGridGates shouldBe Vector(centerSubGridGate1)
}
@@ -151,7 +151,7 @@ class GridAgentDataHelperSpec extends UnitSpec with SubGridGateMokka {
centerGridAgent.getInferiorGridNodeUuids shouldBe Vector(
UUID.fromString("960deb7c-cf50-45d8-8563-5fdd713d90f2"),
UUID.fromString("b17a0ec9-9567-42c8-990f-186888c6eb37"),
- UUID.fromString("3bcda4b0-2d1a-44f5-95c1-a63ce1d40bed")
+ UUID.fromString("3bcda4b0-2d1a-44f5-95c1-a63ce1d40bed"),
)
inferiorGridAgent.getInferiorGridNodeUuids shouldBe Vector.empty[String]
}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetup2WSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetup2WSpec.scala
deleted file mode 100644
index e58ad3c2fb..0000000000
--- a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetup2WSpec.scala
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * © 2020. 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
-
-import org.apache.pekko.actor.{
- Actor,
- ActorIdentity,
- ActorRef,
- ActorSystem,
- Identify,
- Props
-}
-import org.apache.pekko.testkit.ImplicitSender
-import org.apache.pekko.util.Timeout
-import com.typesafe.config.ConfigFactory
-import com.typesafe.scalalogging.LazyLogging
-import edu.ie3.datamodel.models.result.ResultEntity
-import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.io.result.ResultSinkType
-import edu.ie3.simona.sim.setup.SimonaStandaloneSetup
-import edu.ie3.simona.test.common.input.TransformerInputTestData
-import edu.ie3.simona.test.common.{ConfigTestData, TestKitWithShutdown}
-import edu.ie3.simona.util.ResultFileHierarchy
-import edu.ie3.simona.util.ResultFileHierarchy.ResultEntityPathConfig
-import org.scalatest.wordspec.AnyWordSpecLike
-
-import java.util.concurrent.TimeUnit
-import scala.concurrent.Await
-import scala.concurrent.duration.Duration
-
-class GridAgentSetup2WSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "GridAgentSetupSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- )
- with ImplicitSender
- with AnyWordSpecLike
- with TransformerInputTestData
- with ConfigTestData
- with LazyLogging {
-
- "The setup of grid agents" must {
- "provide two grid agents on presence of a two winding transformer" in {
-
- import org.apache.pekko.pattern._
- implicit val timeout: Timeout = Timeout(1, TimeUnit.SECONDS)
-
- // in order to get an actor system we need a tmp actor that calls the corresponding method
- Await.ready(
- system.actorOf(Props(new Actor {
- override def receive: Receive = { case "setup" =>
- val environmentRefs = EnvironmentRefs(
- scheduler = self,
- runtimeEventListener = self,
- primaryServiceProxy = self,
- weather = ActorRef.noSender,
- evDataService = None
- )
-
- SimonaStandaloneSetup(
- typesafeConfig,
- ResultFileHierarchy(
- "test/tmp",
- "GridAgentSetup2WSpec",
- ResultEntityPathConfig(
- Set.empty[Class[_ <: ResultEntity]],
- ResultSinkType(
- simonaConfig.simona.output.sink,
- simonaConfig.simona.simulationName
- )
- )
- )
- ).buildSubGridToActorRefMap(
- gridContainer.getSubGridTopologyGraph,
- context,
- environmentRefs,
- Seq.empty[ActorRef]
- )
- sender() ! "done"
- }
- })) ? "setup",
- Duration(1, TimeUnit.SECONDS)
- )
-
- val sel = system.actorSelection("user/**/GridAgent_*")
- sel ! Identify(0)
-
- logger.debug("Waiting 500 ms to collect all responses")
- val responses: Seq[ActorIdentity] =
- receiveWhile(
- max = Duration.create(500, "ms"),
- idle = Duration.create(250, "ms")
- ) { case msg: ActorIdentity =>
- msg
- }
- logger.debug("All responses received. Evaluating...")
-
- responses.size should be(2)
-
- val regex = """GridAgent_\d*""".r
- val expectedSenders = Vector("GridAgent_1", "GridAgent_2")
- val actualSenders = responses
- .collect { case actorId: ActorIdentity =>
- val actorRefString = actorId.getActorRef.toString
- regex.findFirstIn(actorRefString)
- }
- .flatten
- .sorted
- .toVector
-
- actualSenders should be(expectedSenders)
- }
- }
-}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetup3WSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetup3WSpec.scala
deleted file mode 100644
index f2b2d44f41..0000000000
--- a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetup3WSpec.scala
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * © 2020. 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
-
-import org.apache.pekko.actor.{
- Actor,
- ActorIdentity,
- ActorRef,
- ActorSystem,
- Identify,
- Props
-}
-import org.apache.pekko.testkit.ImplicitSender
-import org.apache.pekko.util.Timeout
-import com.typesafe.config.ConfigFactory
-import com.typesafe.scalalogging.LazyLogging
-import edu.ie3.datamodel.models.result.ResultEntity
-import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.io.result.ResultSinkType
-import edu.ie3.simona.sim.setup.SimonaStandaloneSetup
-import edu.ie3.simona.test.common.{
- ConfigTestData,
- TestKitWithShutdown,
- ThreeWindingTestData
-}
-import edu.ie3.simona.util.ResultFileHierarchy
-import edu.ie3.simona.util.ResultFileHierarchy.ResultEntityPathConfig
-import org.scalatest.wordspec.AnyWordSpecLike
-
-import java.util.concurrent.TimeUnit
-import scala.concurrent.Await
-import scala.concurrent.duration.Duration
-
-class GridAgentSetup3WSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "GridAgentSetupSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="DEBUG"
- """.stripMargin)
- )
- )
- with ImplicitSender
- with AnyWordSpecLike
- with ThreeWindingTestData
- with ConfigTestData
- with LazyLogging {
-
- "The setup of grid agents" must {
- "provide three grid agents on presence of a three winding transformer" in {
- import org.apache.pekko.pattern._
- implicit val timeout: Timeout = Timeout(1, TimeUnit.SECONDS)
-
- // in order to get an actor system we need a tmp actor that calls the corresponding method
- Await.ready(
- system.actorOf(Props(new Actor {
- override def receive: Receive = { case "setup" =>
- val environmentRefs = EnvironmentRefs(
- scheduler = self,
- runtimeEventListener = self,
- primaryServiceProxy = self,
- weather = ActorRef.noSender,
- evDataService = None
- )
-
- SimonaStandaloneSetup(
- typesafeConfig,
- ResultFileHierarchy(
- "test/tmp",
- "GridAgentSetup3WSpec",
- ResultEntityPathConfig(
- Set.empty[Class[_ <: ResultEntity]],
- ResultSinkType(
- simonaConfig.simona.output.sink,
- simonaConfig.simona.simulationName
- )
- )
- )
- ).buildSubGridToActorRefMap(
- threeWindingTestGrid.getSubGridTopologyGraph,
- context,
- environmentRefs,
- Seq.empty[ActorRef]
- )
- sender() ! "done"
- }
- })) ? "setup",
- Duration(1, TimeUnit.SECONDS)
- )
-
- val sel = system.actorSelection("user/**/GridAgent_*")
- sel ! Identify(0)
-
- logger.debug("Waiting 500ms to collect all responses")
- val responses: Seq[ActorIdentity] =
- receiveWhile(
- max = Duration.create(500, "ms"),
- idle = Duration.create(250, "ms")
- ) { case msg: ActorIdentity =>
- msg
- }
- logger.debug("All responses received. Evaluating...")
-
- responses.size should be(3)
- val regex = """GridAgent_\d*""".r
- val expectedSenders = Vector("GridAgent_1", "GridAgent_2", "GridAgent_3")
- val actualSenders = responses
- .collect { case actorId: ActorIdentity =>
- val actorRefString = actorId.getActorRef.toString
- regex.findFirstIn(actorRefString)
- }
- .flatten
- .sorted
- .toVector
-
- actualSenders should be(expectedSenders)
- }
- }
-
-}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetupSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetupSpec.scala
new file mode 100644
index 0000000000..5d7eea87c5
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/agent/grid/GridAgentSetupSpec.scala
@@ -0,0 +1,88 @@
+/*
+ * © 2020. 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
+
+import edu.ie3.simona.agent.EnvironmentRefs
+import edu.ie3.simona.sim.setup.SimonaStandaloneSetup
+import edu.ie3.simona.test.common.input.TransformerInputTestData
+import edu.ie3.simona.test.common.{
+ ConfigTestData,
+ ThreeWindingTestData,
+ UnitSpec,
+}
+import edu.ie3.simona.util.ResultFileHierarchy
+import org.apache.pekko.actor.testkit.typed.Effect.Spawned
+import org.apache.pekko.actor.testkit.typed.scaladsl.BehaviorTestKit
+import org.apache.pekko.actor.typed.scaladsl.Behaviors
+import org.scalatestplus.mockito.MockitoSugar
+
+class GridAgentSetupSpec
+ extends UnitSpec
+ with MockitoSugar
+ with TransformerInputTestData
+ with ConfigTestData
+ with ThreeWindingTestData {
+
+ "The setup of grid agents" must {
+
+ "provide two grid agents on presence of a two winding transformer" in {
+
+ val testKit = BehaviorTestKit(Behaviors.setup[AnyRef] { ctx =>
+ SimonaStandaloneSetup(
+ typesafeConfig,
+ mock[ResultFileHierarchy],
+ ).buildSubGridToActorRefMap(
+ gridContainer.getSubGridTopologyGraph,
+ ctx,
+ mock[EnvironmentRefs],
+ Seq.empty,
+ )
+
+ Behaviors.stopped
+ })
+
+ // two actor should be spawned
+ testKit.expectEffectPF { case Spawned(_, actorName, _) =>
+ actorName shouldBe "1"
+ }
+
+ testKit.expectEffectPF { case Spawned(_, actorName, _) =>
+ actorName shouldBe "2"
+ }
+ }
+
+ "provide three grid agents on presence of a three winding transformer" in {
+
+ val testKit = BehaviorTestKit(Behaviors.setup[AnyRef] { ctx =>
+ SimonaStandaloneSetup(
+ typesafeConfig,
+ mock[ResultFileHierarchy],
+ ).buildSubGridToActorRefMap(
+ threeWindingTestGrid.getSubGridTopologyGraph,
+ ctx,
+ mock[EnvironmentRefs],
+ Seq.empty,
+ )
+
+ Behaviors.stopped
+ })
+
+ // three actor should be spawned
+ testKit.expectEffectPF { case Spawned(_, actorName, _) =>
+ actorName shouldBe "1"
+ }
+
+ testKit.expectEffectPF { case Spawned(_, actorName, _) =>
+ actorName shouldBe "2"
+ }
+
+ testKit.expectEffectPF { case Spawned(_, actorName, _) =>
+ actorName shouldBe "3"
+ }
+ }
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/GridResultsSupportSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/GridResultsSupportSpec.scala
index c9d17d2379..e273b06976 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/GridResultsSupportSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/GridResultsSupportSpec.scala
@@ -6,7 +6,6 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.event.{LoggingAdapter, NoLogging}
import breeze.math.Complex
import edu.ie3.datamodel.models.StandardUnits
import edu.ie3.datamodel.models.input.connector.ConnectorPort
@@ -14,7 +13,7 @@ import edu.ie3.datamodel.models.result.NodeResult
import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
- Transformer2WResult
+ Transformer2WResult,
}
import edu.ie3.powerflow.model.NodeData.StateData
import edu.ie3.powerflow.model.enums.NodeType
@@ -22,20 +21,20 @@ import edu.ie3.simona.agent.grid.GridResultsSupport.PartialTransformer3wResult
import edu.ie3.simona.model.grid.Transformer3wPowerFlowCase.{
PowerFlowCaseA,
PowerFlowCaseB,
- PowerFlowCaseC
+ PowerFlowCaseC,
}
import edu.ie3.simona.model.grid.{
RefSystem,
Transformer3wModel,
TransformerModel,
- TransformerTappingModel
+ TransformerTappingModel,
}
import edu.ie3.simona.test.common.exceptions.InvalidTestDataException
import edu.ie3.simona.test.common.input.GridInputTestData
import edu.ie3.simona.test.common.model.grid.{
BasicGrid,
BasicGridWithSwitches,
- TransformerTestData
+ TransformerTestData,
}
import edu.ie3.simona.test.common.{DefaultTestData, UnitSpec}
import edu.ie3.util.TimeUtil
@@ -61,7 +60,6 @@ class GridResultsSupportSpec
with GridInputTestData
with TableDrivenPropertyChecks {
- override protected val log: LoggingAdapter = NoLogging
implicit val currentTolerance: squants.electro.ElectricCurrent = Amperes(1e-6)
implicit val angleTolerance: squants.Angle = Degrees(1e-6)
@@ -75,8 +73,8 @@ class GridResultsSupportSpec
0,
NodeType.PQ,
Complex(0.9583756183209947, -0.04673985022513541),
- Complex(0.006466666857417822, 2.7286658176028933e-15)
- )
+ Complex(0.006466666857417822, 2.7286658176028933e-15),
+ ),
)
val nodeResult =
@@ -86,7 +84,7 @@ class GridResultsSupportSpec
nodeUuid,
Quantities.getQuantity(0.9595146895129939, PU),
Quantities
- .getQuantity(-2.79209521012981881159, DEGREE_GEOM)
+ .getQuantity(-2.79209521012981881159, DEGREE_GEOM),
)
nodeResult.getInputModel shouldBe expectedNodeResult.getInputModel
@@ -95,12 +93,12 @@ class GridResultsSupportSpec
QuantityUtil.isEquivalentAngle(
nodeResult.getvAng,
expectedNodeResult.getvAng,
- 1e-12
+ 1e-12,
) shouldBe true
QuantityUtil.isEquivalentAbs(
nodeResult.getvMag,
expectedNodeResult.getvMag,
- 1e-12
+ 1e-12,
) shouldBe true
}
}
@@ -112,7 +110,7 @@ class GridResultsSupportSpec
val expectedSwitchResult = new SwitchResult(
defaultSimulationStart,
switch1.uuid,
- switch1.isClosed
+ switch1.isClosed,
)
switchResult.getTime shouldBe expectedSwitchResult.getTime
@@ -132,7 +130,7 @@ class GridResultsSupportSpec
Quantities
.getQuantity(24.9409944828817942724, Units.AMPERE),
Quantities
- .getQuantity(153.57729374050189129708, DEGREE_GEOM)
+ .getQuantity(153.57729374050189129708, DEGREE_GEOM),
)
val nodeAStateData = new StateData(
@@ -140,18 +138,18 @@ class GridResultsSupportSpec
NodeType.PQ,
Complex(
0.8655176782269813,
- -0.037052090894132306
+ -0.037052090894132306,
), // Angle = -2,4512878986765928398°
- Complex(0.319999917504236, 4.86242990316299e-15)
+ Complex(0.319999917504236, 4.86242990316299e-15),
)
val nodeBStateData = new StateData(
2,
NodeType.PQ,
Complex(
0.8637364806386005,
- -0.03745498173182088
+ -0.03745498173182088,
), // Angle = -2,4830128149755043846°
- Complex(0.31999991750423107, -2.3469073906490223e-14)
+ Complex(0.31999991750423107, -2.3469073906490223e-14),
)
val lineResult: LineResult = calcLineResult(
@@ -159,7 +157,7 @@ class GridResultsSupportSpec
nodeAStateData,
nodeBStateData,
default400Kva10KvRefSystem.nominalCurrent,
- defaultSimulationStart
+ defaultSimulationStart,
)
lineResult.getInputModel shouldBe expectedLineResult.getInputModel
@@ -167,23 +165,23 @@ class GridResultsSupportSpec
QuantityUtil.isEquivalentAbs(
lineResult.getiAMag,
expectedLineResult.getiAMag,
- 1e-4
+ 1e-4,
) shouldBe true
QuantityUtil.isEquivalentAngle(
lineResult.getiAAng,
expectedLineResult.getiAAng,
- 1e-3
+ 1e-3,
) shouldBe true
QuantityUtil.isEquivalentAbs(
lineResult.getiBMag,
expectedLineResult.getiBMag,
- 1e-4
+ 1e-4,
) shouldBe true
QuantityUtil.isEquivalentAngle(
lineResult.getiBAng,
expectedLineResult.getiBAng,
- 1e-3
+ 1e-3,
) shouldBe true
// if line is disabled zero results are expected
@@ -193,7 +191,7 @@ class GridResultsSupportSpec
nodeAStateData,
nodeBStateData,
default400Kva10KvRefSystem.nominalCurrent,
- defaultSimulationStart
+ defaultSimulationStart,
)
disabledLineResult shouldBe new LineResult(
@@ -202,7 +200,7 @@ class GridResultsSupportSpec
ScalaQuantityUtil.zeroCompQuantity(Units.AMPERE),
ScalaQuantityUtil.zeroCompQuantity(DEGREE_GEOM),
ScalaQuantityUtil.zeroCompQuantity(Units.AMPERE),
- ScalaQuantityUtil.zeroCompQuantity(DEGREE_GEOM)
+ ScalaQuantityUtil.zeroCompQuantity(DEGREE_GEOM),
)
}
@@ -220,7 +218,7 @@ class GridResultsSupportSpec
iAMag: Double,
iAAng: Double,
iBMag: Double,
- iBAng: Double
+ iBAng: Double,
) =>
/* === Prepare test data and expected result === */
/* Get the correct transformer model */
@@ -241,7 +239,7 @@ class GridResultsSupportSpec
0,
NodeType.SL,
voltageHv,
- Complex.zero
+ Complex.zero,
)
/* Prepare node information for low voltage node */
@@ -249,13 +247,13 @@ class GridResultsSupportSpec
1,
NodeType.PQ,
voltageLv,
- powerLv
+ powerLv,
)
/* Set up grid's reference system */
val refSys = RefSystem(
Kilowatts(400d),
- Volts(400d)
+ Volts(400d),
)
/* Artificial time stamp */
@@ -270,7 +268,7 @@ class GridResultsSupportSpec
Quantities.getQuantity(iAAng, DEGREE_GEOM),
Quantities.getQuantity(iBMag, AMPERE),
Quantities.getQuantity(iBAng, DEGREE_GEOM),
- tapPos
+ tapPos,
)
/* === Perform the operation to test === */
@@ -279,7 +277,7 @@ class GridResultsSupportSpec
hvNodeStateData,
lvNodeStateData,
refSys.nominalCurrent,
- time
+ time,
)
/* === Examine the result === */
@@ -288,23 +286,23 @@ class GridResultsSupportSpec
QuantityUtil.isEquivalentAbs(
actual.getiAMag(),
expectedResult.getiAMag(),
- 1e-3
+ 1e-3,
) shouldBe true
QuantityUtil.isEquivalentAngle(
actual.getiAAng(),
expectedResult.getiAAng(),
- 1e-3
+ 1e-3,
) shouldBe true
QuantityUtil.isEquivalentAbs(
actual.getiBMag(),
expectedResult.getiBMag(),
- 1e-3
+ 1e-3,
) shouldBe true
if (
QuantityUtil.isEquivalentAngle(
actual.getiBAng(),
expectedResult.getiBAng(),
- 1e-3
+ 1e-3,
)
) {
/* Angles are considerably equal */
@@ -343,7 +341,7 @@ class GridResultsSupportSpec
)
)
.to(AMPERE),
- 1e-4
+ 1e-4,
) shouldBe true
/* Testing the imaginary part of the current */
QuantityUtil.isEquivalentAbs(
@@ -373,7 +371,7 @@ class GridResultsSupportSpec
)
)
.to(AMPERE),
- 1e-4
+ 1e-4,
) shouldBe true
}
actual.getTapPos shouldBe expectedResult.getTapPos
@@ -391,13 +389,13 @@ class GridResultsSupportSpec
0,
NodeType.SL,
Complex.one,
- Complex.zero
+ Complex.zero,
)
val nodeStateDataLv: StateData = StateData(
1,
NodeType.PQ,
Complex.one,
- Complex.zero
+ Complex.zero,
)
val expectedResult: Transformer2WResult = new Transformer2WResult(
@@ -407,7 +405,7 @@ class GridResultsSupportSpec
ScalaQuantityUtil.zeroCompQuantity(DEGREE_GEOM),
ScalaQuantityUtil.zeroCompQuantity(AMPERE),
ScalaQuantityUtil.zeroCompQuantity(DEGREE_GEOM),
- transformerModel.currentTapPos
+ transformerModel.currentTapPos,
)
calcTransformer2wResult(
@@ -416,9 +414,9 @@ class GridResultsSupportSpec
nodeStateDataLv,
RefSystem(
Kilowatts(400d),
- Volts(400d)
+ Volts(400d),
).nominalCurrent,
- TimeUtil.withDefaults.toZonedDateTime("2020-06-08 09:03:00")
+ TimeUtil.withDefaults.toZonedDateTime("2020-06-08 09:03:00"),
) shouldBe expectedResult
}
}
@@ -443,25 +441,25 @@ class GridResultsSupportSpec
10,
-10,
0,
- autoTap = true
+ autoTap = true,
),
1,
PowerFlowCaseA,
Each(0.1d),
Each(0.2d),
Each(0.3d),
- Each(0.4d)
+ Each(0.4d),
)
transformerA.initTapping()
val transformerB = transformerA.copy(
powerFlowCase = PowerFlowCaseB,
g = Each(0d),
- b = Each(0d)
+ b = Each(0d),
)
val transformerC = transformerA.copy(
powerFlowCase = PowerFlowCaseC,
g = Each(0d),
- b = Each(0d)
+ b = Each(0d),
)
val iNominal = Amperes(100d)
@@ -477,22 +475,20 @@ class GridResultsSupportSpec
nodeStateData,
internalNodeStateData,
iNominal,
- timeStamp
+ timeStamp,
) match {
case PartialTransformer3wResult.PortA(
time,
input,
currentMagnitude,
currentAngle,
- tapPos
+ tapPos,
) =>
time shouldBe timeStamp
input shouldBe transformerA.uuid
tapPos shouldBe transformerA.currentTapPos
- (currentMagnitude ~= Amperes(
- 13.15547500d
- )) shouldBe true
- (currentAngle ~= Degrees(-45.0000000d)) shouldBe true
+ currentMagnitude should approximate(Amperes(13.15547500d))
+ currentAngle should approximate(Degrees(-45.0000000d))
case wrong => fail(s"Got wrong result: '$wrong'")
}
}
@@ -507,18 +503,18 @@ class GridResultsSupportSpec
nodeStateData,
internalNodeStateData,
iNominal,
- timeStamp
+ timeStamp,
) match {
case PartialTransformer3wResult.PortB(
time,
input,
currentMagnitude,
- currentAngle
+ currentAngle,
) =>
time shouldBe timeStamp
input shouldBe transformerB.uuid
- (currentMagnitude ~= Amperes(14.14213562d)) shouldBe true
- (currentAngle ~= Degrees(135.000000d)) shouldBe true
+ currentMagnitude should approximate(Amperes(14.14213562d))
+ currentAngle should approximate(Degrees(135.000000d))
case wrong => fail(s"Got wrong result: '$wrong'")
}
}
@@ -533,20 +529,18 @@ class GridResultsSupportSpec
nodeStateData,
internalNodeStateData,
iNominal,
- timeStamp
+ timeStamp,
) match {
case PartialTransformer3wResult.PortC(
time,
input,
currentMagnitude,
- currentAngle
+ currentAngle,
) =>
time shouldBe timeStamp
input shouldBe transformerC.uuid
- (currentMagnitude ~= Amperes(
- 14.14213562d
- )) shouldBe true
- (currentAngle ~= Degrees(135.0000000d)) shouldBe true
+ currentMagnitude should approximate(Amperes(14.14213562d))
+ currentAngle should approximate(Degrees(135.0000000d))
case wrong => fail(s"Got wrong result: '$wrong'")
}
}
@@ -558,25 +552,23 @@ class GridResultsSupportSpec
default400Kva10KvRefSystem,
2,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
),
new StateData(0, NodeType.PQ, Complex(1, 0), Complex(1, 0)),
new StateData(1, NodeType.PQ, Complex(0.99, 0), Complex(0.98, 0)),
default400Kva10KvRefSystem.nominalCurrent,
- timeStamp
+ timeStamp,
) match {
case PartialTransformer3wResult.PortB(
time,
input,
currentMagnitude,
- currentAngle
+ currentAngle,
) =>
time shouldBe timeStamp
input shouldBe transformer3wInput.getUuid
- (currentMagnitude ~= Amperes(
- 11.4542161d
- )) shouldBe true
- (currentAngle ~= Degrees(-89.4475391d)) shouldBe true
+ currentMagnitude should approximate(Amperes(11.4542161d))
+ currentAngle should approximate(Degrees(-89.4475391d))
case wrong => fail(s"Got wrong result: '$wrong'")
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/grid/PowerFlowSupportSpec.scala b/src/test/scala/edu/ie3/simona/agent/grid/PowerFlowSupportSpec.scala
index 66805b06d9..719930b288 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/PowerFlowSupportSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/PowerFlowSupportSpec.scala
@@ -6,16 +6,20 @@
package edu.ie3.simona.agent.grid
-import org.apache.pekko.actor.ActorRef
-import org.apache.pekko.event.{LoggingAdapter, NoLogging}
import edu.ie3.powerflow.model.PowerFlowResult.SuccessFullPowerFlowResult.ValidNewtonRaphsonPFResult
import edu.ie3.simona.model.grid.GridModel
import edu.ie3.simona.ontology.messages.PowerMessage.ProvideGridPowerMessage.ExchangePower
-import edu.ie3.simona.ontology.messages.VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
+import VoltageMessage.ProvideSlackVoltageMessage.ExchangeVoltage
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.test.common.model.grid.BasicGridWithSwitches
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.Megavars
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.ActorRef
+import org.slf4j.{Logger, LoggerFactory}
import squants.electro.Kilovolts
import squants.energy.Megawatts
import tech.units.indriya.ComparableQuantity
@@ -31,12 +35,15 @@ import javax.measure.quantity.Angle
* order to comprehend the expected test results.
*/
class PowerFlowSupportSpec
- extends UnitSpec
+ extends ScalaTestWithActorTestKit
+ with UnitSpec
with BasicGridWithSwitches
with PowerFlowSupport
with GridResultsSupport {
- override val log: LoggingAdapter = NoLogging
+ implicit val log: Logger = LoggerFactory.getLogger(this.getClass)
+ val actorRef: ActorRef[GridAgentMessage] =
+ TestProbe[GridAgentMessage]("mock_grid_agent").ref
/** Setting voltage at slack node to 110 kV and introducing a load of 1 MW at
* node 1
@@ -49,21 +56,21 @@ class PowerFlowSupportSpec
ExchangeVoltage(
node6.uuid,
Kilovolts(110d),
- Kilovolts(0d)
+ Kilovolts(0d),
)
)
),
nodeToReceivedPower = Map(
node1.uuid -> Map(
- ActorRef.noSender -> Some(
+ actorRef -> Some(
ExchangePower(
node1.uuid,
Megawatts(1d),
- Megavars(0d)
+ Megavars(0d),
)
)
)
- )
+ ),
)
val currentTolerance = 1e-3 // 1 mA
@@ -97,7 +104,7 @@ class PowerFlowSupportSpec
gridModel.gridComponents.transformers3w,
gridModel.nodeUuidToIndexMap,
receivedValuesStore,
- gridModel.mainRefSystem
+ gridModel.mainRefSystem,
)
operatingPoint.length shouldBe 10 withClue "safety check: 13 nodes minus 3 closed switches"
@@ -106,7 +113,7 @@ class PowerFlowSupportSpec
gridModel,
3,
operatingPoint,
- slackNodeVoltages
+ slackNodeVoltages,
)(Vector(1e-12)) match {
case successResult: ValidNewtonRaphsonPFResult => successResult
case failure => fail(s"Newton-Raphson failed: $failure")
@@ -115,11 +122,14 @@ class PowerFlowSupportSpec
val sweepValueStore = SweepValueStore(
result,
gridModel.gridComponents.nodes,
- gridModel.nodeUuidToIndexMap
+ gridModel.nodeUuidToIndexMap,
)
val pfResult =
- createResultModels(gridModel, sweepValueStore)(ZonedDateTime.now())
+ createResultModels(gridModel, sweepValueStore)(
+ ZonedDateTime.now(),
+ log,
+ )
// left/top side segments should have similar currents
val loadLinesLeft =
@@ -127,26 +137,26 @@ class PowerFlowSupportSpec
line18To1,
line0To17,
line0To15,
- line16To3
+ line16To3,
).map(_.uuid).toSet
pfResult.lineResults
.filter(lineRes => loadLinesLeft.contains(lineRes.getInputModel))
.foreach { lineRes =>
lineRes.getiAMag() should equalWithTolerance(
30.4954d.asAmpere,
- currentTolerance
+ currentTolerance,
)
lineRes.getiBMag() should equalWithTolerance(
30.4954d.asAmpere,
- currentTolerance
+ currentTolerance,
)
normalizeAngle(lineRes.getiAAng()) should equalWithTolerance(
179.7095d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
normalizeAngle(lineRes.getiBAng()) should equalWithTolerance(
179.7095d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
}
@@ -155,26 +165,26 @@ class PowerFlowSupportSpec
Iterable(
line1To13,
line14To2,
- line2To3
+ line2To3,
).map(_.uuid).toSet
pfResult.lineResults
.filter(lineRes => loadLinesRight.contains(lineRes.getInputModel))
.foreach { lineRes =>
lineRes.getiAMag() should equalWithTolerance(
27.723d.asAmpere,
- currentTolerance
+ currentTolerance,
)
lineRes.getiBMag() should equalWithTolerance(
27.723d.asAmpere,
- currentTolerance
+ currentTolerance,
)
normalizeAngle(lineRes.getiAAng()) should equalWithTolerance(
179.7095d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
normalizeAngle(lineRes.getiBAng()) should equalWithTolerance(
179.7095d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
}
@@ -198,7 +208,7 @@ class PowerFlowSupportSpec
gridModel.gridComponents.transformers3w,
gridModel.nodeUuidToIndexMap,
receivedValuesStore,
- gridModel.mainRefSystem
+ gridModel.mainRefSystem,
)
operatingPoint.length shouldBe 11 withClue "safety check: 13 nodes minus 2 closed switches"
@@ -207,7 +217,7 @@ class PowerFlowSupportSpec
gridModel,
50,
operatingPoint,
- slackNodeVoltages
+ slackNodeVoltages,
)(Vector(1e-12)) match {
case successResult: ValidNewtonRaphsonPFResult => successResult
case failure => fail(s"Newton-Raphson failed: $failure")
@@ -218,9 +228,9 @@ class PowerFlowSupportSpec
SweepValueStore(
result,
gridModel.gridComponents.nodes,
- gridModel.nodeUuidToIndexMap
- )
- )(ZonedDateTime.now())
+ gridModel.nodeUuidToIndexMap,
+ ),
+ )(ZonedDateTime.now(), log)
// left/top side segments (lines that are adjacent to the open switch) should have no load
val loadLinesLeft =
@@ -230,11 +240,11 @@ class PowerFlowSupportSpec
.foreach { lineRes =>
lineRes.getiAMag() should equalWithTolerance(
0.0001d.asAmpere,
- currentTolerance
+ currentTolerance,
)
lineRes.getiBMag() should equalWithTolerance(
0.0001d.asAmpere,
- currentTolerance
+ currentTolerance,
)
// angles are not reliable enough with such small magnitudes
}
@@ -247,19 +257,19 @@ class PowerFlowSupportSpec
.foreach { lineRes =>
lineRes.getiAMag() should equalWithTolerance(
58.6017d.asAmpere,
- currentTolerance
+ currentTolerance,
)
lineRes.getiBMag() should equalWithTolerance(
58.6017d.asAmpere,
- currentTolerance
+ currentTolerance,
)
normalizeAngle(lineRes.getiAAng()) should equalWithTolerance(
179.4090d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
normalizeAngle(lineRes.getiBAng()) should equalWithTolerance(
179.4090d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
}
@@ -283,7 +293,7 @@ class PowerFlowSupportSpec
gridModel.gridComponents.transformers3w,
gridModel.nodeUuidToIndexMap,
receivedValuesStore,
- gridModel.mainRefSystem
+ gridModel.mainRefSystem,
)
operatingPoint.length shouldBe 11 withClue "safety check: 13 nodes minus 2 closed switches"
@@ -292,7 +302,7 @@ class PowerFlowSupportSpec
gridModel,
50,
operatingPoint,
- slackNodeVoltages
+ slackNodeVoltages,
)(Vector(1e-12)) match {
case successResult: ValidNewtonRaphsonPFResult => successResult
case failure => fail(s"Newton-Raphson failed: $failure")
@@ -303,9 +313,9 @@ class PowerFlowSupportSpec
SweepValueStore(
result,
gridModel.gridComponents.nodes,
- gridModel.nodeUuidToIndexMap
- )
- )(ZonedDateTime.now())
+ gridModel.nodeUuidToIndexMap,
+ ),
+ )(ZonedDateTime.now(), log)
// left/top side segments (lines that are adjacent to the open switch) should have load
val expectedLoadLines =
@@ -315,19 +325,19 @@ class PowerFlowSupportSpec
.foreach { lineRes =>
lineRes.getiAMag() should equalWithTolerance(
58.5343d.asAmpere,
- currentTolerance
+ currentTolerance,
)
lineRes.getiBMag() should equalWithTolerance(
58.5343d.asAmpere,
- currentTolerance
+ currentTolerance,
)
normalizeAngle(lineRes.getiAAng()) should equalWithTolerance(
179.461d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
normalizeAngle(lineRes.getiBAng()) should equalWithTolerance(
179.461d.asDegreeGeom,
- angleTolerance
+ angleTolerance,
)
}
@@ -341,11 +351,11 @@ class PowerFlowSupportSpec
.foreach { lineRes =>
lineRes.getiAMag() should equalWithTolerance(
0.0001d.asAmpere,
- currentTolerance
+ currentTolerance,
)
lineRes.getiBMag() should equalWithTolerance(
0.0001d.asAmpere,
- currentTolerance
+ currentTolerance,
)
// angles are not reliable enough with such small magnitudes
}
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 e4d9fedf2c..378c08c1ef 100644
--- a/src/test/scala/edu/ie3/simona/agent/grid/ReceivedValuesStoreSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/grid/ReceivedValuesStoreSpec.scala
@@ -6,53 +6,52 @@
package edu.ie3.simona.agent.grid
-import java.util.UUID
-
-import org.apache.pekko.actor.{ActorRef, ActorSystem}
-import org.apache.pekko.testkit.{TestKit, TestProbe}
-import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.graph.SubGridGate
-import edu.ie3.simona.test.common.{TestKitWithShutdown, UnitSpec}
+import edu.ie3.simona.agent.participant.ParticipantAgent.ParticipantMessage
+import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.test.common.model.grid.SubGridGateMokka
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.ActorRef
+
+import java.util.UUID
class ReceivedValuesStoreSpec
- extends TestKitWithShutdown(
- ActorSystem(
- "ReceivedValuesStoreSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- )
+ extends ScalaTestWithActorTestKit
with UnitSpec
with SubGridGateMokka {
// test actorRefs
- val actorProbe1: TestProbe = TestProbe()
- val actorProbe2: TestProbe = TestProbe()
- val actorProbe3: TestProbe = TestProbe()
+ val participant1: TestProbe[ParticipantMessage] =
+ TestProbe[ParticipantMessage]()
+ val participant2: TestProbe[ParticipantMessage] =
+ TestProbe[ParticipantMessage]()
+ val participant3: TestProbe[ParticipantMessage] =
+ TestProbe[ParticipantMessage]()
+ val gridAgent: TestProbe[GridAgentMessage] = TestProbe[GridAgentMessage]()
// test data used by almost all tests
// / node to asset agents mapping
- val nodeToAssetAgentsMap: Map[UUID, Set[ActorRef]] = Map(
+ val nodeToAssetAgentsMap: Map[UUID, Set[ActorRef[ParticipantMessage]]] = Map(
UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546") -> Set(
- actorProbe1.ref
+ participant1.ref
),
UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97") -> Set(
- actorProbe2.ref
- )
+ participant2.ref
+ ),
)
// / subnet gate mapping for inferior grids
- val inferiorSubGridGateToActorRefMap: Map[SubGridGate, ActorRef] = Map(
+ val inferiorSubGridGateToActorRefMap
+ : Map[SubGridGate, ActorRef[GridAgentMessage]] = Map(
build2wSubGridGate(
UUID.fromString("5cd55ab5-a7d2-499f-a25f-6dbc3845c5e8"),
1,
UUID.fromString("1676360a-c7c4-43a9-a667-90ddfe8a18e6"),
- 2
- ) -> actorProbe3.ref
+ 2,
+ ) -> gridAgent.ref
)
// / superior grid nodeUuid vector
@@ -64,15 +63,17 @@ class ReceivedValuesStoreSpec
"initialize an empty store correctly when everything is empty" in {
- val nodeToAssetAgentsMap = Map.empty[UUID, Set[ActorRef]]
- val inferiorSubGridGateToActorRefMap = Map.empty[SubGridGate, ActorRef]
+ val nodeToAssetAgentsMap =
+ Map.empty[UUID, Set[ActorRef[ParticipantMessage]]]
+ val inferiorSubGridGateToActorRefMap =
+ Map.empty[SubGridGate, ActorRef[GridAgentMessage]]
val superiorGridNodeUuids = Vector.empty[UUID]
val receivedValuesStore =
ReceivedValuesStore.empty(
nodeToAssetAgentsMap,
inferiorSubGridGateToActorRefMap,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
receivedValuesStore.nodeToReceivedSlackVoltage.size shouldBe 0
@@ -86,19 +87,19 @@ class ReceivedValuesStoreSpec
ReceivedValuesStore.empty(
nodeToAssetAgentsMap,
inferiorSubGridGateToActorRefMap,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
receivedValuesStore.nodeToReceivedPower.size shouldBe 3
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546")
- ) shouldBe Map(actorProbe1.ref -> None)
+ ) shouldBe Map(participant1.ref -> None)
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97")
- ) shouldBe Map(actorProbe2.ref -> None)
+ ) shouldBe Map(participant2.ref -> None)
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("5cd55ab5-a7d2-499f-a25f-6dbc3845c5e8")
- ) shouldBe Map(actorProbe3.ref -> None)
+ ) shouldBe Map(gridAgent.ref -> None)
receivedValuesStore.nodeToReceivedSlackVoltage.size shouldBe 1
receivedValuesStore.nodeToReceivedSlackVoltage(
@@ -112,22 +113,23 @@ class ReceivedValuesStoreSpec
val nodeToAssetAgentsMap =
Map(
UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546") -> Set(
- actorProbe1.ref
+ participant1.ref
),
UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97") -> Set(
- actorProbe2.ref,
- actorProbe3.ref
- )
+ participant2.ref,
+ participant3.ref,
+ ),
)
- val inferiorSubGridGateToActorRefMap = Map.empty[SubGridGate, ActorRef]
+ val inferiorSubGridGateToActorRefMap =
+ Map.empty[SubGridGate, ActorRef[GridAgentMessage]]
val superiorGridNodeUuids = Vector.empty[UUID]
val receivedValuesStore =
ReceivedValuesStore.empty(
nodeToAssetAgentsMap,
inferiorSubGridGateToActorRefMap,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
receivedValuesStore.nodeToReceivedSlackVoltage.size shouldBe 0
@@ -135,12 +137,12 @@ class ReceivedValuesStoreSpec
receivedValuesStore.nodeToReceivedPower.size shouldBe 2
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546")
- ) shouldBe Map(actorProbe1.ref -> None)
+ ) shouldBe Map(participant1.ref -> None)
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97")
) shouldBe Map(
- actorProbe2.ref -> None,
- actorProbe3.ref -> None
+ participant2.ref -> None,
+ participant3.ref -> None,
)
}
@@ -153,7 +155,7 @@ class ReceivedValuesStoreSpec
ReceivedValuesStore.empty(
nodeToAssetAgentsMap,
inferiorSubGridGateToActorRefMap,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
receivedValuesStore.nodeToReceivedSlackVoltage.size shouldBe 0
@@ -161,31 +163,33 @@ class ReceivedValuesStoreSpec
receivedValuesStore.nodeToReceivedPower.size shouldBe 3
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546")
- ) shouldBe Map(actorProbe1.ref -> None)
+ ) shouldBe Map(participant1.ref -> None)
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97")
- ) shouldBe Map(actorProbe2.ref -> None)
+ ) shouldBe Map(participant2.ref -> None)
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("5cd55ab5-a7d2-499f-a25f-6dbc3845c5e8")
- ) shouldBe Map(actorProbe3.ref -> None)
+ ) shouldBe Map(gridAgent.ref -> None)
}
"initialize an empty store correctly when only information on the superior grid slack nodes are provided" in {
- val nodeToAssetAgentsMap = Map.empty[UUID, Set[ActorRef]]
- val inferiorSubGridGateToActorRefMap = Map.empty[SubGridGate, ActorRef]
+ val nodeToAssetAgentsMap =
+ Map.empty[UUID, Set[ActorRef[ParticipantMessage]]]
+ val inferiorSubGridGateToActorRefMap =
+ Map.empty[SubGridGate, ActorRef[GridAgentMessage]]
val superiorGridNodeUuids = Vector(
UUID.fromString("baded8c4-b703-4316-b62f-75ffe09c9843"),
- UUID.fromString("d5040bf7-56c1-4d6a-908a-47c05b0c5c54")
+ UUID.fromString("d5040bf7-56c1-4d6a-908a-47c05b0c5c54"),
)
val receivedValuesStore =
ReceivedValuesStore.empty(
nodeToAssetAgentsMap,
inferiorSubGridGateToActorRefMap,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
receivedValuesStore.nodeToReceivedPower.size shouldBe 0
@@ -202,14 +206,15 @@ class ReceivedValuesStoreSpec
"initialize an empty store correctly when only an invalid mapping for asset agents with duplicates is provided" in {
- val inferiorSubGridGateToActorRefMap = Map.empty[SubGridGate, ActorRef]
+ val inferiorSubGridGateToActorRefMap =
+ Map.empty[SubGridGate, ActorRef[GridAgentMessage]]
val superiorGridNodeUuids = Vector.empty[UUID]
val receivedValuesStore =
ReceivedValuesStore.empty(
nodeToAssetAgentsMap,
inferiorSubGridGateToActorRefMap,
- superiorGridNodeUuids
+ superiorGridNodeUuids,
)
receivedValuesStore.nodeToReceivedSlackVoltage.size shouldBe 0
@@ -217,10 +222,10 @@ class ReceivedValuesStoreSpec
receivedValuesStore.nodeToReceivedPower.size shouldBe 2
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("dd9a5b54-94bb-4201-9108-2b1b7d689546")
- ) shouldBe Map(actorProbe1.ref -> None)
+ ) shouldBe Map(participant1.ref -> None)
receivedValuesStore.nodeToReceivedPower(
UUID.fromString("34e807f1-c62b-4968-b0f6-980ce500ff97")
- ) shouldBe Map(actorProbe2.ref -> None)
+ ) shouldBe Map(participant2.ref -> None)
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala
index 5f53c56304..73239981d3 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/EvcsAgentModelCalculationSpec.scala
@@ -9,6 +9,7 @@ package edu.ie3.simona.agent.participant
import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.models.input.system.EvcsInput
import edu.ie3.datamodel.models.input.system.characteristic.QV
+import edu.ie3.datamodel.models.result.system.{EvResult, EvcsResult}
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService.ActorEvMovementsService
@@ -19,39 +20,53 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData._
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
import edu.ie3.simona.config.SimonaConfig.EvcsRuntimeConfig
+import edu.ie3.simona.event.ResultEvent.{
+ FlexOptionsResultEvent,
+ ParticipantResultEvent,
+}
import edu.ie3.simona.event.notifier.NotifierConfig
-import edu.ie3.simona.model.participant.EvcsModel.EvcsRelevantData
+import edu.ie3.simona.model.participant.evcs.EvModelWrapper
+import edu.ie3.simona.model.participant.evcs.EvcsModel.{
+ EvcsState,
+ ScheduleEntry,
+}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage._
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.simona.ontology.messages.services.EvMessage._
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
-import edu.ie3.simona.service.ev.ExtEvDataService.FALLBACK_EV_MOVEMENTS_STEM_DISTANCE
import edu.ie3.simona.test.ParticipantAgentSpec
import edu.ie3.simona.test.common.input.EvcsInputTestData
import edu.ie3.simona.test.common.{EvTestData, TestSpawnerClassic}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
+import edu.ie3.simona.util.TickUtil.TickLong
+import edu.ie3.util.TimeUtil
+import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import edu.ie3.util.scala.quantities.{Megavars, ReactivePower, Vars}
import org.apache.pekko.actor.ActorSystem
import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
import org.apache.pekko.testkit.{TestFSMRef, TestProbe}
-import squants.energy.{Megawatts, Watts}
-import squants.{Each, Power}
+import squants.energy._
+import squants.{Each, Energy, Power}
+import java.time.ZonedDateTime
import java.util.UUID
+import scala.collection.immutable.{SortedMap, SortedSet}
class EvcsAgentModelCalculationSpec
extends ParticipantAgentSpec(
@@ -61,49 +76,63 @@ class EvcsAgentModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with EvcsInputTestData
with EvTestData
with TestSpawnerClassic {
- private implicit val powerTolerance: Power = Watts(0.1)
- private implicit val reactivePowerTolerance: ReactivePower = Vars(0.1)
+ private val requestVoltageDeviationThreshold = 1e-14d
+
+ private val defaultOutputConfig =
+ NotifierConfig(
+ simulationResultInfo = false,
+ powerRequestReply = false,
+ flexResult = false,
+ )
+
+ private val modelConfig =
+ EvcsRuntimeConfig.apply(
+ calculateMissingReactivePowerWithModel = false,
+ scaling = 1.0,
+ uuids = List("default"),
+ chargingStrategy = "maxPower",
+ lowestEvSoc = 0.2,
+ )
+
+ protected implicit val simulationStartDate: ZonedDateTime =
+ TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00")
+ protected val simulationEndDate: ZonedDateTime =
+ TimeUtil.withDefaults.toZonedDateTime("2020-01-01 02:00:00")
/* Alter the input model to have a voltage sensitive reactive power calculation */
- private val voltageSensitiveInput = evcsInputModel
+ private val evcsInputModelQv = evcsInputModel
.copy()
.qCharacteristics(new QV("qV:{(0.95,-0.625),(1.05,0.625)}"))
.build()
- private val evService = TestProbe("evService")
+ private val resolution = 3600L
- private val noServices = None
- private val withServices = Some(
- Vector(
- ActorEvMovementsService(evService.ref)
- )
- )
-
- private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
+ private implicit val powerTolerance: Power = Watts(0.1)
+ private implicit val energyTolerance: Energy = WattHours(0.1)
+ private implicit val reactivePowerTolerance: ReactivePower = Vars(0.1)
"An evcs agent with model calculation depending on no secondary data service" should {
val initStateData = ParticipantInitializeStateData[
EvcsInput,
EvcsRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = evcsInputModel,
modelConfig = modelConfig,
- secondaryDataServices = noServices,
+ secondaryDataServices = None,
simulationStartDate = simulationStartDate,
simulationEndDate = simulationEndDate,
resolution = resolution,
- requestVoltageDeviationThreshold =
- simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
+ requestVoltageDeviationThreshold = requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -111,7 +140,7 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
evcsAgent.stateName shouldBe Uninitialized
@@ -132,7 +161,7 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
@@ -142,28 +171,34 @@ class EvcsAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
deathProbe.expectTerminated(evcsAgent.ref)
}
}
"An evcs agent with model calculation depending on one secondary data service" should {
+ val evService = TestProbe("evService")
+
val initStateData = ParticipantInitializeStateData[
EvcsInput,
EvcsRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = evcsInputModel,
modelConfig = modelConfig,
- secondaryDataServices = withServices,
+ secondaryDataServices = Iterable(
+ ActorEvMovementsService(evService.ref)
+ ),
simulationStartDate = simulationStartDate,
simulationEndDate = simulationEndDate,
resolution = resolution,
- requestVoltageDeviationThreshold =
- simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
+ requestVoltageDeviationThreshold = requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -171,7 +206,7 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
@@ -191,7 +226,7 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
@@ -212,27 +247,32 @@ class EvcsAgentModelCalculationSpec
simulationEndDate,
timeBin,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
) =>
inputModel shouldBe SimpleInputContainer(evcsInputModel)
modelConfig shouldBe modelConfig
- secondaryDataServices shouldBe withServices
- simulationStartDate shouldBe this.simulationStartDate
- simulationEndDate shouldBe this.simulationEndDate
- timeBin shouldBe this.resolution
- requestVoltageDeviationThreshold shouldBe simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold
+ secondaryDataServices shouldBe Iterable(
+ ActorEvMovementsService(evService.ref)
+ )
+ simulationStartDate shouldBe simulationStartDate
+ simulationEndDate shouldBe simulationEndDate
+ timeBin shouldBe resolution
+ requestVoltageDeviationThreshold shouldBe requestVoltageDeviationThreshold
outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe None
case unsuitableStateData =>
fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
}
/* Refuse registration */
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
- evService.expectMsg(
- RegisterForEvDataMessage(evcsInputModel.getUuid)
- )
+ evService.expectMsg(RegisterForEvDataMessage(evcsInputModel.getUuid))
/* ... as well as corresponding state and state data */
evcsAgent.stateName shouldBe HandleInformation
@@ -250,34 +290,35 @@ class EvcsAgentModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
),
awaitRegistrationResponsesFrom,
- foreseenNextDataTicks
+ foreseenNextDataTicks,
) =>
/* Base state data */
startDate shouldBe simulationStartDate
endDate shouldBe simulationEndDate
- services shouldBe Some(
- Vector(
- ActorEvMovementsService(evService.ref)
- )
+ services shouldBe Iterable(
+ ActorEvMovementsService(evService.ref)
)
outputConfig shouldBe NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
additionalActivationTicks shouldBe empty
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
- resolution * 10,
- Map(0L -> Each(1.0))
+ resolution,
+ SortedMap(0L -> Each(1.0)),
)
- resultValueStore shouldBe ValueStore.forResult(resolution, 10)
- requestValueStore shouldBe ValueStore[ApparentPower](resolution * 10)
+ resultValueStore shouldBe ValueStore(resolution)
+ requestValueStore shouldBe ValueStore[ApparentPower](resolution)
/* Additional information */
- awaitRegistrationResponsesFrom shouldBe Vector(evService.ref)
+ awaitRegistrationResponsesFrom shouldBe Iterable(evService.ref)
foreseenNextDataTicks shouldBe Map.empty
case _ =>
fail(
@@ -286,7 +327,10 @@ class EvcsAgentModelCalculationSpec
}
/* Reply, that registration was successful */
- evService.send(evcsAgent, RegistrationSuccessfulMessage(None))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
/* Expect a completion message */
scheduler.expectMsg(Completion(evcsAgent.toTyped, None))
@@ -294,7 +338,7 @@ class EvcsAgentModelCalculationSpec
/* ... as well as corresponding state and state data */
evcsAgent.stateName shouldBe Idle
evcsAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* Only check the awaited next data ticks, as the rest has yet been checked */
baseStateData.foreseenDataTicks shouldBe Map(evService.ref -> None)
case _ =>
@@ -309,7 +353,7 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
@@ -317,13 +361,17 @@ class EvcsAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
- evService.expectMsg(
- RegisterForEvDataMessage(evcsInputModel.getUuid)
+ evService.expectMsg(RegisterForEvDataMessage(evcsInputModel.getUuid))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, Some(900L)),
)
- evService.send(evcsAgent, RegistrationSuccessfulMessage(Some(900L)))
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -334,27 +382,27 @@ class EvcsAgentModelCalculationSpec
evcsAgent ! RequestAssetPowerMessage(
0L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
inside(evcsAgent.stateData) {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.requestValueStore shouldBe ValueStore[
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.requestValueStore shouldBe ValueStore[
ApparentPower
](
- resolution * 10,
- Map(
+ resolution,
+ SortedMap(
0L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
- )
+ ),
)
case _ =>
fail(
@@ -370,22 +418,28 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
scheduler.send(
evcsAgent,
- Activation(INIT_SIM_TICK)
+ Activation(INIT_SIM_TICK),
)
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
evService.expectMsgType[RegisterForEvDataMessage]
- evService.send(evcsAgent, RegistrationSuccessfulMessage(None))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -394,12 +448,17 @@ class EvcsAgentModelCalculationSpec
/* Send out new data */
val arrivingEvsData =
- ArrivingEvsData(scala.collection.immutable.Seq(evA, evB))
+ ArrivingEvsData(Seq(EvModelWrapper(evA), EvModelWrapper(evB)))
val key1 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID()))
evService.send(
evcsAgent,
- ProvideEvDataMessage(0L, arrivingEvsData, unlockKey = key1)
+ ProvideEvDataMessage(
+ 0L,
+ evService.ref,
+ arrivingEvsData,
+ unlockKey = key1,
+ ),
)
scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1))
@@ -407,9 +466,9 @@ class EvcsAgentModelCalculationSpec
evcsAgent.stateName shouldBe HandleInformation
evcsAgent.stateData match {
case DataCollectionStateData(
- baseStateData: ParticipantModelBaseStateData[_, _, _],
+ baseStateData: ParticipantModelBaseStateData[_, _, _, _],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(evService.ref -> None)
@@ -430,7 +489,7 @@ class EvcsAgentModelCalculationSpec
/* Trigger the agent */
scheduler.send(
evcsAgent,
- Activation(0)
+ Activation(0),
)
/* The agent will notice, that all expected information are apparent, switch to Calculate and trigger itself
@@ -440,30 +499,46 @@ class EvcsAgentModelCalculationSpec
)
evcsAgent.stateName shouldBe Idle
evcsAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
+ baseStateData.stateDataStore match {
case ValueStore(_, store) =>
- store shouldBe Map(
- 0L -> EvcsRelevantData(
- FALLBACK_EV_MOVEMENTS_STEM_DISTANCE,
- Set(evA, evB)
- )
- )
+ store.keys should contain only 0L
+ store.get(0L) match {
+ case Some(EvcsState(currentEvs, schedule, tick)) =>
+ currentEvs should contain theSameElementsAs Set(
+ EvModelWrapper(evA),
+ EvModelWrapper(evB),
+ )
+
+ schedule shouldBe Map(
+ evA.getUuid -> SortedSet(
+ ScheduleEntry(
+ 0,
+ 200,
+ Kilowatts(11.0),
+ )
+ ),
+ evB.getUuid -> SortedSet(
+ ScheduleEntry(
+ 0,
+ 200,
+ Kilowatts(11.0),
+ )
+ ),
+ )
+
+ tick shouldBe 0L
+ case None => fail("Entry for tick 0 expected.")
+ }
}
/* The store for simulation results has been extended */
baseStateData.resultValueStore match {
case ValueStore(_, store) =>
- store.size shouldBe 1
- store.getOrElse(
- 0L,
- fail("Expected a simulation result for tick 900.")
- ) match {
- case ApparentPower(p, q) =>
- (p ~= Megawatts(0d)) shouldBe true
- (q ~= Megavars(0d)) shouldBe true
- }
+ // Since departures and arrivals are considered separately,
+ // EvcsAgent calculates results only with the next activation
+ store.keys shouldBe empty
}
case _ =>
fail(
@@ -479,22 +554,28 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
scheduler.send(
evcsAgent,
- Activation(INIT_SIM_TICK)
+ Activation(INIT_SIM_TICK),
)
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
evService.expectMsgType[RegisterForEvDataMessage]
- evService.send(evcsAgent, RegistrationSuccessfulMessage(None))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -504,16 +585,16 @@ class EvcsAgentModelCalculationSpec
/* Send out activation */
scheduler.send(
evcsAgent,
- Activation(0)
+ Activation(0),
)
/* Find yourself in corresponding state and state data */
evcsAgent.stateName shouldBe HandleInformation
evcsAgent.stateData match {
case DataCollectionStateData(
- baseStateData: ParticipantModelBaseStateData[_, _, _],
+ baseStateData: ParticipantModelBaseStateData[_, _, _, _],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(evService.ref -> None)
@@ -531,12 +612,17 @@ class EvcsAgentModelCalculationSpec
/* Send out new data */
val arrivingEvsData =
- ArrivingEvsData(Seq(evA, evB))
+ ArrivingEvsData(Seq(EvModelWrapper(evA), EvModelWrapper(evB)))
val key1 = Some(ScheduleKey(lock.ref.toTyped, UUID.randomUUID()))
evService.send(
evcsAgent,
- ProvideEvDataMessage(0L, arrivingEvsData, unlockKey = key1)
+ ProvideEvDataMessage(
+ 0L,
+ evService.ref,
+ arrivingEvsData,
+ unlockKey = key1,
+ ),
)
scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1))
@@ -547,30 +633,47 @@ class EvcsAgentModelCalculationSpec
)
evcsAgent.stateName shouldBe Idle
evcsAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
+ baseStateData.stateDataStore match {
case ValueStore(_, store) =>
- store shouldBe Map(
- 0L -> EvcsRelevantData(
- FALLBACK_EV_MOVEMENTS_STEM_DISTANCE,
- Set(evA, evB)
- )
- )
+ store.keys should contain only 0L
+ store.get(0L) match {
+ case Some(EvcsState(currentEvs, schedule, tick)) =>
+ currentEvs should contain theSameElementsAs Set(
+ EvModelWrapper(evA),
+ EvModelWrapper(evB),
+ )
+ schedule shouldBe Map(
+ evA.getUuid ->
+ SortedSet(
+ ScheduleEntry(
+ 0,
+ 200,
+ Kilowatts(11.0),
+ )
+ ),
+ evB.getUuid ->
+ SortedSet(
+ ScheduleEntry(
+ 0,
+ 200,
+ Kilowatts(11.0),
+ )
+ ),
+ )
+
+ tick shouldBe 0L
+ case None => fail("Entry for tick 0 expected.")
+ }
}
/* The store for simulation results has been extended */
baseStateData.resultValueStore match {
case ValueStore(_, store) =>
- store.size shouldBe 1
- store.getOrElse(
- 0L,
- fail("Expected a simulation result for tick 900.")
- ) match {
- case ApparentPower(p, q) =>
- (p ~= Megawatts(0d)) shouldBe true
- (q ~= Megavars(0d)) shouldBe true
- }
+ // Since departures and arrivals are considered separately,
+ // EvcsAgent calculates results only with the next activation
+ store shouldBe empty
}
case _ =>
fail(
@@ -584,22 +687,28 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
scheduler.send(
evcsAgent,
- Activation(INIT_SIM_TICK)
+ Activation(INIT_SIM_TICK),
)
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
evService.expectMsgType[RegisterForEvDataMessage]
- evService.send(evcsAgent, RegistrationSuccessfulMessage(None))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -608,13 +717,13 @@ class EvcsAgentModelCalculationSpec
evcsAgent ! RequestAssetPowerMessage(
7200L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
@@ -625,22 +734,28 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = Iterable.empty,
)
)
scheduler.send(
evcsAgent,
- Activation(INIT_SIM_TICK)
+ Activation(INIT_SIM_TICK),
)
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
evService.expectMsgType[RegisterForEvDataMessage]
- evService.send(evcsAgent, RegistrationSuccessfulMessage(None))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -649,13 +764,13 @@ class EvcsAgentModelCalculationSpec
/* Send out public evcs request */
evService.send(
evcsAgent,
- EvFreeLotsRequest(0L)
+ EvFreeLotsRequest(0L),
)
evService.expectMsg(
FreeLotsResponse(
evcsInputModel.getUuid,
- 2
+ 2,
)
)
@@ -667,29 +782,30 @@ class EvcsAgentModelCalculationSpec
evcsAgent,
ProvideEvDataMessage(
0L,
- ArrivingEvsData(Seq(evA)),
- unlockKey = key1
- )
+ evService.ref,
+ ArrivingEvsData(Seq(EvModelWrapper(evA))),
+ unlockKey = key1,
+ ),
)
scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1))
scheduler.send(
evcsAgent,
- Activation(0)
+ Activation(0),
)
scheduler.expectMsg(Completion(evcsAgent.toTyped))
/* Ask for public evcs lot count again with a later tick */
evService.send(
evcsAgent,
- EvFreeLotsRequest(3600L)
+ EvFreeLotsRequest(3600L),
)
// this time, only one is still free
evService.expectMsg(
FreeLotsResponse(
evcsInputModel.getUuid,
- 1
+ 1,
)
)
@@ -700,18 +816,19 @@ class EvcsAgentModelCalculationSpec
new EvcsAgent(
scheduler = scheduler.ref,
initStateData = ParticipantInitializeStateData(
- inputModel = voltageSensitiveInput,
+ inputModel = evcsInputModelQv,
modelConfig = modelConfig,
- secondaryDataServices = withServices,
+ secondaryDataServices = Iterable(
+ ActorEvMovementsService(evService.ref)
+ ),
simulationStartDate = simulationStartDate,
simulationEndDate = simulationEndDate,
- resolution = simonaConfig.simona.powerflow.resolution.getSeconds,
- requestVoltageDeviationThreshold =
- simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
+ resolution = resolution,
+ requestVoltageDeviationThreshold = requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
),
- listener = systemListener
+ listener = systemListener,
)
)
@@ -722,11 +839,17 @@ class EvcsAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(evcsAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
evService.expectMsgType[RegisterForEvDataMessage]
- evService.send(evcsAgent, RegistrationSuccessfulMessage(None))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -739,9 +862,10 @@ class EvcsAgentModelCalculationSpec
evcsAgent,
ProvideEvDataMessage(
0L,
- ArrivingEvsData(Seq(evA)),
- unlockKey = key1
- )
+ evService.ref,
+ ArrivingEvsData(Seq(EvModelWrapper(evA.copyWithDeparture(3600L)))),
+ unlockKey = key1,
+ ),
)
scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 0, key1))
scheduler.send(evcsAgent, Activation(0))
@@ -752,17 +876,18 @@ class EvcsAgentModelCalculationSpec
// departures first
evService.send(
evcsAgent,
- DepartingEvsRequest(3600L, Seq(evA.getUuid))
+ DepartingEvsRequest(3600L, Seq(evA.getUuid)),
)
evService.expectMsgType[DepartingEvsResponse] match {
- case DepartingEvsResponse(evcs, evs) =>
- evcs shouldBe evcsInputModel.getUuid
-
- evs should have size 1
-
- val ev = evs.headOption.getOrElse(fail("No EV departed"))
- ev.getUuid shouldBe evA.getUuid
- ev.getStoredEnergy should equalWithTolerance(11d.asKiloWattHour)
+ case DepartingEvsResponse(evcs, evModels) =>
+ evcs shouldBe evcsInputModelQv.getUuid
+ evModels should have size 1
+ evModels.headOption match {
+ case Some(evModel) =>
+ evModel.uuid shouldBe evA.getUuid
+ evModel.storedEnergy should approximate(KilowattHours(11.0))
+ case None => fail("Expected to get at least one ev.")
+ }
}
// arrivals second
@@ -771,9 +896,10 @@ class EvcsAgentModelCalculationSpec
evcsAgent,
ProvideEvDataMessage(
3600L,
- ArrivingEvsData(Seq(evB)),
- unlockKey = key2
- )
+ evService.ref,
+ ArrivingEvsData(Seq(EvModelWrapper(evB.copyWithDeparture(7200L)))),
+ unlockKey = key2,
+ ),
)
scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 3600, key2))
@@ -785,18 +911,16 @@ class EvcsAgentModelCalculationSpec
// departures first
evService.send(
evcsAgent,
- DepartingEvsRequest(7200L, Seq(evB.getUuid))
+ DepartingEvsRequest(7200L, Seq(evB.getUuid)),
)
evService.expectMsgType[DepartingEvsResponse] match {
case DepartingEvsResponse(evcs, evModels) =>
- evcs shouldBe evcsInputModel.getUuid
+ evcs shouldBe evcsInputModelQv.getUuid
evModels should have size 1
evModels.headOption match {
case Some(evModel) =>
- evModel.getUuid shouldBe evB.getUuid
- evModel.getStoredEnergy should equalWithTolerance(
- 11d.asKiloWattHour
- )
+ evModel.uuid shouldBe evB.getUuid
+ evModel.storedEnergy should approximate(KilowattHours(11.0))
case None => fail("Expected to get at least one ev.")
}
}
@@ -806,9 +930,10 @@ class EvcsAgentModelCalculationSpec
evcsAgent,
ProvideEvDataMessage(
7200L,
- ArrivingEvsData(Seq(evA)),
- unlockKey = key3
- )
+ evService.ref,
+ ArrivingEvsData(Seq(EvModelWrapper(evA.copyWithDeparture(10800L)))),
+ unlockKey = key3,
+ ),
)
scheduler.expectMsg(ScheduleActivation(evcsAgent.toTyped, 7200, key3))
@@ -820,13 +945,13 @@ class EvcsAgentModelCalculationSpec
evcsAgent ! RequestAssetPowerMessage(
7500L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.00572)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.011))
+ q should approximate(Megavars(0.0))
case answer => fail(s"Did not expect to get that answer: $answer")
}
}
@@ -837,14 +962,14 @@ class EvcsAgentModelCalculationSpec
evcsAgent ! RequestAssetPowerMessage(
7500L,
Each(1.000000000000001d),
- Each(0.0)
+ Each(0.0),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(0.00572)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.011))
+ q should approximate(Megavars(0.0))
}
}
@@ -853,15 +978,980 @@ class EvcsAgentModelCalculationSpec
evcsAgent ! RequestAssetPowerMessage(
7500L,
Each(0.98),
- Each(0.0)
+ Each(0.0),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgClass(classOf[AssetPowerChangedMessage]) match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.00572)) shouldBe true
- (q ~= Megavars(-0.00335669)) shouldBe true
+ p should approximate(Megawatts(0.011))
+ q should approximate(Megavars(-0.0067133728))
+ }
+ }
+ }
+
+ "An evcs agent with model calculation controlled by an EmAgent" should {
+
+ "be initialized correctly" in {
+ val evService = TestProbe("evService")
+ val emAgent = TestProbe("EmAgentProbe")
+
+ val evcsAgent = TestFSMRef(
+ new EvcsAgent(
+ scheduler = scheduler.ref,
+ initStateData = ParticipantInitializeStateData(
+ inputModel = evcsInputModelQv,
+ modelConfig = modelConfig,
+ secondaryDataServices = Iterable(
+ ActorEvMovementsService(evService.ref)
+ ),
+ simulationStartDate = simulationStartDate,
+ simulationEndDate = simulationEndDate,
+ resolution = resolution,
+ requestVoltageDeviationThreshold = requestVoltageDeviationThreshold,
+ outputConfig = defaultOutputConfig,
+ primaryServiceProxy = primaryServiceProxy.ref,
+ maybeEmAgent = Some(emAgent.ref.toTyped),
+ ),
+ listener = Iterable.empty,
+ )
+ )
+
+ scheduler.send(evcsAgent, Activation(INIT_SIM_TICK))
+
+ /* Actor should ask for registration with primary service */
+ primaryServiceProxy.expectMsg(
+ PrimaryServiceRegistrationMessage(evcsInputModelQv.getUuid)
+ )
+ /* State should be information handling and having correct state data */
+ evcsAgent.stateName shouldBe HandleInformation
+ evcsAgent.stateData match {
+ case ParticipantInitializingStateData(
+ inputModel,
+ modelConfig,
+ secondaryDataServices,
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ maybeEmAgent,
+ ) =>
+ inputModel shouldBe SimpleInputContainer(evcsInputModelQv)
+ modelConfig shouldBe modelConfig
+ secondaryDataServices shouldBe Iterable(
+ ActorEvMovementsService(evService.ref)
+ )
+ simulationStartDate shouldBe simulationStartDate
+ simulationEndDate shouldBe simulationEndDate
+ resolution shouldBe resolution
+ requestVoltageDeviationThreshold shouldBe requestVoltageDeviationThreshold
+ outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe Some(emAgent.ref.toTyped)
+ case unsuitableStateData =>
+ fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
+ }
+
+ /* Refuse registration */
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
+
+ emAgent.expectMsg(
+ RegisterParticipant(
+ evcsInputModelQv.getUuid,
+ evcsAgent.toTyped,
+ evcsInputModelQv,
+ )
+ )
+ // only receive registration message. ScheduleFlexRequest after secondary service initialized
+ emAgent.expectNoMessage()
+
+ evService.expectMsg(RegisterForEvDataMessage(evcsInputModelQv.getUuid))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
+
+ emAgent.expectMsg(
+ ScheduleFlexRequest(evcsInputModelQv.getUuid, 0)
+ )
+
+ scheduler.expectMsg(Completion(evcsAgent.toTyped))
+
+ /* ... as well as corresponding state and state data */
+ evcsAgent.stateName shouldBe Idle
+ evcsAgent.stateData match {
+ case ParticipantModelBaseStateData(
+ startDate,
+ endDate,
+ _,
+ services,
+ outputConfig,
+ additionalActivationTicks,
+ foreseenDataTicks,
+ _,
+ voltageValueStore,
+ resultValueStore,
+ requestValueStore,
+ _,
+ _,
+ _,
+ ) =>
+ /* Base state data */
+ startDate shouldBe simulationStartDate
+ endDate shouldBe simulationEndDate
+ services shouldBe Iterable(
+ ActorEvMovementsService(evService.ref)
+ )
+ outputConfig shouldBe defaultOutputConfig
+ additionalActivationTicks shouldBe empty
+ foreseenDataTicks shouldBe Map(evService.ref -> None)
+ voltageValueStore shouldBe ValueStore(
+ resolution,
+ SortedMap(0L -> Each(1.0)),
+ )
+ resultValueStore shouldBe ValueStore(
+ resolution
+ )
+ requestValueStore shouldBe ValueStore[ApparentPower](
+ resolution
+ )
+ case unrecognized =>
+ fail(
+ s"Did not find expected state data $ParticipantModelBaseStateData, but $unrecognized"
+ )
+ }
+ }
+
+ "provide correct flex options when in Idle" in {
+ val evService = TestProbe("evService")
+ val emAgent = TestProbe("EmAgentProbe")
+ val resultListener = TestProbe("ResultListener")
+
+ val evcsAgent = TestFSMRef(
+ new EvcsAgent(
+ scheduler = scheduler.ref,
+ initStateData = ParticipantInitializeStateData(
+ inputModel = SimpleInputContainer(evcsInputModelQv),
+ modelConfig = modelConfig,
+ secondaryDataServices = Iterable(
+ ActorEvMovementsService(evService.ref)
+ ),
+ simulationStartDate = simulationStartDate,
+ simulationEndDate = simulationEndDate,
+ resolution = resolution,
+ requestVoltageDeviationThreshold = requestVoltageDeviationThreshold,
+ outputConfig = NotifierConfig(
+ simulationResultInfo = true,
+ powerRequestReply = false,
+ flexResult = true,
+ ),
+ primaryServiceProxy = primaryServiceProxy.ref,
+ maybeEmAgent = Some(emAgent.ref.toTyped),
+ ),
+ listener = Iterable(resultListener.ref),
+ )
+ )
+
+ scheduler.send(evcsAgent, Activation(INIT_SIM_TICK))
+
+ /* Actor should ask for registration with primary service */
+ primaryServiceProxy.expectMsg(
+ PrimaryServiceRegistrationMessage(evcsInputModelQv.getUuid)
+ )
+ /* State should be information handling and having correct state data */
+ evcsAgent.stateName shouldBe HandleInformation
+ evcsAgent.stateData match {
+ case ParticipantInitializingStateData(
+ inputModel,
+ modelConfig,
+ secondaryDataServices,
+ simulationStartDate,
+ simulationEndDate,
+ resolution,
+ requestVoltageDeviationThreshold,
+ outputConfig,
+ maybeEmAgent,
+ ) =>
+ inputModel shouldBe SimpleInputContainer(evcsInputModelQv)
+ modelConfig shouldBe modelConfig
+ secondaryDataServices shouldBe Iterable(
+ ActorEvMovementsService(evService.ref)
+ )
+ simulationStartDate shouldBe simulationStartDate
+ simulationEndDate shouldBe simulationEndDate
+ resolution shouldBe resolution
+ requestVoltageDeviationThreshold shouldBe requestVoltageDeviationThreshold
+ outputConfig shouldBe NotifierConfig(
+ simulationResultInfo = true,
+ powerRequestReply = false,
+ flexResult = true,
+ )
+ maybeEmAgent shouldBe Some(emAgent.ref.toTyped)
+ case unsuitableStateData =>
+ fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
+ }
+
+ /* Refuse registration */
+ primaryServiceProxy.send(
+ evcsAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
+
+ emAgent.expectMsg(
+ RegisterParticipant(
+ evcsInputModelQv.getUuid,
+ evcsAgent.toTyped,
+ evcsInputModelQv,
+ )
+ )
+ emAgent.expectNoMessage()
+
+ evService.expectMsg(RegisterForEvDataMessage(evcsInputModelQv.getUuid))
+ evService.send(
+ evcsAgent,
+ RegistrationSuccessfulMessage(evService.ref, None),
+ )
+
+ emAgent.expectMsg(
+ ScheduleFlexRequest(evcsInputModelQv.getUuid, 0)
+ )
+
+ scheduler.expectMsg(Completion(evcsAgent.toTyped))
+
+ /* TICK 0 (expected activation)
+ - currently no cars
+ */
+
+ emAgent.send(evcsAgent, RequestFlexOptions(0))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ refPower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ refPower should approximate(Kilowatts(0.0))
+ minPower should approximate(Kilowatts(0.0))
+ maxPower should approximate(Kilowatts(0.0))
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 0.toDateTime
+ flexResult.getpRef should beEquivalentTo(0d.asKiloWatt)
+ flexResult.getpMin should beEquivalentTo(0d.asKiloWatt)
+ flexResult.getpMax should beEquivalentTo(0d.asKiloWatt)
+ }
+
+ emAgent.send(
+ evcsAgent,
+ IssueNoControl(0),
+ )
+
+ // next potential activation at fully charged battery:
+ // net power = 12.961kW * 0.92 = 11.92412kW
+ // time to charge fully ~= 16.7727262054h = 60382 ticks (rounded)
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(0))
+ result.q should approximate(Megavars(0))
+ requestAtNextActivation shouldBe false
+ requestAtTick shouldBe None
+ }
+
+ // results arrive after next activation
+ resultListener.expectNoMessage()
+
+ /* TICK 900
+ - ev 900 arrives
+ - charging with 11 kW
+ */
+
+ val ev900 = EvModelWrapper(evA.copyWithDeparture(4500))
+
+ evService.send(
+ evcsAgent,
+ ProvideEvDataMessage(
+ 900,
+ evService.ref,
+ ArrivingEvsData(Seq(ev900)),
+ ),
+ )
+
+ emAgent.expectMsg(ScheduleFlexRequest(evcsInputModelQv.getUuid, 900))
+ emAgent.send(evcsAgent, RequestFlexOptions(900))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe ev900.sRatedAc
+ minPower shouldBe ev900.sRatedAc // battery is empty
+ maxPower shouldBe ev900.sRatedAc
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 900.toDateTime
+ flexResult.getpRef should beEquivalentTo(ev900.unwrap().getSRatedAC)
+ flexResult.getpMin should beEquivalentTo(ev900.unwrap().getSRatedAC)
+ flexResult.getpMax should beEquivalentTo(ev900.unwrap().getSRatedAC)
+ }
+
+ emAgent.send(evcsAgent, IssueNoControl(900))
+
+ // at 4500 ev is departing
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(
+ Kilowatts(
+ ev900
+ .unwrap()
+ .getSRatedAC
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ )
+ )
+ result.q should approximate(Megavars(0))
+ requestAtNextActivation shouldBe true
+ requestAtTick shouldBe Some(4500)
+ }
+
+ // result of tick 0
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 0.toDateTime
+ result.getP should beEquivalentTo(0d.asMegaWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ /* TICK 4500
+ - ev900 departs, ev4500 arrives
+ - charging with 11 kW
+ */
+
+ // departure first
+ evService.send(
+ evcsAgent,
+ DepartingEvsRequest(4500, Seq(ev900.uuid)),
+ )
+
+ evService.expectMsgPF() { case DepartingEvsResponse(uuid, evs) =>
+ evs.size shouldBe 1
+ uuid shouldBe evcsInputModelQv.getUuid
+ evs.headOption.foreach { ev =>
+ ev.uuid shouldBe ev900.uuid
+ ev.storedEnergy should approximate(KilowattHours(11.0))
+ }
+ }
+
+ // results arrive right after departure request
+ Range(0, 2)
+ .map { _ =>
+ resultListener.expectMsgType[ParticipantResultEvent]
+ }
+ .foreach {
+ case ParticipantResultEvent(result: EvResult)
+ if result.getTime.equals(900.toDateTime) =>
+ result.getInputModel shouldBe ev900.uuid
+ result.getP should beEquivalentTo(ev900.unwrap().getSRatedAC)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(0d.asPercent)
+ case ParticipantResultEvent(result: EvResult)
+ if result.getTime.equals(4500.toDateTime) =>
+ result.getInputModel shouldBe ev900.uuid
+ result.getP should beEquivalentTo(0d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(18.96551724137931d.asPercent)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 900.toDateTime
+ result.getP should beEquivalentTo(ev900.unwrap().getSRatedAC)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
}
+
+ val ev4500 = EvModelWrapper(evB.copyWithDeparture(72000))
+
+ evService.send(
+ evcsAgent,
+ ProvideEvDataMessage(
+ 4500,
+ evService.ref,
+ ArrivingEvsData(Seq(ev4500)),
+ ),
+ )
+
+ emAgent.expectMsg(ScheduleFlexRequest(evcsInputModelQv.getUuid, 4500))
+ emAgent.send(evcsAgent, RequestFlexOptions(4500))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe ev4500.sRatedAc
+ minPower shouldBe ev900.sRatedAc // battery is empty
+ maxPower shouldBe ev4500.sRatedAc
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 4500.toDateTime
+ flexResult.getpRef should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ flexResult.getpMin should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ flexResult.getpMax should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ }
+
+ emAgent.send(evcsAgent, IssueNoControl(4500))
+
+ // we currently have an empty battery in ev4500
+ // time to charge to minimal soc ~= 1.45454545455h = 5236 ticks (rounded) from now
+ // current tick is 4500, thus: 4500 + 5236 = 9736
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(11))
+ result.q should approximate(Megavars(0))
+ requestAtNextActivation shouldBe true
+ requestAtTick shouldBe Some(9736)
+ }
+
+ // already sent out after EV departed
+ resultListener.expectNoMessage()
+
+ /* TICK 9736
+ - flex control changes
+ - charging with 10 kW
+ */
+
+ // sending flex request at very next activated tick
+ emAgent.send(evcsAgent, RequestFlexOptions(9736))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe ev4500.sRatedAc
+ minPower shouldBe Kilowatts(0.0) // battery is exactly at margin
+ maxPower shouldBe ev4500.sRatedAc
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 9736.toDateTime
+ flexResult.getpRef should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ flexResult.getpMin should beEquivalentTo(0d.asKiloWatt)
+ flexResult.getpMax should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ }
+
+ emAgent.send(evcsAgent, IssuePowerControl(9736, Kilowatts(10.0)))
+
+ evService.expectNoMessage()
+
+ // ev4500 is now at 16 kWh
+ // time to charge fully = 6.4 h = 23040 ticks from now
+ // current tick is 9736, thus: 9736 + 23040 = 32776
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(10))
+ result.q should approximate(Megavars(0))
+ // since battery is still below lowest soc, it's still considered empty
+ requestAtNextActivation shouldBe true
+ requestAtTick shouldBe Some(32776)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvResult) =>
+ result.getInputModel shouldBe ev4500.uuid
+ result.getTime shouldBe 4500.toDateTime
+ result.getP should beEquivalentTo(11d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(0d.asPercent)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 4500.toDateTime
+ result.getP should beEquivalentTo(11d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ /* TICK 11700
+ - ev11700 arrives
+ - charging with 16 kW
+ */
+
+ // with stored energy right at minimal SOC
+ val ev11700 = EvModelWrapper(
+ evA.copyWithDeparture(36000).copyWith(11.6d.asKiloWattHour)
+ )
+
+ evService.send(
+ evcsAgent,
+ ProvideEvDataMessage(
+ 11700,
+ evService.ref,
+ ArrivingEvsData(Seq(ev11700)),
+ ),
+ )
+
+ emAgent.expectMsg(ScheduleFlexRequest(evcsInputModelQv.getUuid, 11700))
+ emAgent.send(evcsAgent, RequestFlexOptions(11700))
+
+ val combinedChargingPower =
+ ev11700.unwrap().getSRatedAC.add(ev4500.unwrap().getSRatedAC)
+ val combinedChargingPowerSq = Kilowatts(
+ combinedChargingPower.to(PowerSystemUnits.KILOWATT).getValue.doubleValue
+ )
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ refPower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ refPower shouldBe combinedChargingPowerSq
+ minPower shouldBe ev4500.sRatedAc * -1 // battery of earlier ev is above lowest soc now
+ maxPower shouldBe combinedChargingPowerSq
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 11700.toDateTime
+ flexResult.getpRef should beEquivalentTo(combinedChargingPower)
+ flexResult.getpMin should beEquivalentTo(
+ ev4500.unwrap().getSRatedAC.multiply(-1)
+ )
+ flexResult.getpMax should beEquivalentTo(combinedChargingPower)
+ }
+
+ emAgent.send(evcsAgent, IssuePowerControl(11700, Kilowatts(16)))
+
+ // no departing evs here
+ evService.expectNoMessage()
+
+ // ev4500 is now at ~ 21.45555556 kWh, ev11700 just arrived with 11.6 kWh
+ // ev4500: time to charge fully ~= 7.3180556 h = 26345 ticks from now
+ // ev11700: time to charge fully = 5.8 h = 20880 ticks from now
+ // current tick is 11700, thus: 11700 + 20880 = 32580
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(16))
+ result.q should approximate(Megavars(0))
+ // since battery is still below lowest soc, it's still considered empty
+ requestAtNextActivation shouldBe true
+ requestAtTick shouldBe Some(32580)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvResult) =>
+ result.getInputModel shouldBe ev4500.uuid
+ result.getTime shouldBe 9736.toDateTime
+ result.getP should beEquivalentTo(10d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(20d.asPercent, 1e-2)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 9736.toDateTime
+ result.getP should beEquivalentTo(10d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ /* TICK 18000
+ - flex control changes
+ - discharging with 20 kW
+ */
+
+ // sending flex request at very next activated tick
+ emAgent.send(evcsAgent, RequestFlexOptions(18000))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe combinedChargingPowerSq
+ minPower shouldBe combinedChargingPowerSq * -1 // battery of both evs is above lowest soc now
+ maxPower shouldBe combinedChargingPowerSq
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 18000.toDateTime
+ flexResult.getpRef should beEquivalentTo(combinedChargingPower)
+ flexResult.getpMin should beEquivalentTo(
+ combinedChargingPower.multiply(
+ -1
+ )
+ )
+ flexResult.getpMax should beEquivalentTo(combinedChargingPower)
+ }
+
+ emAgent.send(evcsAgent, IssuePowerControl(18000, Kilowatts(-20)))
+
+ // no departing evs here
+ evService.expectNoMessage()
+
+ // ev4500 is now at ~ 35.455556 kWh, ev11700 at 25.6 kWh
+ // ev4500: time to discharge to lowest soc ~= 1.9455556 h = 7004 ticks from now
+ // ev11700: time to discharge to lowest soc ~= 1.4 h = 5040 ticks from now
+ // current tick is 18000, thus: 18000 + 5040 = 23040
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(-20))
+ result.q should approximate(Megavars(0))
+ requestAtNextActivation shouldBe false
+ requestAtTick shouldBe Some(23040)
+ }
+
+ Range(0, 2)
+ .map { _ =>
+ resultListener.expectMsgType[ParticipantResultEvent]
+ }
+ .foreach {
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev4500.uuid =>
+ result.getTime shouldBe 11700.toDateTime
+ result.getP should beEquivalentTo(8d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(26.819d.asPercent, 1e-2)
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev11700.uuid =>
+ result.getTime shouldBe 11700.toDateTime
+ result.getP should beEquivalentTo(8d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(20.0d.asPercent)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 11700.toDateTime
+ result.getP should beEquivalentTo(16d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ /* TICK 23040
+ - ev11700 at lowest soc
+ - discharging with 10 kW
+ */
+
+ emAgent.send(evcsAgent, RequestFlexOptions(23040))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe combinedChargingPowerSq
+ minPower shouldBe ev4500.sRatedAc * -1 // battery of ev11700 is below lowest soc now
+ maxPower shouldBe combinedChargingPowerSq
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 23040.toDateTime
+ flexResult.getpRef should beEquivalentTo(combinedChargingPower)
+ flexResult.getpMin should beEquivalentTo(
+ ev4500
+ .unwrap()
+ .getSRatedAC
+ .multiply(
+ -1
+ )
+ )
+ flexResult.getpMax should beEquivalentTo(combinedChargingPower)
+ }
+
+ emAgent.send(evcsAgent, IssuePowerControl(23040, Kilowatts(-10)))
+
+ // no departing evs here
+ evService.expectNoMessage()
+
+ // ev4500 is now at 21.455556 kWh, ev11700 at 11.6 kWh (lowest soc)
+ // ev4500: time to discharge to lowest soc = 0.5455556 h = 1964 ticks from now
+ // current tick is 18864, thus: 23040 + 1964 = 25004
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(-10))
+ result.q should approximate(Megavars(0))
+ requestAtNextActivation shouldBe false
+ requestAtTick shouldBe Some(25004)
+ }
+
+ Range(0, 2)
+ .map { _ =>
+ resultListener.expectMsgType[ParticipantResultEvent]
+ }
+ .foreach {
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev4500.uuid =>
+ result.getTime shouldBe 18000.toDateTime
+ result.getP should beEquivalentTo((-10d).asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(44.3194d.asPercent, 1e-2)
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev11700.uuid =>
+ result.getTime shouldBe 18000.toDateTime
+ result.getP should beEquivalentTo((-10d).asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(44.137931034d.asPercent, 1e-6)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 18000.toDateTime
+ result.getP should beEquivalentTo((-20d).asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ /* TICK 25004
+ - both evs at lowest soc
+ - no power
+ */
+
+ emAgent.send(evcsAgent, RequestFlexOptions(25004))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe combinedChargingPowerSq
+ minPower shouldBe Kilowatts(0.0) // both at lowest soc
+ maxPower shouldBe combinedChargingPowerSq
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 25004.toDateTime
+ flexResult.getpRef should beEquivalentTo(combinedChargingPower)
+ flexResult.getpMin should beEquivalentTo(0.asKiloWatt)
+ flexResult.getpMax should beEquivalentTo(combinedChargingPower)
+ }
+
+ emAgent.send(evcsAgent, IssuePowerControl(25004, Kilowatts(0.0)))
+
+ // no departing evs here
+ evService.expectNoMessage()
+
+ // no new activation
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(0))
+ result.q should approximate(Megavars(0))
+ requestAtNextActivation shouldBe false
+ requestAtTick shouldBe None
+ }
+
+ Range(0, 2)
+ .map { _ =>
+ resultListener.expectMsgType[ParticipantResultEvent]
+ }
+ .foreach {
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev4500.uuid =>
+ result.getTime shouldBe 23040.toDateTime
+ result.getP should beEquivalentTo((-10d).asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(26.819445d.asPercent, 1e-2)
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev11700.uuid =>
+ result.getTime shouldBe 23040.toDateTime
+ result.getP should beEquivalentTo(0d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(20d.asPercent)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 23040.toDateTime
+ result.getP should beEquivalentTo((-10d).asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ /* TICK 36000
+ - ev11700 departs
+ - charging with 4 kW
+ */
+
+ // departure first
+ evService.send(
+ evcsAgent,
+ DepartingEvsRequest(36000, Seq(ev900.uuid)),
+ )
+
+ evService.expectMsgPF() { case DepartingEvsResponse(uuid, evs) =>
+ evs.size shouldBe 1
+ uuid shouldBe evcsInputModelQv.getUuid
+ evs.headOption.foreach { ev =>
+ ev.uuid shouldBe ev11700.uuid
+ ev.storedEnergy should approximate(KilowattHours(11.6))
+ }
+ }
+
+ Range(0, 2)
+ .map { _ =>
+ resultListener.expectMsgType[ParticipantResultEvent]
+ }
+ .foreach {
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev4500.uuid =>
+ result.getTime shouldBe 25004.toDateTime
+ result.getP should beEquivalentTo(0d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(20d.asPercent, 1e-2)
+ case ParticipantResultEvent(result: EvResult)
+ if result.getInputModel == ev11700.uuid =>
+ result.getTime shouldBe 25004.toDateTime
+ result.getP should beEquivalentTo(0d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ result.getSoc should beEquivalentTo(20d.asPercent)
+ }
+
+ resultListener.expectMsgPF() {
+ case ParticipantResultEvent(result: EvcsResult) =>
+ result.getInputModel shouldBe evcsInputModelQv.getUuid
+ result.getTime shouldBe 25004.toDateTime
+ result.getP should beEquivalentTo(0d.asKiloWatt)
+ result.getQ should beEquivalentTo(0d.asMegaVar)
+ }
+
+ // sending flex request at very next activated tick
+ emAgent.send(evcsAgent, RequestFlexOptions(36000))
+
+ emAgent.expectMsgType[ProvideFlexOptions] match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ referencePower shouldBe ev4500.sRatedAc
+ minPower shouldBe Kilowatts(0d)
+ maxPower shouldBe ev4500.sRatedAc
+ }
+
+ resultListener.expectMsgPF() { case FlexOptionsResultEvent(flexResult) =>
+ flexResult.getInputModel shouldBe evcsInputModelQv.getUuid
+ flexResult.getTime shouldBe 36000.toDateTime
+ flexResult.getpRef should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ flexResult.getpMin should beEquivalentTo(0.asKiloWatt)
+ flexResult.getpMax should beEquivalentTo(ev4500.unwrap().getSRatedAC)
+ }
+
+ emAgent.send(evcsAgent, IssuePowerControl(36000, Kilowatts(4.0)))
+
+ // ev11700 is now at 16 kWh
+ // ev11700: time to charge fully = 16 h = 57600 ticks from now
+ // current tick is 36000, thus: 36000 + 57600 = 93600
+ // BUT: departing tick 72000 is earlier
+ emAgent.expectMsgPF() {
+ case FlexCtrlCompletion(
+ modelUuid,
+ result,
+ requestAtNextActivation,
+ requestAtTick,
+ ) =>
+ modelUuid shouldBe evcsInputModelQv.getUuid
+ result.p should approximate(Kilowatts(4))
+ result.q should approximate(Megavars(0))
+ // since we're starting from lowest again
+ requestAtNextActivation shouldBe true
+ requestAtTick shouldBe Some(72000)
+ }
+
+ // expect no more messages after completion of initialization
+ scheduler.expectNoMessage()
+
}
+
}
+
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/FixedFeedInAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/FixedFeedInAgentModelCalculationSpec.scala
index 1b3046f6f4..bdda047856 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/FixedFeedInAgentModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/FixedFeedInAgentModelCalculationSpec.scala
@@ -21,7 +21,7 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
ParticipantInitializeStateData,
ParticipantInitializingStateData,
ParticipantUninitializedStateData,
- SimpleInputContainer
+ SimpleInputContainer,
}
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
@@ -33,7 +33,7 @@ import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
@@ -49,6 +49,7 @@ import squants.energy.{Kilowatts, Megawatts, Watts}
import java.time.ZonedDateTime
import java.util.concurrent.TimeUnit
+import scala.collection.SortedMap
class FixedFeedInAgentModelCalculationSpec
extends ParticipantAgentSpec(
@@ -58,7 +59,7 @@ class FixedFeedInAgentModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with FixedFeedInputTestData {
@@ -82,11 +83,12 @@ class FixedFeedInAgentModelCalculationSpec
private val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0d))
+ LoadReference.ActivePower(Kilowatts(0d)),
)
private val defaultOutputConfig = NotifierConfig(
simonaConfig.simona.output.participant.defaultConfig.simulationResult,
- simonaConfig.simona.output.participant.defaultConfig.powerRequestReply
+ simonaConfig.simona.output.participant.defaultConfig.powerRequestReply,
+ simonaConfig.simona.output.participant.defaultConfig.flexResult,
)
private val fixedFeedConfigUtil = ConfigUtil.ParticipantConfigUtil(
@@ -96,14 +98,14 @@ class FixedFeedInAgentModelCalculationSpec
fixedFeedConfigUtil.getOrDefault[FixedFeedInRuntimeConfig](
voltageSensitiveInput.getUuid
)
- private val services = None
+ private val services = Iterable.empty
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
"A fixed feed in agent with model calculation " should {
val initStateData = ParticipantInitializeStateData[
FixedFeedInInput,
FixedFeedInRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
modelConfig = modelConfig,
@@ -114,7 +116,7 @@ class FixedFeedInAgentModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -122,7 +124,7 @@ class FixedFeedInAgentModelCalculationSpec
new FixedFeedInAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -142,7 +144,7 @@ class FixedFeedInAgentModelCalculationSpec
new FixedFeedInAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -163,7 +165,8 @@ class FixedFeedInAgentModelCalculationSpec
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
) =>
inputModel shouldBe SimpleInputContainer(voltageSensitiveInput)
modelConfig shouldBe modelConfig
@@ -173,12 +176,16 @@ class FixedFeedInAgentModelCalculationSpec
resolution shouldBe this.resolution
requestVoltageDeviationThreshold shouldBe simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold
outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe None
case unsuitableStateData =>
fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
}
/* Refuse registration */
- primaryServiceProxy.send(fixedFeedAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ fixedFeedAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a completion notification */
scheduler.expectMsg(Completion(fixedFeedAgent.toTyped, Some(0)))
@@ -198,23 +205,22 @@ class FixedFeedInAgentModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
) =>
/* Base state data */
startDate shouldBe simulationStartDate
endDate shouldBe simulationEndDate
- services shouldBe None
+ services shouldBe Iterable.empty
outputConfig shouldBe defaultOutputConfig
additionalActivationTicks shouldBe empty
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
resolution,
- Map(0L -> Each(1.0))
- )
- resultValueStore shouldBe ValueStore.forResult(
- resolution,
- 2
+ SortedMap(0L -> Each(1.0)),
)
+ resultValueStore shouldBe ValueStore(resolution)
requestValueStore shouldBe ValueStore[ApparentPower](
resolution
)
@@ -230,7 +236,7 @@ class FixedFeedInAgentModelCalculationSpec
new FixedFeedInAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -238,7 +244,10 @@ class FixedFeedInAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(fixedFeedAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ fixedFeedAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -249,27 +258,27 @@ class FixedFeedInAgentModelCalculationSpec
fixedFeedAgent ! RequestAssetPowerMessage(
0L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
)
inside(fixedFeedAgent.stateData) {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.requestValueStore shouldBe ValueStore[
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.requestValueStore shouldBe ValueStore[
ApparentPower
](
resolution,
- Map(
+ SortedMap(
0L -> ApparentPower(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
- )
+ ),
)
case _ =>
fail(
@@ -283,7 +292,7 @@ class FixedFeedInAgentModelCalculationSpec
new FixedFeedInAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -291,7 +300,10 @@ class FixedFeedInAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(fixedFeedAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ fixedFeedAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I am not interested in the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -308,17 +320,13 @@ class FixedFeedInAgentModelCalculationSpec
awaitAssert(fixedFeedAgent.stateName shouldBe Idle)
inside(fixedFeedAgent.stateData) {
- case participantModelBaseStateData: ParticipantModelBaseStateData[
- _,
- _,
- _
- ] =>
- participantModelBaseStateData.resultValueStore.last(0L) match {
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.resultValueStore.last(0L) match {
case Some((tick, entry)) =>
tick shouldBe 0L
inside(entry) { case ApparentPower(p, q) =>
- (p ~= Megawatts(-268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(-268.603e-6))
+ q should approximate(Megavars(0.0))
}
case None =>
fail("Result value store does not contain entry for tick 900.")
@@ -335,7 +343,7 @@ class FixedFeedInAgentModelCalculationSpec
new FixedFeedInAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -344,7 +352,10 @@ class FixedFeedInAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(fixedFeedAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ fixedFeedAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(fixedFeedAgent.toTyped, Some(0)))
@@ -359,13 +370,13 @@ class FixedFeedInAgentModelCalculationSpec
fixedFeedAgent ! RequestAssetPowerMessage(
3000L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(-268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(-268.603e-6))
+ q should approximate(Megavars(0.0))
}
}
@@ -373,7 +384,7 @@ class FixedFeedInAgentModelCalculationSpec
new FixedFeedInAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -384,7 +395,10 @@ class FixedFeedInAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(fixedFeedAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ fixedFeedAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(fixedFeedAgent.toTyped, Some(0)))
@@ -397,13 +411,13 @@ class FixedFeedInAgentModelCalculationSpec
fixedFeedAgent ! RequestAssetPowerMessage(
3000L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(-268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(-268.603e-6))
+ q should approximate(Megavars(0.0))
case answer => fail(s"Did not expect to get that answer: $answer")
}
}
@@ -414,14 +428,14 @@ class FixedFeedInAgentModelCalculationSpec
fixedFeedAgent ! RequestAssetPowerMessage(
3000L,
Each(1.000000000000001d),
- Each(0d)
+ Each(0d),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(-268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(-268.603e-6))
+ q should approximate(Megavars(0.0))
}
}
@@ -430,14 +444,14 @@ class FixedFeedInAgentModelCalculationSpec
fixedFeedAgent ! RequestAssetPowerMessage(
3000L,
Each(0.98),
- Each(0d)
+ Each(0d),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgClass(classOf[AssetPowerChangedMessage]) match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(-0.000268603)) shouldBe true
- (q ~= Megavars(-22.07138418e-6)) shouldBe true
+ p should approximate(Megawatts(-0.000268603))
+ q should approximate(Megavars(-22.07138418e-6))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala
index 9ac4aac473..e8fd0715aa 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/HpAgentModelCalculationSpec.scala
@@ -6,10 +6,6 @@
package edu.ie3.simona.agent.participant
-import org.apache.pekko.actor.ActorSystem
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.testkit.{TestFSMRef, TestProbe}
-import org.apache.pekko.util.Timeout
import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.models.input.system.HpInput
import edu.ie3.simona.agent.ValueStore
@@ -25,24 +21,24 @@ import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.config.SimonaConfig.HpRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.integration.common.IntegrationSpecCommon
-import edu.ie3.simona.model.participant.HpModel.{HpRelevantData, HpState}
+import edu.ie3.simona.model.participant.HpModel.HpState
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.{
ProvideWeatherMessage,
RegisterForWeatherMessage,
- WeatherData
+ WeatherData,
}
import edu.ie3.simona.test.ParticipantAgentSpec
import edu.ie3.simona.test.common.model.participant.HpTestData
@@ -52,17 +48,22 @@ import edu.ie3.util.scala.quantities.{
Megavars,
ReactivePower,
Vars,
- WattsPerSquareMeter
+ WattsPerSquareMeter,
}
+import org.apache.pekko.actor.ActorSystem
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.testkit.{TestFSMRef, TestProbe}
+import org.apache.pekko.util.Timeout
import org.scalatest.PrivateMethodTester
import squants.energy.{Kilowatts, Megawatts, Watts}
import squants.motion.MetersPerSecond
import squants.thermal.Celsius
-import squants.{Dimensionless, Each, Power, Temperature}
+import squants.{Dimensionless, Each}
import java.io.File
import java.time.ZonedDateTime
import java.util.concurrent.TimeUnit
+import scala.collection.SortedMap
class HpAgentModelCalculationSpec
extends ParticipantAgentSpec(
@@ -72,7 +73,7 @@ class HpAgentModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with HpTestData
@@ -81,12 +82,15 @@ class HpAgentModelCalculationSpec
implicit val simulationStart: ZonedDateTime = defaultSimulationStart
implicit val receiveTimeOut: Timeout = Timeout(10, TimeUnit.SECONDS)
implicit val noReceiveTimeOut: Timeout = Timeout(1, TimeUnit.SECONDS)
- implicit val powerTolerance: Power = Watts(1e-3)
- implicit val reactivepowerTolerance: ReactivePower = Vars(1e-3)
- implicit val temperatureTolerance: Temperature = Celsius(1e-10)
- implicit val dimensionlessTolerance: Dimensionless = Each(1e-10)
+
+ private implicit val powerTolerance: squants.Power = Watts(0.1)
+ private implicit val reactivePowerTolerance: ReactivePower = Vars(0.1)
+ private implicit val temperatureTolerance: squants.Temperature = Celsius(
+ 1e-10
+ )
+
/* Alter the input model to have a voltage sensitive reactive power calculation */
- val hpInput: HpInput = inputModel
+ val hpInput: HpInput = hpInputModel
private val simonaConfig: SimonaConfig = SimonaConfig(
ConfigFactory
@@ -96,7 +100,8 @@ class HpAgentModelCalculationSpec
)
private val defaultOutputConfig = NotifierConfig(
simonaConfig.simona.output.participant.defaultConfig.simulationResult,
- simonaConfig.simona.output.participant.defaultConfig.powerRequestReply
+ simonaConfig.simona.output.participant.defaultConfig.powerRequestReply,
+ simonaConfig.simona.output.participant.defaultConfig.flexResult,
)
private val participantConfigUtil = ConfigUtil.ParticipantConfigUtil(
simonaConfig.simona.runtime.participant
@@ -105,11 +110,9 @@ class HpAgentModelCalculationSpec
participantConfigUtil.getOrDefault[HpRuntimeConfig](
hpInput.getUuid
)
- private val noServices = None
- private val services = Some(
- Vector(
- ActorWeatherService(weatherService.ref)
- )
+ private val noServices = Iterable.empty
+ private val services = Iterable(
+ ActorWeatherService(weatherService.ref)
)
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
@@ -117,7 +120,7 @@ class HpAgentModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
HpInput,
HpRuntimeConfig,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
](
inputModel = hpInput,
modelConfig = modelConfig,
@@ -128,7 +131,8 @@ class HpAgentModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
+ maybeEmAgent = None,
)
"be instantiated correctly" in {
@@ -136,7 +140,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -158,7 +162,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -168,7 +172,10 @@ class HpAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
deathProbe.expectTerminated(hpAgent)
}
@@ -178,7 +185,7 @@ class HpAgentModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
HpInput,
HpRuntimeConfig,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
](
inputModel = hpInput,
thermalGrid = thermalGrid,
@@ -190,7 +197,8 @@ class HpAgentModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
+ maybeEmAgent = None,
)
"be instantiated correctly" in {
@@ -198,7 +206,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -218,7 +226,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -239,7 +247,8 @@ class HpAgentModelCalculationSpec
defaultSimulationEnd,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ _,
) =>
inputModel shouldBe WithHeatInputContainer(hpInput, thermalGrid)
modelConfig shouldBe modelConfig
@@ -254,7 +263,10 @@ class HpAgentModelCalculationSpec
}
/* Refuse registration */
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
weatherService.expectMsg(
@@ -277,28 +289,29 @@ class HpAgentModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
),
awaitRegistrationResponsesFrom,
- foreseenNextDataTicks
+ foreseenNextDataTicks,
) =>
/* Base state data */
startDate shouldBe defaultSimulationStart
endDate shouldBe defaultSimulationEnd
- services shouldBe Some(
- Vector(
- ActorWeatherService(weatherService.ref)
- )
+ services shouldBe Iterable(
+ ActorWeatherService(weatherService.ref)
)
outputConfig shouldBe NotifierConfig(
simulationResultInfo = true,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
additionalActivationTicks shouldBe empty
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
resolution,
- Map(0L -> Each(1d))
+ SortedMap(0L -> Each(1.0)),
)
resultValueStore shouldBe ValueStore(resolution)
requestValueStore shouldBe ValueStore[ApparentPowerAndHeat](
@@ -306,7 +319,7 @@ class HpAgentModelCalculationSpec
)
/* Additional information */
- awaitRegistrationResponsesFrom shouldBe Vector(weatherService.ref)
+ awaitRegistrationResponsesFrom shouldBe Iterable(weatherService.ref)
foreseenNextDataTicks shouldBe Map.empty
case _ =>
fail(
@@ -315,7 +328,10 @@ class HpAgentModelCalculationSpec
}
/* Reply, that registration was successful */
- weatherService.send(hpAgent, RegistrationSuccessfulMessage(Some(4711L)))
+ weatherService.send(
+ hpAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(4711L)),
+ )
/* Expect a completion message */
scheduler.expectMsg(Completion(hpAgent.toTyped, Some(4711L)))
@@ -323,7 +339,7 @@ class HpAgentModelCalculationSpec
/* ... as well as corresponding state and state data */
hpAgent.stateName shouldBe Idle
hpAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* Only check the awaited next data ticks, as the rest has yet been checked */
baseStateData.foreseenDataTicks shouldBe Map(
weatherService.ref -> Some(4711L)
@@ -340,7 +356,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -348,13 +364,19 @@ class HpAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
weatherService.expectMsg(
RegisterForWeatherMessage(51.4843281, 7.4116482)
)
- weatherService.send(hpAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ hpAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -364,29 +386,29 @@ class HpAgentModelCalculationSpec
hpAgent ! RequestAssetPowerMessage(
0L,
- Each(1d),
- Each(0d)
+ Dimensionless.primaryUnit(1.0),
+ Dimensionless.primaryUnit(0.0),
)
expectMsg(
AssetPowerChangedMessage(
- Megawatts(0d),
- Megavars(0d)
+ Megawatts(0.0),
+ Megavars(0.0),
)
)
inside(hpAgent.stateData) {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case modelBaseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
modelBaseStateData.requestValueStore shouldBe ValueStore[
ApparentPowerAndHeat
](
resolution,
- Map(
+ SortedMap(
0L -> ApparentPowerAndHeat(
- Megawatts(0d),
- Megavars(0d),
- Megawatts(0d)
+ Megawatts(0.0),
+ Megavars(0.0),
+ Megawatts(0.0),
)
- )
+ ),
)
case _ =>
fail(
@@ -400,7 +422,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -408,11 +430,17 @@ class HpAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(hpAgent, RegistrationSuccessfulMessage(Some(0L)))
+ weatherService.send(
+ hpAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(0L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -421,24 +449,24 @@ class HpAgentModelCalculationSpec
/* Send out new data */
val weatherData = WeatherData(
- WattsPerSquareMeter(0d),
- WattsPerSquareMeter(0d),
- Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ WattsPerSquareMeter(0),
+ WattsPerSquareMeter(0),
+ Celsius(1.815),
+ MetersPerSecond(7.726576),
)
weatherService.send(
hpAgent,
- ProvideWeatherMessage(0L, weatherData, Some(3600L))
+ ProvideWeatherMessage(0L, weatherService.ref, weatherData, Some(3600L)),
)
/* Find yourself in corresponding state and state data */
hpAgent.stateName shouldBe HandleInformation
hpAgent.stateData match {
case DataCollectionStateData(
- baseStateData: ParticipantModelBaseStateData[_, _, _],
+ baseStateData: ParticipantModelBaseStateData[_, _, _, _],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -467,48 +495,36 @@ class HpAgentModelCalculationSpec
hpAgent.stateName shouldBe Idle
hpAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
- /* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
- case ValueStore(_, store) =>
- store.get(0L) match {
- case Some(
- HpRelevantData(
- HpState(
- isRunning,
- lastTimeTick,
- activePower,
- qDot,
- thermalGridState
- ),
- currentTimeTick,
- ambientTemperature
- )
- ) =>
- isRunning shouldBe false
- lastTimeTick shouldBe 0L
- (activePower =~ Kilowatts(0d)) shouldBe true
-
- (qDot =~
- Kilowatts(0d)) shouldBe true
-
- thermalGridState.houseState match {
- case Some(ThermalHouseState(_, innerTemperature, _)) =>
- (innerTemperature =~
- Celsius(
- 20.9999769069444444444444444444444
- )) shouldBe true
- case None =>
- fail(
- s"Expected to get a result for thermal house '${inputModel.getUuid}'"
- )
- }
-
- currentTimeTick shouldBe 0L
- (ambientTemperature =~ Celsius(1.815d)) shouldBe true
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.stateDataStore.last(0L) match {
+ case Some(
+ (
+ _,
+ HpState(
+ isRunning,
+ lastTimeTick,
+ _,
+ activePower,
+ qDot,
+ thermalGridState,
+ _,
+ ),
+ )
+ ) =>
+ isRunning shouldBe false
+ lastTimeTick shouldBe 0L
+ activePower should approximate(Kilowatts(0.0))
+ qDot should approximate(Kilowatts(0.0))
+
+ thermalGridState.houseState match {
+ case Some(ThermalHouseState(_, innerTemperature, _)) =>
+ innerTemperature should approximate(Celsius(20.999976906944))
case None =>
- fail("Did expect to get hp relevant data for tick 0L")
+ fail(
+ s"Expected to get a result for thermal house '${hpInputModel.getUuid}'"
+ )
}
+ case None => fail("Expected to get a model state")
}
/* The store for simulation results has been extended */
@@ -517,12 +533,12 @@ class HpAgentModelCalculationSpec
store.size shouldBe 1
store.getOrElse(
0L,
- fail("Expected a simulation result for tick 900.")
+ fail("Expected a simulation result for tick 900."),
) match {
case ApparentPowerAndHeat(p, q, qDot) =>
- (p =~ Megawatts(0d)) shouldBe true
- q =~ Megavars(0d) shouldBe true
- qDot =~ Megawatts(0d) shouldBe true
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
+ qDot should approximate(Megawatts(0d))
}
}
case _ =>
@@ -537,7 +553,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -545,11 +561,17 @@ class HpAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(hpAgent, RegistrationSuccessfulMessage(Some(0L)))
+ weatherService.send(
+ hpAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(0L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -562,9 +584,9 @@ class HpAgentModelCalculationSpec
hpAgent.stateName shouldBe HandleInformation
hpAgent.stateData match {
case DataCollectionStateData(
- baseStateData: ParticipantModelBaseStateData[_, _, _],
+ baseStateData: ParticipantModelBaseStateData[_, _, _, _],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -584,15 +606,15 @@ class HpAgentModelCalculationSpec
/* Providing the awaited data will lead to the foreseen transitions */
val weatherData = WeatherData(
- WattsPerSquareMeter(0d),
- WattsPerSquareMeter(0d),
- Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ WattsPerSquareMeter(0),
+ WattsPerSquareMeter(0),
+ Celsius(1.815),
+ MetersPerSecond(7.726576),
)
weatherService.send(
hpAgent,
- ProvideWeatherMessage(0L, weatherData, Some(3600L))
+ ProvideWeatherMessage(0L, weatherService.ref, weatherData, Some(3600L)),
)
/* Expect confirmation */
@@ -601,47 +623,36 @@ class HpAgentModelCalculationSpec
/* Expect the state change to idle with updated base state data */
hpAgent.stateName shouldBe Idle
hpAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
- /* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
- case ValueStore(_, store) =>
- store.get(0L) match {
- case Some(
- HpRelevantData(
- HpState(
- isRunning,
- lastTimeTick,
- activePower,
- qDot,
- thermalGridState
- ),
- currentTimeTick,
- ambientTemperature
- )
- ) =>
- isRunning shouldBe false
- lastTimeTick shouldBe 0L
- (activePower =~ Kilowatts(0d)) shouldBe true
-
- (qDot =~ Kilowatts(0d)) shouldBe true
-
- thermalGridState.houseState match {
- case Some(ThermalHouseState(_, innerTemperature, _)) =>
- (innerTemperature =~ Celsius(
- 20.9999769069444444444444444444444
- )) shouldBe true
- case None =>
- fail(
- s"Expected to get a result for thermal house '${inputModel.getUuid}'"
- )
- }
-
- currentTimeTick shouldBe 0L
- (ambientTemperature =~
- Celsius(1.815d)) shouldBe true
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.stateDataStore.last(0L) match {
+ case Some(
+ (
+ _,
+ HpState(
+ isRunning,
+ lastTimeTick,
+ _,
+ activePower,
+ qDot,
+ thermalGridState,
+ _,
+ ),
+ )
+ ) =>
+ isRunning shouldBe false
+ lastTimeTick shouldBe 0L
+ activePower should approximate(Kilowatts(0d))
+ qDot should approximate(Kilowatts(0d))
+
+ thermalGridState.houseState match {
+ case Some(ThermalHouseState(_, innerTemperature, _)) =>
+ innerTemperature should approximate(Celsius(20.999976906944))
case None =>
- fail("Did expect to get hp relevant data for tick 0L")
+ fail(
+ s"Expected to get a result for thermal house '${hpInputModel.getUuid}'"
+ )
}
+ case None => fail("Expected to get a model state.")
}
/* The store for simulation results has been extended */
@@ -650,14 +661,12 @@ class HpAgentModelCalculationSpec
store.size shouldBe 1
store.getOrElse(
0L,
- fail("Expected a simulation result for tick 0.")
+ fail("Expected a simulation result for tick 0."),
) match {
case ApparentPowerAndHeat(p, q, qDot) =>
- (p =~ Megawatts(0d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
- (
- qDot =~ Megawatts(0d)
- ) shouldBe true
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
+ qDot should approximate(Megawatts(0d))
}
}
case _ =>
@@ -672,7 +681,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -681,11 +690,17 @@ class HpAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(hpAgent, RegistrationSuccessfulMessage(Some(3600L)))
+ weatherService.send(
+ hpAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(3600L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -694,22 +709,27 @@ class HpAgentModelCalculationSpec
/* Ask the agent for average power in tick 7200 */
hpAgent ! RequestAssetPowerMessage(
7200L,
- Each(1d),
- Each(0d)
+ Each(1.0),
+ Each(0.0),
)
expectNoMessage(noReceiveTimeOut.duration)
awaitAssert(hpAgent.stateName == Idle)
/* Send out the expected data and wait for the reply */
val weatherData = WeatherData(
- WattsPerSquareMeter(0d),
- WattsPerSquareMeter(0d),
- Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ WattsPerSquareMeter(0),
+ WattsPerSquareMeter(0),
+ Celsius(1.815),
+ MetersPerSecond(7.726576),
)
weatherService.send(
hpAgent,
- ProvideWeatherMessage(3600L, weatherData, Some(7200L))
+ ProvideWeatherMessage(
+ 3600L,
+ weatherService.ref,
+ weatherData,
+ Some(7200L),
+ ),
)
/* Trigger the agent */
@@ -722,8 +742,8 @@ class HpAgentModelCalculationSpec
/* Appreciate the answer to my previous request */
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p =~ Megawatts(0d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
}
}
@@ -731,7 +751,7 @@ class HpAgentModelCalculationSpec
new HpAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -741,11 +761,17 @@ class HpAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(hpAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ hpAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(hpAgent, RegistrationSuccessfulMessage(Some(0L)))
+ weatherService.send(
+ hpAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(0L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -757,14 +783,15 @@ class HpAgentModelCalculationSpec
hpAgent,
ProvideWeatherMessage(
0L,
+ weatherService.ref,
WeatherData(
- WattsPerSquareMeter(0d),
- WattsPerSquareMeter(0d),
- Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ WattsPerSquareMeter(0),
+ WattsPerSquareMeter(0),
+ Celsius(1.815),
+ MetersPerSecond(7.726576),
),
- Some(3600L)
- )
+ Some(3600L),
+ ),
)
scheduler.send(hpAgent, Activation(0))
scheduler.expectMsg(Completion(hpAgent.toTyped, Some(3600)))
@@ -774,14 +801,15 @@ class HpAgentModelCalculationSpec
hpAgent,
ProvideWeatherMessage(
3600L,
+ weatherService.ref,
WeatherData(
- WattsPerSquareMeter(0d),
- WattsPerSquareMeter(0d),
- Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ WattsPerSquareMeter(0),
+ WattsPerSquareMeter(0),
+ Celsius(1.815),
+ MetersPerSecond(7.726576),
),
- Some(7200L)
- )
+ Some(7200L),
+ ),
)
scheduler.send(hpAgent, Activation(3600))
scheduler.expectMsg(Completion(hpAgent.toTyped, Some(7200)))
@@ -791,14 +819,15 @@ class HpAgentModelCalculationSpec
hpAgent,
ProvideWeatherMessage(
7200L,
+ weatherService.ref,
WeatherData(
- WattsPerSquareMeter(0d),
- WattsPerSquareMeter(0d),
- Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ WattsPerSquareMeter(0),
+ WattsPerSquareMeter(0),
+ Celsius(1.815),
+ MetersPerSecond(7.726576),
),
- None
- )
+ None,
+ ),
)
scheduler.send(hpAgent, Activation(7200))
scheduler.expectMsg(Completion(hpAgent.toTyped))
@@ -806,15 +835,14 @@ class HpAgentModelCalculationSpec
/* Ask the agent for average power in tick 7500 */
hpAgent ! RequestAssetPowerMessage(
7500L,
- Each(1d),
- Each(0d)
+ Each(1.0),
+ Each(0.0),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p =~ Megawatts(0d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
-
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
case answer => fail(s"Did not expect to get that answer: $answer")
}
}
@@ -825,14 +853,14 @@ class HpAgentModelCalculationSpec
hpAgent ! RequestAssetPowerMessage(
7500L,
Each(1.000000000000001d),
- Each(0d)
+ Each(0.0),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p =~ Megawatts(0d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
}
}
@@ -840,16 +868,15 @@ class HpAgentModelCalculationSpec
/* Ask again with changed information */
hpAgent ! RequestAssetPowerMessage(
7500L,
- Each(0.98d),
- Each(0d)
+ Each(0.98),
+ Each(0.0),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgClass(classOf[AssetPowerChangedMessage]) match {
case AssetPowerChangedMessage(p, q) =>
- (p =~ Megawatts(0d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
-
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentFixedModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentFixedModelCalculationSpec.scala
index d7252f1dab..df28adfa5f 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentFixedModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentFixedModelCalculationSpec.scala
@@ -21,7 +21,7 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
ParticipantInitializeStateData,
ParticipantInitializingStateData,
ParticipantUninitializedStateData,
- SimpleInputContainer
+ SimpleInputContainer,
}
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
@@ -33,7 +33,7 @@ import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
@@ -48,6 +48,7 @@ import squants.Each
import squants.energy.{Kilowatts, Megawatts, Watts}
import java.util.concurrent.TimeUnit
+import scala.collection.SortedMap
class LoadAgentFixedModelCalculationSpec
extends ParticipantAgentSpec(
@@ -57,7 +58,7 @@ class LoadAgentFixedModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with LoadTestData
@@ -74,11 +75,12 @@ class LoadAgentFixedModelCalculationSpec
private val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0d))
+ LoadReference.ActivePower(Kilowatts(0d)),
)
private val defaultOutputConfig = NotifierConfig(
simonaConfig.simona.output.participant.defaultConfig.simulationResult,
- simonaConfig.simona.output.participant.defaultConfig.powerRequestReply
+ simonaConfig.simona.output.participant.defaultConfig.powerRequestReply,
+ simonaConfig.simona.output.participant.defaultConfig.flexResult,
)
private val loadConfigUtil = ConfigUtil.ParticipantConfigUtil(
simonaConfig.simona.runtime.participant
@@ -87,7 +89,7 @@ class LoadAgentFixedModelCalculationSpec
loadConfigUtil.getOrDefault[LoadRuntimeConfig](
voltageSensitiveInput.getUuid
)
- private val services = None
+ private val services = Iterable.empty
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
private implicit val powerTolerance: squants.Power = Watts(0.1)
@@ -97,7 +99,7 @@ class LoadAgentFixedModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
modelConfig = modelConfig,
@@ -108,7 +110,7 @@ class LoadAgentFixedModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -116,7 +118,7 @@ class LoadAgentFixedModelCalculationSpec
new FixedLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -136,7 +138,7 @@ class LoadAgentFixedModelCalculationSpec
new FixedLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -157,7 +159,8 @@ class LoadAgentFixedModelCalculationSpec
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
) =>
inputModel shouldBe SimpleInputContainer(voltageSensitiveInput)
modelConfig shouldBe modelConfig
@@ -167,12 +170,16 @@ class LoadAgentFixedModelCalculationSpec
resolution shouldBe this.resolution
requestVoltageDeviationThreshold shouldBe simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold
outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe None
case unsuitableStateData =>
fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
}
/* Refuse registration */
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a completion notification */
scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0)))
@@ -192,23 +199,22 @@ class LoadAgentFixedModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
) =>
/* Base state data */
startDate shouldBe simulationStartDate
endDate shouldBe simulationEndDate
- services shouldBe None
+ services shouldBe Iterable.empty
outputConfig shouldBe defaultOutputConfig
additionalActivationTicks shouldBe empty
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
resolution,
- Map(0L -> Each(1.0))
- )
- resultValueStore shouldBe ValueStore.forResult(
- resolution,
- 2
+ SortedMap(0L -> Each(1.0)),
)
+ resultValueStore shouldBe ValueStore(resolution)
requestValueStore shouldBe ValueStore[ApparentPower](
resolution
)
@@ -224,7 +230,7 @@ class LoadAgentFixedModelCalculationSpec
new FixedLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -232,7 +238,10 @@ class LoadAgentFixedModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -243,27 +252,27 @@ class LoadAgentFixedModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
0L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
)
inside(loadAgent.stateData) {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.requestValueStore shouldBe ValueStore[
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.requestValueStore shouldBe ValueStore[
ApparentPower
](
resolution,
- Map(
+ SortedMap(
0L -> ApparentPower(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
- )
+ ),
)
case _ =>
fail(
@@ -277,7 +286,7 @@ class LoadAgentFixedModelCalculationSpec
new FixedLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -285,7 +294,10 @@ class LoadAgentFixedModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I am not interested in the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -302,17 +314,13 @@ class LoadAgentFixedModelCalculationSpec
awaitAssert(loadAgent.stateName shouldBe Idle)
inside(loadAgent.stateData) {
- case participantModelBaseStateData: ParticipantModelBaseStateData[
- _,
- _,
- _
- ] =>
- participantModelBaseStateData.resultValueStore.last(0L) match {
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.resultValueStore.last(0L) match {
case Some((tick, entry)) =>
tick shouldBe 0L
inside(entry) { case ApparentPower(p, q) =>
- (p ~= Megawatts(268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(268.603e-6))
+ q should approximate(Megavars(0.0))
}
case None =>
fail("Result value store does not contain entry for tick 0.")
@@ -329,7 +337,7 @@ class LoadAgentFixedModelCalculationSpec
new FixedLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -338,7 +346,10 @@ class LoadAgentFixedModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0)))
@@ -353,13 +364,13 @@ class LoadAgentFixedModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
3000L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(268.603e-6))
+ q should approximate(Megavars(0.0))
}
}
@@ -367,7 +378,7 @@ class LoadAgentFixedModelCalculationSpec
new FixedLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -378,7 +389,10 @@ class LoadAgentFixedModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0)))
@@ -391,13 +405,13 @@ class LoadAgentFixedModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
3000L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(268.603e-6))
+ q should approximate(Megavars(0.0))
case answer => fail(s"Did not expect to get that answer: $answer")
}
}
@@ -408,14 +422,14 @@ class LoadAgentFixedModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
3000L,
Each(1.000000000000001d),
- Each(0d)
+ Each(0d),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(268.603e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(268.603e-6))
+ q should approximate(Megavars(0.0))
}
}
@@ -424,14 +438,14 @@ class LoadAgentFixedModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
3000L,
Each(0.98),
- Each(0d)
+ Each(0d),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(268.603e-6)) shouldBe true
- (q ~= Megavars(-22.07138e-6)) shouldBe true
+ p should approximate(Megawatts(268.603e-6))
+ q should approximate(Megavars(-22.07138e-6))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala
index ee17a58989..46df19190a 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/LoadAgentProfileModelCalculationSpec.scala
@@ -21,7 +21,7 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
ParticipantInitializeStateData,
ParticipantInitializingStateData,
ParticipantUninitializedStateData,
- SimpleInputContainer
+ SimpleInputContainer,
}
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
@@ -33,7 +33,7 @@ import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
@@ -48,6 +48,7 @@ import squants.Each
import squants.energy.{Kilowatts, Megawatts, Watts}
import java.util.concurrent.TimeUnit
+import scala.collection.SortedMap
class LoadAgentProfileModelCalculationSpec
extends ParticipantAgentSpec(
@@ -57,7 +58,7 @@ class LoadAgentProfileModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with LoadTestData
@@ -74,11 +75,12 @@ class LoadAgentProfileModelCalculationSpec
private val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.PROFILE,
- LoadReference.ActivePower(Kilowatts(0d))
+ LoadReference.ActivePower(Kilowatts(0d)),
)
private val defaultOutputConfig = NotifierConfig(
simonaConfig.simona.output.participant.defaultConfig.simulationResult,
- simonaConfig.simona.output.participant.defaultConfig.powerRequestReply
+ simonaConfig.simona.output.participant.defaultConfig.powerRequestReply,
+ simonaConfig.simona.output.participant.defaultConfig.flexResult,
)
private val loadConfigUtil = ConfigUtil.ParticipantConfigUtil(
simonaConfig.simona.runtime.participant
@@ -87,7 +89,7 @@ class LoadAgentProfileModelCalculationSpec
loadConfigUtil.getOrDefault[LoadRuntimeConfig](
voltageSensitiveInput.getUuid
)
- private val services = None
+ private val services = Iterable.empty
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
private implicit val powerTolerance: squants.Power = Watts(0.1)
@@ -97,7 +99,7 @@ class LoadAgentProfileModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
LoadInput,
LoadRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
modelConfig = modelConfig,
@@ -108,7 +110,7 @@ class LoadAgentProfileModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -116,7 +118,7 @@ class LoadAgentProfileModelCalculationSpec
new ProfileLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -136,7 +138,7 @@ class LoadAgentProfileModelCalculationSpec
new ProfileLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -156,7 +158,8 @@ class LoadAgentProfileModelCalculationSpec
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
) =>
inputModel shouldBe SimpleInputContainer(voltageSensitiveInput)
modelConfig shouldBe modelConfig
@@ -166,12 +169,16 @@ class LoadAgentProfileModelCalculationSpec
resolution shouldBe this.resolution
requestVoltageDeviationThreshold shouldBe simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold
outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe None
case unsuitableStateData =>
fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
}
/* Refuse registration */
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a completion notification */
scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0)))
@@ -191,24 +198,23 @@ class LoadAgentProfileModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
) =>
/* Base state data */
startDate shouldBe simulationStartDate
endDate shouldBe simulationEndDate
- services shouldBe None
+ services shouldBe Iterable.empty
outputConfig shouldBe defaultOutputConfig
additionalActivationTicks
.corresponds(Seq(900L, 1800L, 2700L, 3600L))(_ == _) shouldBe true
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
resolution,
- Map(0L -> Each(1.0))
- )
- resultValueStore shouldBe ValueStore.forResult(
- resolution,
- 2
+ SortedMap(0L -> Each(1.0)),
)
+ resultValueStore shouldBe ValueStore(resolution)
requestValueStore shouldBe ValueStore[ApparentPower](
resolution
)
@@ -224,7 +230,7 @@ class LoadAgentProfileModelCalculationSpec
new ProfileLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -232,7 +238,10 @@ class LoadAgentProfileModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -243,27 +252,27 @@ class LoadAgentProfileModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
0L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
)
inside(loadAgent.stateData) {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.requestValueStore shouldBe ValueStore[
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.requestValueStore shouldBe ValueStore[
ApparentPower
](
resolution,
- Map(
+ SortedMap(
0L -> ApparentPower(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
- )
+ ),
)
case _ =>
fail(
@@ -277,7 +286,7 @@ class LoadAgentProfileModelCalculationSpec
new ProfileLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -285,7 +294,10 @@ class LoadAgentProfileModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I am not interested in the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -302,17 +314,13 @@ class LoadAgentProfileModelCalculationSpec
awaitAssert(loadAgent.stateName shouldBe Idle)
inside(loadAgent.stateData) {
- case participantModelBaseStateData: ParticipantModelBaseStateData[
- _,
- _,
- _
- ] =>
- participantModelBaseStateData.resultValueStore.last(0L) match {
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.resultValueStore.last(0L) match {
case Some((tick, entry)) =>
tick shouldBe 0L
inside(entry) { case ApparentPower(p, q) =>
- (p ~= Megawatts(84.000938e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(84.000938e-6))
+ q should approximate(Megavars(0.0))
}
case None =>
fail("Result value store does not contain entry for tick 0.")
@@ -328,7 +336,7 @@ class LoadAgentProfileModelCalculationSpec
new ProfileLoadAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -338,7 +346,10 @@ class LoadAgentProfileModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(loadAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ loadAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(loadAgent.toTyped, Some(0)))
@@ -355,13 +366,13 @@ class LoadAgentProfileModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
1800L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(79.750890e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(79.750890e-6))
+ q should approximate(Megavars(0.0))
}
}
@@ -371,14 +382,14 @@ class LoadAgentProfileModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
1800L,
Each(1.000000000000001d),
- Each(0d)
+ Each(0d),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(79.750890e-6)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(79.750890e-6))
+ q should approximate(Megavars(0.0))
}
}
@@ -387,14 +398,14 @@ class LoadAgentProfileModelCalculationSpec
loadAgent ! RequestAssetPowerMessage(
1800L,
Each(0.98),
- Each(0)
+ Each(0),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(79.750890e-6)) shouldBe true
- (q ~= Megavars(-22.0714e-6)) shouldBe true
+ p should approximate(Megawatts(79.750890e-6))
+ q should approximate(Megavars(-22.0714e-6))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgent2ListenerSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgent2ListenerSpec.scala
index 1007da8e77..2db9a91fc0 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgent2ListenerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgent2ListenerSpec.scala
@@ -9,7 +9,7 @@ package edu.ie3.simona.agent.participant
import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.models.input.system.SystemParticipantInput
import edu.ie3.datamodel.models.result.system.SystemParticipantResult
-import edu.ie3.simona.agent.grid.GridAgent.FinishGridSimulationTrigger
+import edu.ie3.simona.agent.participant.ParticipantAgent.FinishParticipantSimulation
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPower
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.ParticipantInitializeStateData
import edu.ie3.simona.config.SimonaConfig
@@ -21,7 +21,7 @@ import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
@@ -52,7 +52,7 @@ class ParticipantAgent2ListenerSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="OFF"
- """.stripMargin)
+ """.stripMargin),
)
)
with DefaultTestData
@@ -63,7 +63,7 @@ class ParticipantAgent2ListenerSpec
implicit val noReceiveTimeOut: Timeout = Timeout(1, TimeUnit.SECONDS)
/* Assign this test to receive the result events from agent */
- override val systemListener: Iterable[ActorRef] = Vector(self)
+ override val systemListener: Iterable[ActorRef] = Iterable(self)
private val testUUID = UUID.randomUUID
private val testID = "PartAgentExternalMock"
@@ -72,50 +72,51 @@ class ParticipantAgent2ListenerSpec
private val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0d))
+ LoadReference.ActivePower(Kilowatts(0d)),
)
private val mockInputModel = mock[SystemParticipantInput]
when(mockInputModel.getUuid).thenReturn(testUUID)
when(mockInputModel.getId).thenReturn(testID)
- private val sources = None
+ private val services = Iterable.empty
"A participant agent" should {
val initStateData: NotifierConfig => ParticipantInitializeStateData[
SystemParticipantInput,
BaseRuntimeConfig,
- ApparentPower
+ ApparentPower,
] = outputConfig =>
ParticipantInitializeStateData[
SystemParticipantInput,
BaseRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = mockInputModel,
modelConfig = mock[BaseRuntimeConfig],
- secondaryDataServices = sources,
+ secondaryDataServices = services,
simulationStartDate = defaultSimulationStart,
simulationEndDate = defaultSimulationEnd,
resolution = simonaConfig.simona.powerflow.resolution.getSeconds,
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = outputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"inform listeners about new simulation results, when asked to do" in {
/* Let the agent send announcements, when there is anew request reply */
val outputConfig = NotifierConfig(
simulationResultInfo = true,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
initStateData = initStateData(outputConfig),
- listener = systemListener
+ listener = systemListener,
)
)
@@ -124,7 +125,10 @@ class ParticipantAgent2ListenerSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(mockAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ mockAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(mockAgent.toTyped))
@@ -142,11 +146,11 @@ class ParticipantAgent2ListenerSpec
) =>
systemParticipantResult.getP should equalWithTolerance(
Quantities.getQuantity(2, MEGAWATT),
- quantityTolerance
+ quantityTolerance,
)
systemParticipantResult.getQ should equalWithTolerance(
Quantities.getQuantity(1, MEGAVAR),
- quantityTolerance
+ quantityTolerance,
)
case _ => fail("Expected a SystemParticipantResult")
}
@@ -156,14 +160,15 @@ class ParticipantAgent2ListenerSpec
/* Let the agent send announcements, when there is anew request reply */
val outputConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
initStateData = initStateData(outputConfig),
- listener = systemListener
+ listener = systemListener,
)
)
@@ -172,7 +177,10 @@ class ParticipantAgent2ListenerSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(mockAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ mockAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(mockAgent.toTyped))
@@ -188,14 +196,15 @@ class ParticipantAgent2ListenerSpec
/* Let the agent send announcements, when there is anew request reply */
val outputConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = true
+ powerRequestReply = true,
+ flexResult = false,
)
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
initStateData = initStateData(outputConfig),
- listener = systemListener
+ listener = systemListener,
)
)
@@ -204,7 +213,10 @@ class ParticipantAgent2ListenerSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(mockAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ mockAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(mockAgent.toTyped))
@@ -217,7 +229,7 @@ class ParticipantAgent2ListenerSpec
mockAgent ! RequestAssetPowerMessage(
3000L,
Each(1d),
- Each(0d)
+ Each(0d),
)
/* Wait for original reply (this is the querying agent) */
@@ -229,7 +241,7 @@ class ParticipantAgent2ListenerSpec
case unknownMsg => fail(s"Received unexpected message: $unknownMsg")
}
- scheduler.send(mockAgent, FinishGridSimulationTrigger(3000L))
+ scheduler.send(mockAgent, FinishParticipantSimulation(3000L))
/* Wait for the result event (this is the event listener) */
logger.warn(
@@ -245,14 +257,15 @@ class ParticipantAgent2ListenerSpec
/* Let the agent send announcements, when there is anew request reply */
val outputConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
initStateData = initStateData(outputConfig),
- listener = systemListener
+ listener = systemListener,
)
)
@@ -261,7 +274,10 @@ class ParticipantAgent2ListenerSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(mockAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ mockAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
scheduler.expectMsg(Completion(mockAgent.toTyped))
@@ -275,7 +291,7 @@ class ParticipantAgent2ListenerSpec
mockAgent ! RequestAssetPowerMessage(
3000L,
Each(1d),
- Each(0d)
+ Each(0d),
)
/* Wait for original reply (this is the querying agent) */
@@ -287,7 +303,7 @@ class ParticipantAgent2ListenerSpec
case unknownMsg => fail(s"Received unexpected message: $unknownMsg")
}
- scheduler.send(mockAgent, FinishGridSimulationTrigger(3000L))
+ scheduler.send(mockAgent, FinishParticipantSimulation(3000L))
/* Make sure nothing else is sent */
expectNoMessage(noReceiveTimeOut.duration)
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentExternalSourceSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentExternalSourceSpec.scala
index bff18f7a87..784fde78dd 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentExternalSourceSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentExternalSourceSpec.scala
@@ -19,7 +19,7 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ActivePower,
ActivePowerAndHeat,
ApparentPower,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
}
import edu.ie3.simona.agent.participant.statedata.BaseStateData.FromOutsideBaseStateData
import edu.ie3.simona.agent.participant.statedata.DataCollectionStateData
@@ -27,7 +27,7 @@ import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
ParticipantInitializeStateData,
ParticipantInitializingStateData,
ParticipantUninitializedStateData,
- SimpleInputContainer
+ SimpleInputContainer,
}
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
@@ -35,13 +35,14 @@ import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.config.SimonaConfig.BaseRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.load.{LoadModelBehaviour, LoadReference}
import edu.ie3.simona.model.participant.{CalcRelevantData, SystemParticipant}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
@@ -55,13 +56,13 @@ import edu.ie3.util.scala.quantities.{Kilovars, Megavars, ReactivePower, Vars}
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.when
import org.scalatestplus.mockito.MockitoSugar
-import squants.Each
+import squants.{Each, Power}
import squants.energy.{Kilowatts, Megawatts, Watts}
import tech.units.indriya.quantity.Quantities
import java.util.UUID
import java.util.concurrent.TimeUnit
-import scala.collection.SortedSet
+import scala.collection.{SortedMap, SortedSet}
import scala.util.{Failure, Success}
/** Tests a mock participant agent with external data (primary data). Since
@@ -76,7 +77,7 @@ class ParticipantAgentExternalSourceSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with DefaultTestData
@@ -94,12 +95,14 @@ class ParticipantAgentExternalSourceSpec
when(mockInputModel.getId).thenReturn(testID)
when(mockInputModel.getNode).thenReturn(mockNode)
private val mockModel =
- mock[
- SystemParticipant[CalcRelevantData.FixedRelevantData.type, ApparentPower]
- ]
+ mock[SystemParticipant[
+ CalcRelevantData.FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ]]
when(mockModel.getUuid).thenReturn(testUUID)
- private val activeToReactivePowerFunction: squants.Power => ReactivePower =
- (p: squants.Power) => Kilovars(p.toKilowatts * tan(acos(0.9)))
+ private val activeToReactivePowerFunction: Power => ReactivePower =
+ (p: Power) => Kilovars(p.toKilowatts * tan(acos(0.9)))
when(
mockModel.activeToReactivePowerFunc(
any(classOf[squants.Dimensionless])
@@ -108,41 +111,42 @@ class ParticipantAgentExternalSourceSpec
private val simonaConfig: SimonaConfig = createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0.0))
+ LoadReference.ActivePower(Kilowatts(0.0)),
)
private val defaultOutputConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
- private implicit val powerTolerance: squants.Power = Watts(0.1)
+ private implicit val powerTolerance: Power = Watts(0.1)
private implicit val reactivePowerTolerance: ReactivePower = Vars(0.1)
"A participant agent with externally given data provider" should {
val initStateData = ParticipantInitializeStateData[
SystemParticipantInput,
BaseRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = mockInputModel,
modelConfig = mock[BaseRuntimeConfig],
- secondaryDataServices = None,
+ secondaryDataServices = Iterable.empty,
simulationStartDate = defaultSimulationStart,
simulationEndDate = defaultSimulationEnd,
resolution = simonaConfig.simona.powerflow.resolution.getSeconds,
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
@@ -161,7 +165,7 @@ class ParticipantAgentExternalSourceSpec
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
@@ -183,16 +187,18 @@ class ParticipantAgentExternalSourceSpec
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
) =>
inputModel shouldBe SimpleInputContainer(mockInputModel)
modelConfig shouldBe modelConfig
- secondaryDataServices shouldBe None
+ secondaryDataServices shouldBe Iterable.empty
simulationStartDate shouldBe defaultSimulationStart
simulationEndDate shouldBe defaultSimulationEnd
resolution shouldBe this.resolution
requestVoltageDeviationThreshold shouldBe simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold
outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe None
case unsuitableStateData =>
fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
}
@@ -200,7 +206,7 @@ class ParticipantAgentExternalSourceSpec
/* Reply, that registration was successful */
primaryServiceProxy.send(
mockAgent,
- RegistrationSuccessfulMessage(Some(4711L))
+ RegistrationSuccessfulMessage(primaryServiceProxy.ref, Some(4711L)),
)
scheduler.expectMsg(Completion(mockAgent.toTyped, Some(4711L)))
@@ -210,7 +216,8 @@ class ParticipantAgentExternalSourceSpec
mockAgent.stateData match {
case baseStateData: FromOutsideBaseStateData[SystemParticipant[
FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower] =>
/* Only check the awaited next data ticks, as the rest has yet been checked */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -227,7 +234,7 @@ class ParticipantAgentExternalSourceSpec
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
@@ -237,7 +244,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
primaryServiceProxy.send(
mockAgent,
- RegistrationSuccessfulMessage(Some(900L))
+ RegistrationSuccessfulMessage(primaryServiceProxy.ref, Some(900L)),
)
/* I'm not interested in the content of the CompletionMessage */
@@ -249,12 +256,12 @@ class ParticipantAgentExternalSourceSpec
mockAgent ! RequestAssetPowerMessage(
0L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
@@ -270,16 +277,16 @@ class ParticipantAgentExternalSourceSpec
_,
_,
_,
- requestValueStore
+ requestValueStore,
) =>
requestValueStore shouldBe ValueStore[ApparentPower](
resolution,
- Map(
+ SortedMap(
0L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
- )
+ ),
)
case _ =>
fail(
@@ -292,7 +299,7 @@ class ParticipantAgentExternalSourceSpec
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
@@ -302,7 +309,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
primaryServiceProxy.send(
mockAgent,
- RegistrationSuccessfulMessage(Some(900L))
+ RegistrationSuccessfulMessage(primaryServiceProxy.ref, Some(900L)),
)
/* I'm not interested in the content of the CompletionMessage */
@@ -314,12 +321,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent,
ProvidePrimaryDataMessage(
900L,
+ primaryServiceProxy.ref,
ApparentPower(
Kilowatts(0.0),
- Kilovars(900.0)
+ Kilovars(900.0),
),
- Some(1800L)
- )
+ Some(1800L),
+ ),
)
/* Find yourself in corresponding state and state data */
@@ -328,10 +336,11 @@ class ParticipantAgentExternalSourceSpec
case DataCollectionStateData(
baseStateData: FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -343,7 +352,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.ref -> Some(
ApparentPower(
Kilowatts(0.0),
- Kilovars(900.0)
+ Kilovars(900.0),
)
)
)
@@ -367,7 +376,8 @@ class ParticipantAgentExternalSourceSpec
mockAgent.stateData match {
case baseStateData: FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower] =>
/* The new data is apparent in the result value store */
baseStateData.resultValueStore match {
@@ -375,7 +385,7 @@ class ParticipantAgentExternalSourceSpec
store shouldBe Map(
900L -> ApparentPower(
Kilowatts(0.0),
- Kilovars(900.0)
+ Kilovars(900.0),
)
)
}
@@ -390,7 +400,7 @@ class ParticipantAgentExternalSourceSpec
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
@@ -400,7 +410,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
primaryServiceProxy.send(
mockAgent,
- RegistrationSuccessfulMessage(Some(900L))
+ RegistrationSuccessfulMessage(primaryServiceProxy.ref, Some(900L)),
)
/* I'm not interested in the content of the CompletionMessage */
@@ -416,10 +426,11 @@ class ParticipantAgentExternalSourceSpec
case DataCollectionStateData(
baseStateData: FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -442,12 +453,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent,
ProvidePrimaryDataMessage(
900L,
+ primaryServiceProxy.ref,
ApparentPower(
Kilowatts(0.0),
- Kilovars(900.0)
+ Kilovars(900.0),
),
- Some(1800L)
- )
+ Some(1800L),
+ ),
)
/* Expect confirmation */
@@ -458,7 +470,8 @@ class ParticipantAgentExternalSourceSpec
mockAgent.stateData match {
case baseStateData: FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower] =>
/* The new data is apparent in the result value store */
baseStateData.resultValueStore match {
@@ -466,7 +479,7 @@ class ParticipantAgentExternalSourceSpec
store shouldBe Map(
900L -> ApparentPower(
Kilowatts(0.0),
- Kilovars(900.0)
+ Kilovars(900.0),
)
)
}
@@ -481,7 +494,7 @@ class ParticipantAgentExternalSourceSpec
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
@@ -492,7 +505,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
primaryServiceProxy.send(
mockAgent,
- RegistrationSuccessfulMessage(Some(900L))
+ RegistrationSuccessfulMessage(primaryServiceProxy.ref, Some(900L)),
)
/* I'm not interested in the content of the CompletionMessage */
@@ -503,7 +516,7 @@ class ParticipantAgentExternalSourceSpec
mockAgent ! RequestAssetPowerMessage(
1800L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectNoMessage(noReceiveTimeOut.duration)
awaitAssert(mockAgent.stateName == Idle)
@@ -513,12 +526,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent,
ProvidePrimaryDataMessage(
900L,
+ primaryServiceProxy.ref,
ApparentPower(
Kilowatts(0.0),
- Kilovars(900.0)
+ Kilovars(900.0),
),
- Some(1800L)
- )
+ Some(1800L),
+ ),
)
/* Trigger the agent */
@@ -533,17 +547,19 @@ class ParticipantAgentExternalSourceSpec
val mockAgent = TestFSMRef(
new ParticipantAgentMock(
scheduler = scheduler.ref,
- initStateData = initStateData
+ initStateData = initStateData,
)
)
"correctly determine the reactive power function when trivial reactive power is requested" in {
val baseStateData: FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData.FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower] = FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData.FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower](
mockModel,
defaultSimulationStart,
@@ -555,24 +571,26 @@ class ParticipantAgentExternalSourceSpec
1e-4,
ValueStore.forVoltage(
900L,
- Each(1.0)
+ Each(1.0),
),
ValueStore.forResult(900L, 1L),
- ValueStore(900L)
+ ValueStore(900L),
)
val actualFunction =
mockAgent.underlyingActor.getReactivePowerFunction(0L, baseStateData)
- (actualFunction(Kilowatts(100.0)) ~= Kilovars(0.0)) shouldBe true
+ actualFunction(Kilowatts(100.0)) should approximate(Kilovars(0.0))
}
"correctly determine the reactive power function from model when requested" in {
val baseStateData: FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData.FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower] = FromOutsideBaseStateData[SystemParticipant[
CalcRelevantData.FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
], ApparentPower](
mockModel,
defaultSimulationStart,
@@ -584,15 +602,15 @@ class ParticipantAgentExternalSourceSpec
1e-4,
ValueStore.forVoltage(
900L,
- Each(1.0)
+ Each(1.0),
),
ValueStore.forResult(900L, 1L),
- ValueStore(900L)
+ ValueStore(900L),
)
val actualFunction =
mockAgent.underlyingActor.getReactivePowerFunction(0L, baseStateData)
- (actualFunction(Kilowatts(100.0)) ~= Kilovars(48.43221)) shouldBe true
+ actualFunction(Kilowatts(100.0)) should approximate(Kilovars(48.43221))
}
"provide correct average power after three data ticks are available" in {
@@ -603,7 +621,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
primaryServiceProxy.send(
mockAgent,
- RegistrationSuccessfulMessage(Some(900L))
+ RegistrationSuccessfulMessage(primaryServiceProxy.ref, Some(900L)),
)
/* I'm not interested in the content of the CompletionMessage */
@@ -616,12 +634,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent,
ProvidePrimaryDataMessage(
900L,
+ primaryServiceProxy.ref,
ApparentPower(
Kilowatts(100.0),
- Kilovars(33.0)
+ Kilovars(33.0),
),
- Some(1800L)
- )
+ Some(1800L),
+ ),
)
scheduler.send(mockAgent, Activation(900))
scheduler.expectMsg(Completion(mockAgent.toTyped, Some(1800)))
@@ -631,12 +650,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent,
ProvidePrimaryDataMessage(
1800L,
+ primaryServiceProxy.ref,
ApparentPower(
Kilowatts(150.0),
- Kilovars(49.0)
+ Kilovars(49.0),
),
- Some(2700L)
- )
+ Some(2700L),
+ ),
)
scheduler.send(mockAgent, Activation(1800))
scheduler.expectMsg(Completion(mockAgent.toTyped, Some(2700)))
@@ -646,12 +666,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent,
ProvidePrimaryDataMessage(
2700L,
+ primaryServiceProxy.ref,
ApparentPower(
Kilowatts(200.0),
- Kilovars(66.0)
+ Kilovars(66.0),
),
- None
- )
+ None,
+ ),
)
scheduler.send(mockAgent, Activation(2700))
scheduler.expectMsg(Completion(mockAgent.toTyped))
@@ -662,13 +683,13 @@ class ParticipantAgentExternalSourceSpec
mockAgent ! RequestAssetPowerMessage(
3000L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.095)) shouldBe true
- (q ~= Megavars(0.0312)) shouldBe true
+ p should approximate(Megawatts(0.095))
+ q should approximate(Megavars(0.0312))
}
}
@@ -678,14 +699,14 @@ class ParticipantAgentExternalSourceSpec
mockAgent ! RequestAssetPowerMessage(
3000L,
Each(1.000000000000001d),
- Each(0.0)
+ Each(0.0),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(0.095)) shouldBe true
- (q ~= Megavars(0.0312)) shouldBe true
+ p should approximate(Megawatts(0.095))
+ q should approximate(Megavars(0.0312))
}
}
@@ -695,14 +716,14 @@ class ParticipantAgentExternalSourceSpec
mockAgent ! RequestAssetPowerMessage(
3000L,
Each(0.98d),
- Each(0.0)
+ Each(0.0),
)
/* Expect, that nothing has changed, as this model is meant to forward information from outside */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(0.095)) shouldBe true
- (q ~= Megavars(0.0312)) shouldBe true
+ p should approximate(Megawatts(0.095))
+ q should approximate(Megavars(0.0312))
}
}
@@ -720,7 +741,7 @@ class ParticipantAgentExternalSourceSpec
ApparentPowerAndHeat(
Kilowatts(0.0),
Kilovars(0.0),
- Kilowatts(0.0)
+ Kilowatts(0.0),
)
)
)
@@ -741,7 +762,7 @@ class ParticipantAgentExternalSourceSpec
primaryServiceProxy.ref -> Some(
ActivePowerAndHeat(
Kilowatts(0.0),
- Kilowatts(0.0)
+ Kilowatts(0.0),
)
)
)
@@ -764,14 +785,14 @@ class ParticipantAgentExternalSourceSpec
participantAgent.prepareData(data, reactivePowerFunction) match {
case Success(ApparentPower(p, q)) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
case Success(value) =>
fail(s"Succeeded, but with wrong data: '$value'.")
case Failure(exception) =>
fail(
"Was meant to succeed, but failed with exception.",
- exception
+ exception,
)
}
}
@@ -785,17 +806,17 @@ class ParticipantAgentExternalSourceSpec
participantAgent.prepareData(
data,
- (p: squants.Power) => Kilovars(p.toKilowatts * tan(acos(0.9)))
+ (p: squants.Power) => Kilovars(p.toKilowatts * tan(acos(0.9))),
) match {
case Success(ApparentPower(p, q)) =>
- (p ~= Kilowatts(100.0)) shouldBe true
- (q ~= Kilovars(48.43221)) shouldBe true
+ p should approximate(Kilowatts(100.0))
+ q should approximate(Kilovars(48.43221))
case Success(value) =>
fail(s"Succeeded, but with wrong data: '$value'.")
case Failure(exception) =>
fail(
"Was meant to succeed, but failed with exception.",
- exception
+ exception,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentalsSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentalsSpec.scala
index 3d87971bca..02ba2dd0ad 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentalsSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentFundamentalsSpec.scala
@@ -24,10 +24,11 @@ import edu.ie3.simona.config.SimonaConfig.BaseRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.{
AgentInitializationException,
- InconsistentStateException
+ InconsistentStateException,
}
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
import edu.ie3.simona.model.participant.SystemParticipant
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl.CosPhiFixed
import edu.ie3.simona.model.participant.load.FixedLoadModel.FixedLoadRelevantData
import edu.ie3.simona.model.participant.load.{FixedLoadModel, LoadReference}
@@ -35,17 +36,17 @@ import edu.ie3.simona.test.common.AgentSpec
import edu.ie3.simona.test.common.model.participant.LoadTestData
import edu.ie3.util.TimeUtil
import edu.ie3.util.scala.OperationInterval
-import edu.ie3.util.scala.quantities.{Megavars, ReactivePower}
+import edu.ie3.util.scala.quantities.{Megavars, ReactivePower, Vars}
import org.mockito.Mockito.when
import org.scalatest.PrivateMethodTester
import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor3, TableFor5}
import org.scalatestplus.mockito.MockitoSugar
-import squants.Each
-import squants.energy.{Kilowatts, Megawatts}
+import squants.{Each, Power}
+import squants.energy.{Kilowatts, Megawatts, Watts}
import java.util.UUID
import java.util.concurrent.TimeUnit
-import scala.collection.SortedSet
+import scala.collection.{SortedMap, SortedSet}
class ParticipantAgentFundamentalsSpec
extends AgentSpec(
@@ -55,7 +56,7 @@ class ParticipantAgentFundamentalsSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with LoadTestData
@@ -64,13 +65,14 @@ class ParticipantAgentFundamentalsSpec
with MockitoSugar {
implicit val receiveTimeOut: Timeout = Timeout(10, TimeUnit.SECONDS)
implicit val noReceiveTimeOut: Timeout = Timeout(1, TimeUnit.SECONDS)
- implicit val pTolerance: squants.Power = Megawatts(0.001)
- implicit val qTolerance: ReactivePower = Megavars(0.001)
+ private implicit val pTolerance: Power = Watts(0.1)
+ private implicit val qTolerance: ReactivePower = Vars(0.1)
private val outputConfig: NotifierConfig =
NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
/* Get one instance of the mock for participant agent */
@@ -83,46 +85,46 @@ class ParticipantAgentFundamentalsSpec
initStateData = mock[ParticipantInitializeStateData[
SystemParticipantInput,
BaseRuntimeConfig,
- ApparentPower
- ]]
+ ApparentPower,
+ ]],
)
)
val mockAgent: ParticipantAgentMock = mockAgentTestRef.underlyingActor
- private val powerValues: Map[Long, ApparentPower] =
+ private val powerValues =
Map(
0L -> ApparentPower(
Megawatts(1.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1L -> ApparentPower(
Megawatts(2.0),
- Megavars(1.0)
+ Megavars(1.0),
),
3L -> ApparentPower(
Megawatts(3.0),
- Megavars(2.0)
+ Megavars(2.0),
),
4L -> ApparentPower(
Megawatts(5.0),
- Megavars(4.0)
+ Megavars(4.0),
),
7L -> ApparentPower(
Megawatts(3.0),
- Megavars(2.0)
+ Megavars(2.0),
),
8L -> ApparentPower(
Megawatts(6.0),
- Megavars(5.0)
+ Megavars(5.0),
),
9L -> ApparentPower(
Megawatts(6.0),
- Megavars(5.0)
+ Megavars(5.0),
),
10L -> ApparentPower(
Megawatts(4.0),
- Megavars(3.0)
- )
+ Megavars(3.0),
+ ),
)
/* Calculates the reactive power as the square of the active power */
@@ -159,21 +161,21 @@ class ParticipantAgentFundamentalsSpec
("2020-01-01 00:15:00", 900L, 0L),
("2020-01-01 00:15:10", 900L, 890L),
("2020-01-01 00:15:00", 1800L, 900L),
- ("2020-01-01 00:14:10", 1800L, 950L)
+ ("2020-01-01 00:14:10", 1800L, 950L),
)
forAll(testData) {
(
simulationStartString: String,
resolution: Long,
- expectedFirstTick: Long
+ expectedFirstTick: Long,
) =>
{
val simulationStart =
TimeUtil.withDefaults.toZonedDateTime(simulationStartString)
val firstTick = mockAgent.firstFullResolutionInSimulation(
simulationStart,
- resolution
+ resolution,
)
firstTick shouldBe expectedFirstTick
@@ -188,7 +190,7 @@ class ParticipantAgentFundamentalsSpec
"resolution",
"operationStart",
"operationEnd",
- "expectedTicks"
+ "expectedTicks",
),
("2020-01-01 00:00:00", 900L, 0L, 2700L, List(0L, 900L, 1800L, 2700L)),
("2020-01-01 00:15:00", 900L, 0L, 2700L, List(0L, 900L, 1800L, 2700L)),
@@ -199,8 +201,8 @@ class ParticipantAgentFundamentalsSpec
900L,
0L,
2880L,
- List(180L, 1080L, 1980L, 2880L)
- )
+ List(180L, 1080L, 1980L, 2880L),
+ ),
)
forAll(testData) {
@@ -209,7 +211,7 @@ class ParticipantAgentFundamentalsSpec
resolution: Long,
operationStart: Long,
operationEnd: Long,
- expectedTicks: List[Long]
+ expectedTicks: List[Long],
) =>
{
val simulationStart =
@@ -219,7 +221,7 @@ class ParticipantAgentFundamentalsSpec
simulationStart,
resolution,
operationStart,
- operationEnd
+ operationEnd,
)
additionalActivationTicks.corresponds(expectedTicks)(
@@ -234,7 +236,7 @@ class ParticipantAgentFundamentalsSpec
"bring up no activation trigger" in {
val baseStateData = ParticipantAgentFundamentalsSpec.mockBaseStateData(
SortedSet.empty,
- Map.empty
+ Map.empty,
)
mockAgent.popNextActivationTrigger(baseStateData) match {
@@ -251,8 +253,8 @@ class ParticipantAgentFundamentalsSpec
SortedSet(100L, 200L, 300L),
Map(
self -> Some(10L),
- noSender -> Some(0L)
- )
+ noSender -> Some(0L),
+ ),
)
mockAgent.popNextActivationTrigger(baseStateData) match {
@@ -270,8 +272,8 @@ class ParticipantAgentFundamentalsSpec
SortedSet(0L, 10L, 20L),
Map(
self -> Some(200L),
- noSender -> Some(100L)
- )
+ noSender -> Some(100L),
+ ),
)
mockAgent.popNextActivationTrigger(baseStateData) match {
@@ -292,8 +294,8 @@ class ParticipantAgentFundamentalsSpec
SortedSet(0L, 10L, 20L),
Map(
self -> Some(20L),
- noSender -> Some(0L)
- )
+ noSender -> Some(0L),
+ ),
)
mockAgent.popNextActivationTrigger(baseStateData) match {
@@ -316,12 +318,12 @@ class ParticipantAgentFundamentalsSpec
powerValues,
-10L,
5L,
- None
+ None,
)
apparentPower match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(0.8666666666666667)) shouldBe true
- (q ~= Megavars(0.5333333333333334)) shouldBe true
+ p should approximate(Megawatts(0.8666666666666667))
+ q should approximate(Megavars(0.5333333333333334))
}
}
@@ -331,12 +333,12 @@ class ParticipantAgentFundamentalsSpec
powerValues,
8L,
15L,
- None
+ None,
)
apparentPower match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(4.571428571428573)) shouldBe true
- (q ~= Megavars(3.571428571428571)) shouldBe true
+ p should approximate(Megawatts(4.571428571428573))
+ q should approximate(Megavars(3.571428571428571))
}
}
@@ -346,12 +348,12 @@ class ParticipantAgentFundamentalsSpec
powerValues,
8L,
15L,
- None
+ None,
)
apparentPower match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(4.571428571428573)) shouldBe true
- (q ~= Megavars(3.571428571428571)) shouldBe true
+ p should approximate(Megawatts(4.571428571428573))
+ q should approximate(Megavars(3.571428571428571))
}
}
@@ -361,12 +363,12 @@ class ParticipantAgentFundamentalsSpec
powerValues,
-10L,
5L,
- activeToReactivePowerFuncOpt
+ activeToReactivePowerFuncOpt,
)
apparentPower match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(0.8666666666666667)) shouldBe true
- (q ~= Megavars(2.8666666666666667)) shouldBe true
+ p should approximate(Megawatts(0.8666666666666667))
+ q should approximate(Megavars(2.8666666666666667))
}
}
@@ -376,12 +378,12 @@ class ParticipantAgentFundamentalsSpec
powerValues,
8L,
15L,
- activeToReactivePowerFuncOpt
+ activeToReactivePowerFuncOpt,
)
apparentPower match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(4.571428571428573)) shouldBe true
- (q ~= Megavars(21.71428571428571)) shouldBe true
+ p should approximate(Megawatts(4.571428571428573))
+ q should approximate(Megavars(21.71428571428571))
}
}
@@ -391,12 +393,12 @@ class ParticipantAgentFundamentalsSpec
powerValues,
8L,
15L,
- activeToReactivePowerFuncOpt
+ activeToReactivePowerFuncOpt,
)
apparentPower match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(4.571428571428573)) shouldBe true
- (q ~= Megavars(21.71428571428571)) shouldBe true
+ p should approximate(Megawatts(4.571428571428573))
+ q should approximate(Megavars(21.71428571428571))
}
}
}
@@ -406,47 +408,47 @@ class ParticipantAgentFundamentalsSpec
val requestTick = 1800L
val resultValueStore = ValueStore(
900,
- Map(
+ SortedMap(
800L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1000L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1200L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1400L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1600L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1800L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
- )
- )
+ Megavars(0.0),
+ ),
+ ),
)
val requestValueStore = ValueStore(
900,
- Map(
+ SortedMap(
900L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
- )
+ ),
)
mockAgent.getRelevantResultData(
requestTick,
resultValueStore,
- requestValueStore
+ requestValueStore,
) shouldBe Some(
RelevantResultValues(
900L,
@@ -454,29 +456,29 @@ class ParticipantAgentFundamentalsSpec
Map(
800L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1000L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1200L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1400L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1600L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
),
1800L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
- )
- )
+ Megavars(0.0),
+ ),
+ ),
)
)
}
@@ -485,27 +487,27 @@ class ParticipantAgentFundamentalsSpec
val requestTick = 1800L
val resultValueStore = ValueStore(
900,
- Map(
+ SortedMap(
800L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
- )
+ ),
)
val requestValueStore = ValueStore(
900,
- Map(
+ SortedMap(
900L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
- )
+ ),
)
mockAgent.getRelevantResultData(
requestTick,
resultValueStore,
- requestValueStore
+ requestValueStore,
) shouldBe Some(
RelevantResultValues(
900L,
@@ -513,9 +515,9 @@ class ParticipantAgentFundamentalsSpec
Map(
800L -> ApparentPower(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
- )
+ ),
)
)
}
@@ -526,7 +528,8 @@ class ParticipantAgentFundamentalsSpec
val baseStateData = ParticipantModelBaseStateData[
ApparentPower,
FixedLoadRelevantData.type,
- FixedLoadModel
+ ConstantState.type,
+ FixedLoadModel,
](
simulationStartDate,
simulationEndDate,
@@ -538,7 +541,7 @@ class ParticipantAgentFundamentalsSpec
CosPhiFixed(0.95),
Kilowatts(100.0),
0.95,
- LoadReference.ActivePower(Kilowatts(95.0))
+ LoadReference.ActivePower(Kilowatts(95.0)),
),
None,
outputConfig,
@@ -548,12 +551,14 @@ class ParticipantAgentFundamentalsSpec
ValueStore.forVoltage(901L, Each(1.0)),
ValueStore(901L),
ValueStore(901L),
- ValueStore(901L)
+ ValueStore(901L),
+ ValueStore(901L),
+ None,
)
ParticipantAgent.getAndCheckNodalVoltage(
baseStateData,
- 1000L
+ 1000L,
) shouldBe Each(1.0)
}
@@ -561,7 +566,8 @@ class ParticipantAgentFundamentalsSpec
val baseStateData = ParticipantModelBaseStateData[
ApparentPower,
FixedLoadRelevantData.type,
- FixedLoadModel
+ ConstantState.type,
+ FixedLoadModel,
](
simulationStartDate,
simulationEndDate,
@@ -573,7 +579,7 @@ class ParticipantAgentFundamentalsSpec
CosPhiFixed(0.95),
Kilowatts(100.0),
0.95,
- LoadReference.ActivePower(Kilowatts(95.0))
+ LoadReference.ActivePower(Kilowatts(95.0)),
),
None,
outputConfig,
@@ -583,7 +589,9 @@ class ParticipantAgentFundamentalsSpec
ValueStore(901L),
ValueStore(901L),
ValueStore(901L),
- ValueStore(901L)
+ ValueStore(901L),
+ ValueStore(901L),
+ None,
)
intercept[InconsistentStateException] {
@@ -607,20 +615,29 @@ case object ParticipantAgentFundamentalsSpec extends MockitoSugar {
*/
def mockBaseStateData(
additionalActivationTicks: SortedSet[Long],
- foreseenDataTicks: Map[ActorRef, Option[Long]]
+ foreseenDataTicks: Map[ActorRef, Option[Long]],
): ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ ConstantState.type,
+ SystemParticipant[FixedRelevantData.type, ApparentPower, ConstantState.type],
] = {
- val modelMock =
- mock[SystemParticipant[FixedRelevantData.type, ApparentPower]]
+ val modelMock = mock[SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ]]
when(modelMock.getUuid).thenReturn(UUID.randomUUID())
ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ ConstantState.type,
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
](
TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00"),
TimeUtil.withDefaults.toZonedDateTime("2020-01-01 23:59:00"),
@@ -628,7 +645,8 @@ case object ParticipantAgentFundamentalsSpec extends MockitoSugar {
None,
NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
),
additionalActivationTicks,
foreseenDataTicks,
@@ -636,7 +654,9 @@ case object ParticipantAgentFundamentalsSpec extends MockitoSugar {
ValueStore(0L),
ValueStore(0L),
ValueStore(0L),
- ValueStore(0L)
+ ValueStore(0L),
+ ValueStore(0L),
+ None,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala
index 2b0264258d..2b7b5f399e 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/ParticipantAgentMock.scala
@@ -7,24 +7,24 @@
package edu.ie3.simona.agent.participant
import org.apache.pekko.actor.{ActorRef, FSM, Props}
+import org.apache.pekko.actor.typed.{ActorRef => TypedActorRef}
import edu.ie3.datamodel.models.input.system.SystemParticipantInput
import edu.ie3.datamodel.models.result.system.SystemParticipantResult
import edu.ie3.simona.agent.ValueStore
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ApparentPower,
- ZERO_POWER
+ ZERO_POWER,
}
import edu.ie3.simona.agent.participant.data.Data.SecondaryData
import edu.ie3.simona.agent.participant.data.secondary.SecondaryDataService
import edu.ie3.simona.agent.participant.statedata.BaseStateData.ParticipantModelBaseStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
InputModelContainer,
- ParticipantInitializeStateData
+ ParticipantInitializeStateData,
}
import edu.ie3.simona.agent.participant.statedata.{
BaseStateData,
- DataCollectionStateData,
- ParticipantStateData
+ ParticipantStateData,
}
import edu.ie3.simona.agent.state.AgentState
import edu.ie3.simona.agent.state.AgentState.Idle
@@ -33,10 +33,17 @@ import edu.ie3.simona.config.SimonaConfig.BaseRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
import edu.ie3.simona.exceptions.agent.InvalidRequestException
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
-import edu.ie3.simona.model.participant.SystemParticipant
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl.CosPhiFixed
+import edu.ie3.simona.model.participant.{
+ CalcRelevantData,
+ FlexChangeIndicator,
+ ModelState,
+ SystemParticipant,
+}
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage.FlexResponse
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
-import edu.ie3.util.scala.quantities.{Megavars, ReactivePower}
+import edu.ie3.util.scala.quantities.{Kilovars, Megavars, ReactivePower}
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito
import org.mockito.Mockito.doReturn
@@ -59,24 +66,34 @@ class ParticipantAgentMock(
initStateData: ParticipantInitializeStateData[
SystemParticipantInput,
SimonaConfig.BaseRuntimeConfig,
- ApparentPower
+ ApparentPower,
],
- override val listener: Iterable[ActorRef] = Vector.empty[ActorRef]
+ override val listener: Iterable[ActorRef] = Iterable.empty[ActorRef],
) extends ParticipantAgent[
ApparentPower,
FixedRelevantData.type,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
SystemParticipantInput,
SimonaConfig.BaseRuntimeConfig,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
](scheduler, initStateData)
with ParticipantAgentFundamentals[
ApparentPower,
FixedRelevantData.type,
+ ConstantState.type,
ParticipantStateData[ApparentPower],
SystemParticipantInput,
SimonaConfig.BaseRuntimeConfig,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
] {
override protected val pdClassTag: ClassTag[ApparentPower] =
classTag[ApparentPower]
@@ -91,14 +108,20 @@ class ParticipantAgentMock(
ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ ConstantState.type,
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
],
- squants.Dimensionless
- ) => ApparentPower = (_, _, _) =>
+ ConstantState.type,
+ squants.Dimensionless,
+ ) => ApparentPower = (_, _, _, _) =>
// output different from default (0, 0)
ApparentPower(
Megawatts(2.0),
- Megavars(1.0)
+ Megavars(1.0),
)
/** Abstractly calculate the power output of the participant with all needed
@@ -108,8 +131,8 @@ class ParticipantAgentMock(
* secondary data is also put to storage. Actual implementation can be found
* in each participant's fundamentals.
*
- * @param collectionStateData
- * State data with collected, comprehensive secondary data.
+ * @param baseStateData
+ * The base state data with collected secondary data
* @param currentTick
* Tick, the trigger belongs to
* @param scheduler
@@ -118,9 +141,19 @@ class ParticipantAgentMock(
* [[Idle]] with updated result values
*/
override def calculatePowerWithSecondaryDataAndGoToIdle(
- collectionStateData: DataCollectionStateData[ApparentPower],
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
+ ],
+ modelState: ConstantState.type,
currentTick: Long,
- scheduler: ActorRef
+ scheduler: ActorRef,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] =
throw new InvalidRequestException(
"Request to calculate power with secondary data cannot be processed for this mock agent."
@@ -153,30 +186,45 @@ class ParticipantAgentMock(
override def determineModelBaseStateData(
inputModel: InputModelContainer[SystemParticipantInput],
modelConfig: SimonaConfig.BaseRuntimeConfig,
- services: Option[Vector[SecondaryDataService[_ <: SecondaryData]]],
+ services: Iterable[SecondaryDataService[_ <: SecondaryData]],
simulationStartDate: ZonedDateTime,
simulationEndDate: ZonedDateTime,
resolution: Long,
requestVoltageDeviationThreshold: Double,
- outputConfig: NotifierConfig
+ outputConfig: NotifierConfig,
+ maybeEmAgent: Option[TypedActorRef[FlexResponse]],
): ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ ConstantState.type,
+ SystemParticipant[FixedRelevantData.type, ApparentPower, ConstantState.type],
] = {
val func = CosPhiFixed(0.95).activeToReactivePowerFunc(
Kilowatts(0.0),
0.95d,
- Each(1.0)
+ Each(1.0),
)
- val participant: SystemParticipant[FixedRelevantData.type, ApparentPower] =
- mock[SystemParticipant[FixedRelevantData.type, ApparentPower]]
+ val participant: SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ] =
+ mock[SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ]]
doReturn(func).when(participant).activeToReactivePowerFunc(any())
ParticipantModelBaseStateData[
ApparentPower,
FixedRelevantData.type,
- SystemParticipant[FixedRelevantData.type, ApparentPower]
+ ConstantState.type,
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
](
simulationStartDate,
simulationEndDate,
@@ -188,11 +236,13 @@ class ParticipantAgentMock(
requestVoltageDeviationThreshold,
ValueStore.forVoltage(
resolution,
- Each(1.0)
+ Each(1.0),
),
ValueStore.forResult(resolution, 2),
ValueStore(resolution),
- ValueStore(resolution)
+ ValueStore(resolution),
+ ValueStore(resolution),
+ None,
)
}
@@ -212,21 +262,52 @@ class ParticipantAgentMock(
inputModel: InputModelContainer[SystemParticipantInput],
modelConfig: BaseRuntimeConfig,
simulationStartDate: ZonedDateTime,
- simulationEndDate: ZonedDateTime
+ simulationEndDate: ZonedDateTime,
): SystemParticipant[
FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
] = {
val mockModel =
mock[SystemParticipant[
FixedRelevantData.type,
- ApparentPower
+ ApparentPower,
+ ConstantState.type,
]]
val uuid = inputModel.electricalInputModel.getUuid
Mockito.when(mockModel.getUuid).thenReturn(uuid)
mockModel
}
+ override protected def createInitialState(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
+ ]
+ ): ModelState.ConstantState.type =
+ ConstantState
+
+ override protected def createCalcRelevantData(
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ FixedRelevantData.type,
+ ConstantState.type,
+ SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPower,
+ ConstantState.type,
+ ],
+ ],
+ tick: Long,
+ ): FixedRelevantData.type =
+ FixedRelevantData
+
/** To clean up agent value stores after power flow convergence. This is
* necessary for agents whose results are time dependent e.g. storage agents
* @param baseStateData
@@ -238,7 +319,7 @@ class ParticipantAgentMock(
*/
override def finalizeTickAfterPF(
baseStateData: BaseStateData[ApparentPower],
- currentTick: Long
+ currentTick: Long,
): FSM.State[AgentState, ParticipantStateData[ApparentPower]] =
goto(Idle) using baseStateData
@@ -261,14 +342,14 @@ class ParticipantAgentMock(
windowEnd: Long,
activeToReactivePowerFuncOpt: Option[
squants.Power => ReactivePower
- ] = None
+ ] = None,
): ApparentPower =
ParticipantAgentFundamentals.averageApparentPower(
tickToResults,
windowStart,
windowEnd,
activeToReactivePowerFuncOpt,
- log
+ log,
)
/** Determines the correct result.
@@ -285,14 +366,81 @@ class ParticipantAgentMock(
override protected def buildResult(
uuid: UUID,
dateTime: ZonedDateTime,
- result: ApparentPower
+ result: ApparentPower,
): SystemParticipantResult =
new SystemParticipantResult(
dateTime,
uuid,
result.p.toMegawatts.asMegaWatt,
- result.q.toMegavars.asMegaVar
+ result.q.toMegavars.asMegaVar,
) {}
+
+ /** Handle an active power change by flex control.
+ *
+ * @param tick
+ * Tick, in which control is issued
+ * @param baseStateData
+ * Base state data of the agent
+ * @param data
+ * Calculation relevant data
+ * @param lastState
+ * Last known model state
+ * @param setPower
+ * Setpoint active power
+ * @return
+ * Updated model state, a result model and a [[FlexChangeIndicator]]
+ */
+ override def handleControlledPowerChange(
+ tick: Long,
+ baseStateData: ParticipantModelBaseStateData[
+ ApparentPower,
+ CalcRelevantData.FixedRelevantData.type,
+ ModelState.ConstantState.type,
+ SystemParticipant[
+ CalcRelevantData.FixedRelevantData.type,
+ ApparentPower,
+ ModelState.ConstantState.type,
+ ],
+ ],
+ data: CalcRelevantData.FixedRelevantData.type,
+ lastState: ModelState.ConstantState.type,
+ setPower: squants.Power,
+ ): (ModelState.ConstantState.type, ApparentPower, FlexChangeIndicator) = (
+ ConstantState,
+ ApparentPower(
+ Kilowatts(0.0),
+ Kilovars(0.0),
+ ),
+ FlexChangeIndicator(),
+ )
+
+ /** Update the last known model state with the given external, relevant data
+ *
+ * @param tick
+ * Tick to update state for
+ * @param modelState
+ * Last known model state
+ * @param calcRelevantData
+ * Data, relevant for calculation
+ * @param nodalVoltage
+ * Current nodal voltage of the agent
+ * @param model
+ * Model for calculation
+ * @return
+ * The updated state at given tick under consideration of calculation
+ * relevant data
+ */
+ override protected def updateState(
+ tick: Long,
+ modelState: ModelState.ConstantState.type,
+ calcRelevantData: CalcRelevantData.FixedRelevantData.type,
+ nodalVoltage: squants.Dimensionless,
+ model: SystemParticipant[
+ CalcRelevantData.FixedRelevantData.type,
+ ApparentPower,
+ ModelState.ConstantState.type,
+ ],
+ ): ModelState.ConstantState.type = modelState
}
object ParticipantAgentMock {
@@ -301,13 +449,13 @@ object ParticipantAgentMock {
initStateData: ParticipantInitializeStateData[
SystemParticipantInput,
SimonaConfig.BaseRuntimeConfig,
- ApparentPower
- ]
+ ApparentPower,
+ ],
): Props =
Props(
new ParticipantAgentMock(
scheduler,
- initStateData
+ initStateData,
)
)
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/PvAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/PvAgentModelCalculationSpec.scala
index 3a0882ed1c..7e7e7d28f2 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/PvAgentModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/PvAgentModelCalculationSpec.scala
@@ -25,43 +25,43 @@ import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.config.SimonaConfig.PvRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
-import edu.ie3.simona.model.participant.PvModel.PvRelevantData
import edu.ie3.simona.model.participant.load.{LoadModelBehaviour, LoadReference}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.{
ProvideWeatherMessage,
RegisterForWeatherMessage,
- WeatherData
+ WeatherData,
}
import edu.ie3.simona.test.ParticipantAgentSpec
import edu.ie3.simona.test.common.input.PvInputTestData
import edu.ie3.simona.util.ConfigUtil
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
-import edu.ie3.simona.util.TickUtil.TickLong
+import edu.ie3.util.TimeUtil
import edu.ie3.util.scala.quantities.{
Megavars,
ReactivePower,
Vars,
- WattsPerSquareMeter
+ WattsPerSquareMeter,
}
-import org.scalatest.PrivateMethodTester
import squants.energy.{Kilowatts, Megawatts, Watts}
import squants.motion.MetersPerSecond
import squants.thermal.Celsius
import squants.{Each, Power}
+import java.time.ZonedDateTime
import java.util.concurrent.TimeUnit
+import scala.collection.SortedMap
class PvAgentModelCalculationSpec
extends ParticipantAgentSpec(
@@ -71,31 +71,37 @@ class PvAgentModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
- with PrivateMethodTester
with PvInputTestData {
+
+ private implicit val simulationStartDate: ZonedDateTime =
+ TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00")
+ private val simulationEndDate: ZonedDateTime =
+ TimeUtil.withDefaults.toZonedDateTime("2020-01-01 01:00:00")
+
implicit val receiveTimeOut: Timeout = Timeout(10, TimeUnit.SECONDS)
implicit val noReceiveTimeOut: Timeout = Timeout(1, TimeUnit.SECONDS)
/* Alter the input model to have a voltage sensitive reactive power calculation */
- val voltageSensitiveInput: PvInput = pvInputModel
+ val voltageSensitiveInput: PvInput = pvInput
.copy()
.qCharacteristics(new QV("qV:{(0.95,-0.625),(1.05,0.625)}"))
.build()
/* Assign this test to receive the result events from agent */
- override val systemListener: Iterable[ActorRef] = Vector(self)
+ override val systemListener: Iterable[ActorRef] = Iterable(self)
private val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0d))
+ LoadReference.ActivePower(Kilowatts(0d)),
)
private val defaultOutputConfig = NotifierConfig(
simonaConfig.simona.output.participant.defaultConfig.simulationResult,
- simonaConfig.simona.output.participant.defaultConfig.powerRequestReply
+ simonaConfig.simona.output.participant.defaultConfig.powerRequestReply,
+ simonaConfig.simona.output.participant.defaultConfig.flexResult,
)
private val configUtil = ConfigUtil.ParticipantConfigUtil(
simonaConfig.simona.runtime.participant
@@ -103,11 +109,9 @@ class PvAgentModelCalculationSpec
private val modelConfig = configUtil.getOrDefault[PvRuntimeConfig](
voltageSensitiveInput.getUuid
)
- private val noServices = None
- private val withServices = Some(
- Vector(
- ActorWeatherService(weatherService.ref)
- )
+ private val noServices = Iterable.empty
+ private val withServices = Iterable(
+ ActorWeatherService(weatherService.ref)
)
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
@@ -118,7 +122,7 @@ class PvAgentModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
PvInput,
PvRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
modelConfig = modelConfig,
@@ -129,7 +133,7 @@ class PvAgentModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -137,7 +141,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -159,7 +163,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -169,7 +173,10 @@ class PvAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
deathProbe.expectTerminated(pvAgent.ref)
}
@@ -179,7 +186,7 @@ class PvAgentModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
PvInput,
PvRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
modelConfig = modelConfig,
@@ -190,7 +197,7 @@ class PvAgentModelCalculationSpec
requestVoltageDeviationThreshold =
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
outputConfig = defaultOutputConfig,
- primaryServiceProxy = primaryServiceProxy.ref
+ primaryServiceProxy = primaryServiceProxy.ref,
)
"be instantiated correctly" in {
@@ -198,7 +205,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -218,7 +225,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -239,7 +246,8 @@ class PvAgentModelCalculationSpec
simulationEndDate,
resolution,
requestVoltageDeviationThreshold,
- outputConfig
+ outputConfig,
+ maybeEmAgent,
) =>
inputModel shouldBe SimpleInputContainer(voltageSensitiveInput)
modelConfig shouldBe modelConfig
@@ -249,12 +257,16 @@ class PvAgentModelCalculationSpec
resolution shouldBe this.resolution
requestVoltageDeviationThreshold shouldBe simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold
outputConfig shouldBe defaultOutputConfig
+ maybeEmAgent shouldBe None
case unsuitableStateData =>
fail(s"Agent has unsuitable state data '$unsuitableStateData'.")
}
/* Refuse registration */
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
weatherService.expectMsg(
@@ -277,34 +289,35 @@ class PvAgentModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
),
awaitRegistrationResponsesFrom,
- foreseenNextDataTicks
+ foreseenNextDataTicks,
) =>
/* Base state data */
startDate shouldBe simulationStartDate
endDate shouldBe simulationEndDate
- services shouldBe Some(
- Vector(
- ActorWeatherService(weatherService.ref)
- )
+ services shouldBe Iterable(
+ ActorWeatherService(weatherService.ref)
)
outputConfig shouldBe NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
additionalActivationTicks shouldBe empty
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
- resolution * 10,
- Map(0L -> Each(1.0))
+ resolution,
+ SortedMap(0L -> Each(1.0)),
)
- resultValueStore shouldBe ValueStore.forResult(resolution, 10)
- requestValueStore shouldBe ValueStore[ApparentPower](resolution * 10)
+ resultValueStore shouldBe ValueStore(resolution)
+ requestValueStore shouldBe ValueStore[ApparentPower](resolution)
/* Additional information */
- awaitRegistrationResponsesFrom shouldBe Vector(weatherService.ref)
+ awaitRegistrationResponsesFrom shouldBe Iterable(weatherService.ref)
foreseenNextDataTicks shouldBe Map.empty
case _ =>
fail(
@@ -313,7 +326,10 @@ class PvAgentModelCalculationSpec
}
/* Reply, that registration was successful */
- weatherService.send(pvAgent, RegistrationSuccessfulMessage(Some(4711L)))
+ weatherService.send(
+ pvAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(4711L)),
+ )
/* Expect a completion message */
scheduler.expectMsg(Completion(pvAgent.toTyped, Some(4711L)))
@@ -321,7 +337,7 @@ class PvAgentModelCalculationSpec
/* ... as well as corresponding state and state data */
pvAgent.stateName shouldBe Idle
pvAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* Only check the awaited next data ticks, as the rest has yet been checked */
baseStateData.foreseenDataTicks shouldBe Map(
weatherService.ref -> Some(4711L)
@@ -338,7 +354,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -346,13 +362,19 @@ class PvAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
weatherService.expectMsg(
RegisterForWeatherMessage(52.02083574, 7.40110716)
)
- weatherService.send(pvAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ pvAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -363,27 +385,27 @@ class PvAgentModelCalculationSpec
pvAgent ! RequestAssetPowerMessage(
0L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
)
inside(pvAgent.stateData) {
- case modelBaseStateData: ParticipantModelBaseStateData[_, _, _] =>
- modelBaseStateData.requestValueStore shouldBe ValueStore[
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
+ baseStateData.requestValueStore shouldBe ValueStore[
ApparentPower
](
- resolution * 10,
- Map(
+ resolution,
+ SortedMap(
0L -> ApparentPower(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
- )
+ ),
)
case _ =>
fail(
@@ -397,7 +419,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -405,11 +427,17 @@ class PvAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(pvAgent, RegistrationSuccessfulMessage(Some(0L)))
+ weatherService.send(
+ pvAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(0L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -421,21 +449,21 @@ class PvAgentModelCalculationSpec
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ MetersPerSecond(7.726576d),
)
weatherService.send(
pvAgent,
- ProvideWeatherMessage(0L, weatherData, Some(3600L))
+ ProvideWeatherMessage(0L, weatherService.ref, weatherData, Some(3600L)),
)
/* Find yourself in corresponding state and state data */
pvAgent.stateName shouldBe HandleInformation
pvAgent.stateData match {
case DataCollectionStateData(
- baseStateData: ParticipantModelBaseStateData[_, _, _],
+ baseStateData: ParticipantModelBaseStateData[_, _, _, _],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -464,17 +492,12 @@ class PvAgentModelCalculationSpec
pvAgent.stateName shouldBe Idle
pvAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
+ baseStateData.receivedSecondaryDataStore match {
case ValueStore(_, store) =>
store shouldBe Map(
- 0L -> PvRelevantData(
- 0L.toDateTime,
- 3600L,
- weatherData.diffIrr,
- weatherData.dirIrr
- )
+ 0L -> Map(weatherService.ref -> weatherData)
)
}
@@ -484,11 +507,11 @@ class PvAgentModelCalculationSpec
store.size shouldBe 1
store.getOrElse(
0L,
- fail("Expected a simulation result for tick 900.")
+ fail("Expected a simulation result for tick 900."),
) match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
case _ =>
@@ -503,7 +526,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -511,11 +534,17 @@ class PvAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(pvAgent, RegistrationSuccessfulMessage(Some(0L)))
+ weatherService.send(
+ pvAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(0L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -528,9 +557,9 @@ class PvAgentModelCalculationSpec
pvAgent.stateName shouldBe HandleInformation
pvAgent.stateData match {
case DataCollectionStateData(
- baseStateData: ParticipantModelBaseStateData[_, _, _],
+ baseStateData: ParticipantModelBaseStateData[_, _, _, _],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -553,12 +582,12 @@ class PvAgentModelCalculationSpec
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ MetersPerSecond(7.726576d),
)
weatherService.send(
pvAgent,
- ProvideWeatherMessage(0L, weatherData, Some(3600L))
+ ProvideWeatherMessage(0L, weatherService.ref, weatherData, Some(3600L)),
)
/* Expect confirmation */
@@ -567,17 +596,12 @@ class PvAgentModelCalculationSpec
/* Expect the state change to idle with updated base state data */
pvAgent.stateName shouldBe Idle
pvAgent.stateData match {
- case baseStateData: ParticipantModelBaseStateData[_, _, _] =>
+ case baseStateData: ParticipantModelBaseStateData[_, _, _, _] =>
/* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
+ baseStateData.receivedSecondaryDataStore match {
case ValueStore(_, store) =>
store shouldBe Map(
- 0L -> PvRelevantData(
- 0L.toDateTime,
- 3600L,
- weatherData.diffIrr,
- weatherData.dirIrr
- )
+ 0L -> Map(weatherService.ref -> weatherData)
)
}
@@ -587,11 +611,11 @@ class PvAgentModelCalculationSpec
store.size shouldBe 1
store.getOrElse(
0L,
- fail("Expected a simulation result for tick 0.")
+ fail("Expected a simulation result for tick 0."),
) match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
case _ =>
@@ -606,7 +630,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -615,11 +639,17 @@ class PvAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(pvAgent, RegistrationSuccessfulMessage(Some(3600L)))
+ weatherService.send(
+ pvAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(3600L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -629,7 +659,7 @@ class PvAgentModelCalculationSpec
pvAgent ! RequestAssetPowerMessage(
7200L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectNoMessage(noReceiveTimeOut.duration)
awaitAssert(pvAgent.stateName == Idle)
@@ -639,11 +669,16 @@ class PvAgentModelCalculationSpec
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ MetersPerSecond(7.726576d),
)
weatherService.send(
pvAgent,
- ProvideWeatherMessage(3600L, weatherData, Some(7200L))
+ ProvideWeatherMessage(
+ 3600L,
+ weatherService.ref,
+ weatherData,
+ Some(7200L),
+ ),
)
/* Trigger the agent */
@@ -656,8 +691,8 @@ class PvAgentModelCalculationSpec
/* Appreciate the answer to my previous request */
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
@@ -665,7 +700,7 @@ class PvAgentModelCalculationSpec
new PvAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -675,11 +710,17 @@ class PvAgentModelCalculationSpec
/* Refuse registration with primary service */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(pvAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ pvAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(pvAgent, RegistrationSuccessfulMessage(Some(0L)))
+ weatherService.send(
+ pvAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(0L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -691,14 +732,15 @@ class PvAgentModelCalculationSpec
pvAgent,
ProvideWeatherMessage(
0L,
+ weatherService.ref,
WeatherData(
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ MetersPerSecond(7.726576d),
),
- Some(3600L)
- )
+ Some(3600L),
+ ),
)
scheduler.send(pvAgent, Activation(0))
scheduler.expectMsg(Completion(pvAgent.toTyped, Some(3600)))
@@ -708,14 +750,15 @@ class PvAgentModelCalculationSpec
pvAgent,
ProvideWeatherMessage(
3600L,
+ weatherService.ref,
WeatherData(
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ MetersPerSecond(7.726576d),
),
- Some(7200L)
- )
+ Some(7200L),
+ ),
)
scheduler.send(pvAgent, Activation(3600))
scheduler.expectMsg(Completion(pvAgent.toTyped, Some(7200)))
@@ -725,14 +768,15 @@ class PvAgentModelCalculationSpec
pvAgent,
ProvideWeatherMessage(
7200L,
+ weatherService.ref,
WeatherData(
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(1.815d),
- MetersPerSecond(7.726576d)
+ MetersPerSecond(7.726576d),
),
- None
- )
+ None,
+ ),
)
scheduler.send(pvAgent, Activation(7200))
scheduler.expectMsg(Completion(pvAgent.toTyped))
@@ -741,13 +785,13 @@ class PvAgentModelCalculationSpec
pvAgent ! RequestAssetPowerMessage(
7500L,
Each(1d),
- Each(0d)
+ Each(0d),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
case answer => fail(s"Did not expect to get that answer: $answer")
}
}
@@ -758,14 +802,14 @@ class PvAgentModelCalculationSpec
pvAgent ! RequestAssetPowerMessage(
7500L,
Each(1.000000000000001d),
- Each(0d)
+ Each(0d),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
@@ -774,14 +818,14 @@ class PvAgentModelCalculationSpec
pvAgent ! RequestAssetPowerMessage(
7500L,
Each(0.98),
- Each(0d)
+ Each(0d),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgClass(classOf[AssetPowerChangedMessage]) match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(-780.6e-6)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(-780.6e-6))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/RichValueSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/RichValueSpec.scala
index 5cf286ba8c..5710c15417 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/RichValueSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/RichValueSpec.scala
@@ -12,7 +12,7 @@ import edu.ie3.datamodel.models.value.{
HeatDemandValue,
PValue,
SValue,
- Value
+ Value,
}
import edu.ie3.simona.agent.participant.data.Data.PrimaryData
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
@@ -20,7 +20,7 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ActivePowerAndHeat,
ApparentPower,
ApparentPowerAndHeat,
- RichValue
+ RichValue,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.quantities.PowerSystemUnits
@@ -54,32 +54,32 @@ class RichValueSpec extends UnitSpec with TableDrivenPropertyChecks {
new PValue(null),
new HeatAndPValue(
null,
- Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT)
+ Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT),
),
new HeatAndPValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
- null
+ null,
),
new SValue(null, Quantities.getQuantity(25d, PowerSystemUnits.KILOVAR)),
new SValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
- null
+ null,
),
new HeatAndSValue(
null,
Quantities.getQuantity(25d, PowerSystemUnits.KILOVAR),
- Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT)
+ Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT),
),
new HeatAndSValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
null,
- Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT)
+ Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT),
),
new HeatAndSValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
Quantities.getQuantity(25d, PowerSystemUnits.KILOVAR),
- null
- )
+ null,
+ ),
)
forAll(table)({ maliciousValue: Value =>
@@ -99,40 +99,40 @@ class RichValueSpec extends UnitSpec with TableDrivenPropertyChecks {
("value", "primaryData"),
(
new PValue(Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT)),
- ActivePower(Kilowatts(50d))
+ ActivePower(Kilowatts(50d)),
),
(
new HeatAndPValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
- Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT)
+ Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT),
),
ActivePowerAndHeat(
Kilowatts(50d),
- Kilowatts(12.5d)
- )
+ Kilowatts(12.5d),
+ ),
),
(
new SValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
- Quantities.getQuantity(25d, PowerSystemUnits.KILOVAR)
+ Quantities.getQuantity(25d, PowerSystemUnits.KILOVAR),
),
ApparentPower(
Kilowatts(50d),
- Kilovars(25d)
- )
+ Kilovars(25d),
+ ),
),
(
new HeatAndSValue(
Quantities.getQuantity(50d, PowerSystemUnits.KILOWATT),
Quantities.getQuantity(25d, PowerSystemUnits.KILOVAR),
- Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT)
+ Quantities.getQuantity(12.5, PowerSystemUnits.KILOWATT),
),
ApparentPowerAndHeat(
Kilowatts(50d),
Kilovars(25d),
- Kilowatts(12.5)
- )
- )
+ Kilowatts(12.5),
+ ),
+ ),
)
forAll(table)({ case (value: Value, primaryData: PrimaryData) =>
@@ -142,7 +142,7 @@ class RichValueSpec extends UnitSpec with TableDrivenPropertyChecks {
case Failure(exception) =>
fail(
s"Conversion of '$value' to primary data should succeed, but failed.",
- exception
+ exception,
)
}
})
diff --git a/src/test/scala/edu/ie3/simona/agent/participant/WecAgentModelCalculationSpec.scala b/src/test/scala/edu/ie3/simona/agent/participant/WecAgentModelCalculationSpec.scala
index 24ccac5f34..4e2151087f 100644
--- a/src/test/scala/edu/ie3/simona/agent/participant/WecAgentModelCalculationSpec.scala
+++ b/src/test/scala/edu/ie3/simona/agent/participant/WecAgentModelCalculationSpec.scala
@@ -21,7 +21,7 @@ import edu.ie3.simona.agent.participant.statedata.DataCollectionStateData
import edu.ie3.simona.agent.participant.statedata.ParticipantStateData.{
CollectRegistrationConfirmMessages,
ParticipantInitializeStateData,
- ParticipantUninitializedStateData
+ ParticipantUninitializedStateData,
}
import edu.ie3.simona.agent.participant.wec.WecAgent
import edu.ie3.simona.agent.state.AgentState.{Idle, Uninitialized}
@@ -29,6 +29,7 @@ import edu.ie3.simona.agent.state.ParticipantAgentState.HandleInformation
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.config.SimonaConfig.WecRuntimeConfig
import edu.ie3.simona.event.notifier.NotifierConfig
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.WecModel
import edu.ie3.simona.model.participant.WecModel.WecRelevantData
import edu.ie3.simona.model.participant.load.{LoadModelBehaviour, LoadReference}
@@ -36,18 +37,18 @@ import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.PowerMessage.{
AssetPowerChangedMessage,
AssetPowerUnchangedMessage,
- RequestAssetPowerMessage
+ RequestAssetPowerMessage,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.ontology.messages.services.WeatherMessage.{
ProvideWeatherMessage,
RegisterForWeatherMessage,
- WeatherData
+ WeatherData,
}
import edu.ie3.simona.test.ParticipantAgentSpec
import edu.ie3.simona.test.common.input.WecInputTestData
@@ -58,7 +59,7 @@ import edu.ie3.util.scala.quantities.{
Megavars,
ReactivePower,
Vars,
- WattsPerSquareMeter
+ WattsPerSquareMeter,
}
import org.scalatest.PrivateMethodTester
import squants.Each
@@ -68,6 +69,7 @@ import squants.thermal.Celsius
import java.time.ZonedDateTime
import java.util.concurrent.TimeUnit
+import scala.collection.SortedMap
class WecAgentModelCalculationSpec
extends ParticipantAgentSpec(
@@ -77,7 +79,7 @@ class WecAgentModelCalculationSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.event.slf4j.Slf4jLogger"]
|pekko.loglevel="DEBUG"
- """.stripMargin)
+ """.stripMargin),
)
)
with PrivateMethodTester
@@ -97,12 +99,12 @@ class WecAgentModelCalculationSpec
.build()
/* Assign this test to receive the result events from agent */
- override val systemListener: Iterable[ActorRef] = Vector(self)
+ override val systemListener: Iterable[ActorRef] = Iterable(self)
private val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0d))
+ LoadReference.ActivePower(Kilowatts(0d)),
)
private val configUtil = ConfigUtil.ParticipantConfigUtil(
simonaConfig.simona.runtime.participant
@@ -112,9 +114,7 @@ class WecAgentModelCalculationSpec
voltageSensitiveInput.getUuid
)
- private val withServices = Some(
- Vector(ActorWeatherService(weatherService.ref))
- )
+ private val withServices = Iterable(ActorWeatherService(weatherService.ref))
private val resolution = simonaConfig.simona.powerflow.resolution.getSeconds
@@ -125,7 +125,7 @@ class WecAgentModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
WecInput,
WecRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
simulationStartDate = simulationStartDate,
@@ -135,11 +135,12 @@ class WecAgentModelCalculationSpec
simonaConfig.simona.runtime.participant.requestVoltageDeviationThreshold,
modelConfig = modelConfig,
primaryServiceProxy = primaryServiceProxy.ref,
- secondaryDataServices = None,
+ secondaryDataServices = Iterable.empty,
outputConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
- )
+ powerRequestReply = false,
+ flexResult = false,
+ ),
)
"be instantiated correctly" in {
@@ -147,7 +148,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -169,7 +170,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -179,7 +180,10 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
deathProbe.expectTerminated(wecAgent.ref)
}
@@ -189,7 +193,7 @@ class WecAgentModelCalculationSpec
val initStateData = ParticipantInitializeStateData[
WecInput,
WecRuntimeConfig,
- ApparentPower
+ ApparentPower,
](
inputModel = voltageSensitiveInput,
modelConfig = modelConfig,
@@ -202,8 +206,9 @@ class WecAgentModelCalculationSpec
secondaryDataServices = withServices,
outputConfig = NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
- )
+ powerRequestReply = false,
+ flexResult = false,
+ ),
)
"be instantiated correctly" in {
@@ -211,7 +216,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -231,7 +236,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -239,7 +244,10 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
weatherService.expectMsg(RegisterForWeatherMessage(51.4843281, 7.4116482))
@@ -260,10 +268,12 @@ class WecAgentModelCalculationSpec
voltageValueStore,
resultValueStore,
requestValueStore,
- _
+ _,
+ _,
+ _,
),
awaitRegistrationResponsesFrom,
- foreseenNextDataTicks
+ foreseenNextDataTicks,
) =>
/* Base state data */
startDate shouldBe simulationStartDate
@@ -271,19 +281,20 @@ class WecAgentModelCalculationSpec
services shouldBe withServices
outputConfig shouldBe NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
additionalActivationTicks shouldBe empty
foreseenDataTicks shouldBe Map.empty
voltageValueStore shouldBe ValueStore(
- resolution * 10,
- Map(0L -> Each(1.0))
+ resolution,
+ SortedMap(0L -> Each(1.0)),
)
- resultValueStore shouldBe ValueStore.forResult(resolution, 10)
- requestValueStore shouldBe ValueStore[ApparentPower](resolution * 10)
+ resultValueStore shouldBe ValueStore(resolution)
+ requestValueStore shouldBe ValueStore[ApparentPower](resolution)
/* Additional information */
- awaitRegistrationResponsesFrom shouldBe Vector(weatherService.ref)
+ awaitRegistrationResponsesFrom shouldBe Iterable(weatherService.ref)
foreseenNextDataTicks shouldBe Map.empty
case _ =>
fail(
@@ -292,7 +303,10 @@ class WecAgentModelCalculationSpec
}
/* Reply, that registration was successful */
- weatherService.send(wecAgent, RegistrationSuccessfulMessage(Some(4711L)))
+ weatherService.send(
+ wecAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(4711L)),
+ )
/* Expect a completion message */
scheduler.expectMsg(Completion(wecAgent.toTyped, Some(4711L)))
@@ -303,7 +317,8 @@ class WecAgentModelCalculationSpec
case baseStateData: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
] =>
/* Only check the awaited next data ticks, as the rest has yet been checked */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -321,7 +336,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -329,11 +344,17 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* Expect a registration message */
weatherService.expectMsg(RegisterForWeatherMessage(51.4843281, 7.4116482))
- weatherService.send(wecAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ wecAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -344,12 +365,12 @@ class WecAgentModelCalculationSpec
wecAgent ! RequestAssetPowerMessage(
0L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsg(
AssetPowerChangedMessage(
Megawatts(0.0),
- Megavars(0.0)
+ Megavars(0.0),
)
)
@@ -357,18 +378,19 @@ class WecAgentModelCalculationSpec
case modelBaseStateData: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
] =>
modelBaseStateData.requestValueStore shouldBe ValueStore[
ApparentPower
](
- resolution * 10,
- Map(
+ resolution,
+ SortedMap(
0L -> ApparentPower(
Megawatts(0d),
- Megavars(0d)
+ Megavars(0d),
)
- )
+ ),
)
case _ =>
fail(
@@ -382,7 +404,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -390,11 +412,17 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(wecAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ wecAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -406,12 +434,17 @@ class WecAgentModelCalculationSpec
WattsPerSquareMeter(50d),
WattsPerSquareMeter(100d),
Celsius(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
)
weatherService.send(
wecAgent,
- ProvideWeatherMessage(900L, weatherData, Some(1800L))
+ ProvideWeatherMessage(
+ 900L,
+ weatherService.ref,
+ weatherData,
+ Some(1800L),
+ ),
)
/* Find yourself in corresponding state and state data */
@@ -421,10 +454,11 @@ class WecAgentModelCalculationSpec
baseStateData: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -456,17 +490,14 @@ class WecAgentModelCalculationSpec
case baseStateData: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
] =>
/* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
+ baseStateData.receivedSecondaryDataStore match {
case ValueStore(_, store) =>
store shouldBe Map(
- 900L -> WecRelevantData(
- weatherData.windVel,
- weatherData.temp,
- None
- )
+ 900L -> Map(weatherService.ref -> weatherData)
)
}
@@ -476,11 +507,11 @@ class WecAgentModelCalculationSpec
store.size shouldBe 1
store.getOrElse(
900L,
- fail("Expected a simulation result for tick 900.")
+ fail("Expected a simulation result for tick 900."),
) match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
case _ =>
@@ -495,7 +526,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -503,11 +534,17 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(wecAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ wecAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -523,10 +560,11 @@ class WecAgentModelCalculationSpec
baseStateData: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
],
expectedSenders,
- isYetTriggered
+ isYetTriggered,
) =>
/* The next data tick is already registered */
baseStateData.foreseenDataTicks shouldBe Map(
@@ -549,12 +587,17 @@ class WecAgentModelCalculationSpec
WattsPerSquareMeter(50d),
WattsPerSquareMeter(100d),
Celsius(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
)
weatherService.send(
wecAgent,
- ProvideWeatherMessage(900L, weatherData, Some(1800L))
+ ProvideWeatherMessage(
+ 900L,
+ weatherService.ref,
+ weatherData,
+ Some(1800L),
+ ),
)
/* Expect confirmation */
@@ -566,17 +609,14 @@ class WecAgentModelCalculationSpec
case baseStateData: ParticipantModelBaseStateData[
ApparentPower,
WecRelevantData,
- WecModel
+ ConstantState.type,
+ WecModel,
] =>
/* The store for calculation relevant data has been extended */
- baseStateData.calcRelevantDateStore match {
+ baseStateData.receivedSecondaryDataStore match {
case ValueStore(_, store) =>
store shouldBe Map(
- 900L -> WecRelevantData(
- weatherData.windVel,
- weatherData.temp,
- None
- )
+ 900L -> Map(weatherService.ref -> weatherData)
)
}
@@ -586,11 +626,11 @@ class WecAgentModelCalculationSpec
store.size shouldBe 1
store.getOrElse(
900L,
- fail("Expected a simulation result for tick 900.")
+ fail("Expected a simulation result for tick 900."),
) match {
case ApparentPower(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
case _ =>
@@ -605,7 +645,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -614,11 +654,17 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(wecAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ wecAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -628,7 +674,7 @@ class WecAgentModelCalculationSpec
wecAgent ! RequestAssetPowerMessage(
1800L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectNoMessage(noReceiveTimeOut.duration)
awaitAssert(wecAgent.stateName == Idle)
@@ -638,11 +684,16 @@ class WecAgentModelCalculationSpec
WattsPerSquareMeter(50d),
WattsPerSquareMeter(100d),
Celsius(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
)
weatherService.send(
wecAgent,
- ProvideWeatherMessage(900L, weatherData, Some(1800L))
+ ProvideWeatherMessage(
+ 900L,
+ weatherService.ref,
+ weatherData,
+ Some(1800L),
+ ),
)
/* Trigger the agent */
@@ -655,8 +706,8 @@ class WecAgentModelCalculationSpec
/* Appreciate the answer to my previous request */
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
@@ -664,7 +715,7 @@ class WecAgentModelCalculationSpec
new WecAgent(
scheduler = scheduler.ref,
initStateData = initStateData,
- listener = systemListener
+ listener = systemListener,
)
)
@@ -674,11 +725,17 @@ class WecAgentModelCalculationSpec
/* Agent attempts to register with primary data service -- refuse this */
primaryServiceProxy.expectMsgType[PrimaryServiceRegistrationMessage]
- primaryServiceProxy.send(wecAgent, RegistrationFailedMessage)
+ primaryServiceProxy.send(
+ wecAgent,
+ RegistrationFailedMessage(primaryServiceProxy.ref),
+ )
/* I'm not interested in the content of the RegistrationMessage */
weatherService.expectMsgType[RegisterForWeatherMessage]
- weatherService.send(wecAgent, RegistrationSuccessfulMessage(Some(900L)))
+ weatherService.send(
+ wecAgent,
+ RegistrationSuccessfulMessage(weatherService.ref, Some(900L)),
+ )
/* I'm not interested in the content of the CompletionMessage */
scheduler.expectMsgType[Completion]
@@ -690,14 +747,15 @@ class WecAgentModelCalculationSpec
wecAgent,
ProvideWeatherMessage(
900L,
+ weatherService.ref,
WeatherData(
WattsPerSquareMeter(50d),
WattsPerSquareMeter(100d),
Celsius(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
),
- Some(1800L)
- )
+ Some(1800L),
+ ),
)
scheduler.send(wecAgent, Activation(900))
scheduler.expectMsg(Completion(wecAgent.toTyped, Some(1800)))
@@ -707,14 +765,15 @@ class WecAgentModelCalculationSpec
wecAgent,
ProvideWeatherMessage(
1800L,
+ weatherService.ref,
WeatherData(
WattsPerSquareMeter(50d),
WattsPerSquareMeter(100d),
Celsius(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
),
- Some(2700L)
- )
+ Some(2700L),
+ ),
)
scheduler.send(wecAgent, Activation(1800))
scheduler.expectMsg(Completion(wecAgent.toTyped, Some(2700)))
@@ -724,14 +783,15 @@ class WecAgentModelCalculationSpec
wecAgent,
ProvideWeatherMessage(
2700L,
+ weatherService.ref,
WeatherData(
WattsPerSquareMeter(50d),
WattsPerSquareMeter(100d),
Celsius(0d),
- MetersPerSecond(0d)
+ MetersPerSecond(0d),
),
- None
- )
+ None,
+ ),
)
scheduler.send(wecAgent, Activation(2700))
scheduler.expectMsg(Completion(wecAgent.toTyped))
@@ -740,13 +800,13 @@ class WecAgentModelCalculationSpec
wecAgent ! RequestAssetPowerMessage(
3000L,
Each(1.0),
- Each(0.0)
+ Each(0.0),
)
expectMsgType[AssetPowerChangedMessage] match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
case answer => fail(s"Did not expect to get that answer: $answer")
}
}
@@ -757,14 +817,14 @@ class WecAgentModelCalculationSpec
wecAgent ! RequestAssetPowerMessage(
3000L,
Each(1.000000000000001d),
- Each(0.0)
+ Each(0.0),
)
/* Expect, that nothing has changed */
expectMsgType[AssetPowerUnchangedMessage] match {
case AssetPowerUnchangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(0.0)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(0.0))
}
}
@@ -773,14 +833,14 @@ class WecAgentModelCalculationSpec
wecAgent ! RequestAssetPowerMessage(
3000L,
Each(0.98d),
- Each(0.0)
+ Each(0.0),
)
/* Expect, the correct values (this model has fixed power factor) */
expectMsgClass(classOf[AssetPowerChangedMessage]) match {
case AssetPowerChangedMessage(p, q) =>
- (p ~= Megawatts(0.0)) shouldBe true
- (q ~= Megavars(-156.1249e-3)) shouldBe true
+ p should approximate(Megawatts(0.0))
+ q should approximate(Megavars(-156.1249e-3))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/api/ExtSimAdapterSpec.scala b/src/test/scala/edu/ie3/simona/api/ExtSimAdapterSpec.scala
index 54dcd9f061..0a4dab2d51 100644
--- a/src/test/scala/edu/ie3/simona/api/ExtSimAdapterSpec.scala
+++ b/src/test/scala/edu/ie3/simona/api/ExtSimAdapterSpec.scala
@@ -6,27 +6,28 @@
package edu.ie3.simona.api
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.actor.{ActorSystem, Terminated}
-import org.apache.pekko.testkit.{TestActorRef, TestProbe}
import com.typesafe.config.ConfigFactory
+import edu.ie3.simona.api.ExtSimAdapter.Stop
import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage
import edu.ie3.simona.api.simulation.ExtSimAdapterData
import edu.ie3.simona.api.simulation.ontology.{
ActivationMessage,
TerminationCompleted,
TerminationMessage,
- CompletionMessage => ExtCompletionMessage
+ CompletionMessage => ExtCompletionMessage,
}
+import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.ScheduleServiceActivation
-import edu.ie3.simona.ontology.messages.{Activation, StopMessage}
import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.test.common.{TestKitWithShutdown, TestSpawnerClassic}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.actor.{ActorSystem, Terminated}
+import org.apache.pekko.testkit.{TestActorRef, TestProbe}
import org.scalatest.wordspec.AnyWordSpecLike
import java.util.UUID
@@ -41,7 +42,7 @@ class ExtSimAdapterSpec
.parseString("""
|pekko.loggers = ["org.apache.pekko.testkit.TestEventListener"]
|pekko.loglevel = "INFO"
- |""".stripMargin)
+ |""".stripMargin),
)
)
with AnyWordSpecLike
@@ -89,7 +90,7 @@ class ExtSimAdapterSpec
awaitCond(
!extData.receiveMessageQueue.isEmpty,
max = 3.seconds,
- message = "No message received"
+ message = "No message received",
)
extData.receiveMessageQueue.size() shouldBe 1
extData.receiveMessageQueue.take() shouldBe new ActivationMessage(
@@ -129,7 +130,7 @@ class ExtSimAdapterSpec
awaitCond(
!extData.receiveMessageQueue.isEmpty,
max = 3.seconds,
- message = "No message received"
+ message = "No message received",
)
extData.receiveMessageQueue.size() shouldBe 1
extData.receiveMessageQueue.take()
@@ -164,12 +165,12 @@ class ExtSimAdapterSpec
val stopWatcher = TestProbe()
stopWatcher.watch(extSimAdapter)
- extSimAdapter ! StopMessage(simSuccessful)
+ extSimAdapter ! Stop(simSuccessful)
awaitCond(
!extData.receiveMessageQueue.isEmpty,
max = 3.seconds,
- message = "No message received"
+ message = "No message received",
)
extData.receiveMessageQueue.size() shouldBe 1
extData.receiveMessageQueue.take() shouldBe new TerminationMessage(
diff --git a/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala b/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala
index 33f9914a0f..3b587fac30 100644
--- a/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala
+++ b/src/test/scala/edu/ie3/simona/config/ConfigFailFastSpec.scala
@@ -12,7 +12,11 @@ import edu.ie3.simona.config.SimonaConfig.Simona.Output.Sink
import edu.ie3.simona.config.SimonaConfig.Simona.Output.Sink.{Csv, InfluxDb1x}
import edu.ie3.simona.config.SimonaConfig.Simona.Powerflow.Newtonraphson
import edu.ie3.simona.config.SimonaConfig.Simona.{Powerflow, Time}
-import edu.ie3.simona.config.SimonaConfig.{BaseCsvParams, ResultKafkaParams}
+import edu.ie3.simona.config.SimonaConfig.{
+ BaseCsvParams,
+ ResultKafkaParams,
+ TransformerControlGroup,
+}
import edu.ie3.simona.exceptions.InvalidConfigParameterException
import edu.ie3.simona.test.common.{ConfigTestData, UnitSpec}
import edu.ie3.simona.util.ConfigUtil.{CsvConfigUtil, NotifierIdentifier}
@@ -33,7 +37,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
new Time(
"2020-06-18 13:41:00",
None,
- "2020-05-18 13:41:00"
+ "2020-05-18 13:41:00",
)
)
}
@@ -45,7 +49,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
new Time(
"2020-06-18 13:41:00",
None,
- "2020-07-18 13:41:00"
+ "2020-07-18 13:41:00",
)
)
}.getMessage shouldBe "Invalid time configuration." +
@@ -88,11 +92,11 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
10,
new Newtonraphson(
List(10, 30),
- 100
+ 100,
),
Duration.of(3600, ChronoUnit.SECONDS),
stopOnFailure = false,
- Duration.of(3600, ChronoUnit.SECONDS)
+ Duration.of(3600, ChronoUnit.SECONDS),
)
)
}
@@ -105,11 +109,11 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
10,
new Newtonraphson(
List(10, 30),
- 100
+ 100,
),
resolution = Duration.of(3600, ChronoUnit.NANOS),
stopOnFailure = false,
- sweepTimeout = Duration.of(3600, ChronoUnit.SECONDS)
+ sweepTimeout = Duration.of(3600, ChronoUnit.SECONDS),
)
)
}.getMessage shouldBe "Invalid time resolution. Please ensure, that the time resolution for power flow calculation is at least rounded to a full second!"
@@ -450,7 +454,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
ConfigFailFast invokePrivate checkBaseRuntimeConfigs(
simonaConfig.simona.runtime.participant.load.defaultConfig,
simonaConfig.simona.runtime.participant.load.individualConfigs,
- defaultString
+ defaultString,
)
}.getMessage shouldBe "There has to be at least one identifier for each participant."
}
@@ -476,7 +480,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
ConfigFailFast invokePrivate checkBaseRuntimeConfigs(
simonaConfig.simona.runtime.participant.load.defaultConfig,
simonaConfig.simona.runtime.participant.load.individualConfigs,
- defaultString
+ defaultString,
)
}.getMessage shouldBe "Found invalid UUID 'blabla' it was meant to be the string 'default' or a valid UUID."
}
@@ -502,7 +506,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
ConfigFailFast invokePrivate checkBaseRuntimeConfigs(
simonaConfig.simona.runtime.participant.load.defaultConfig,
simonaConfig.simona.runtime.participant.load.individualConfigs,
- defaultString
+ defaultString,
)
}.getMessage shouldBe s"Found invalid UUID 'blabla' it was meant to be the string 'default' or a valid UUID."
}
@@ -528,7 +532,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
ConfigFailFast invokePrivate checkBaseRuntimeConfigs(
simonaConfig.simona.runtime.participant.load.defaultConfig,
simonaConfig.simona.runtime.participant.load.individualConfigs,
- defaultString
+ defaultString,
)
}.getMessage shouldBe "The scaling factor for system participants with UUID '49f250fa-41ff-4434-a083-79c98d260a76' may not be negative."
}
@@ -569,7 +573,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
ConfigFailFast invokePrivate checkBaseRuntimeConfigs(
simonaConfig.simona.runtime.participant.load.defaultConfig,
simonaConfig.simona.runtime.participant.load.individualConfigs,
- defaultString
+ defaultString,
)
}.getMessage shouldBe "The basic model configurations contain ambiguous definitions."
}
@@ -687,18 +691,21 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "pv",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "chp",
powerRequestReply = true,
- simulationResult = false
- )
+ simulationResult = false,
+ flexResult = false,
+ ),
)
noException shouldBe thrownBy {
@@ -713,18 +720,21 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "pv",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = false,
- simulationResult = true
- )
+ simulationResult = true,
+ flexResult = false,
+ ),
)
intercept[InvalidConfigParameterException](
@@ -751,7 +761,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
Sink(
Some(Csv("", "", "", isHierarchic = false)),
Some(InfluxDb1x("", 0, "")),
- None
+ None,
)
)
}.getLocalizedMessage shouldBe "Multiple sink configurations are not supported! Please ensure that only " +
@@ -779,9 +789,9 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
0,
"00000000-0000-0000-0000-000000000000",
"https://reg:123",
- "topic"
+ "topic",
)
- )
+ ),
)
)
}.getMessage shouldBe "Connection with kafka broker localhost:12345 failed."
@@ -796,7 +806,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
intercept[InvalidConfigParameterException] {
CsvConfigUtil.checkBaseCsvParams(
csvParams,
- "CsvGridData"
+ "CsvGridData",
)
}.getMessage shouldBe "The csvSep parameter '\t' for 'CsvGridData' configuration is invalid! Please choose between ';' or ','!"
}
@@ -806,7 +816,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
intercept[InvalidConfigParameterException] {
CsvConfigUtil.checkBaseCsvParams(
csvParams,
- "CsvGridData"
+ "CsvGridData",
)
}.getMessage shouldBe "The provided directoryPath for .csv-files '' for 'CsvGridData' configuration is invalid! Please correct the path!"
}
@@ -818,7 +828,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
intercept[InvalidConfigParameterException] {
CsvConfigUtil.checkBaseCsvParams(
csvParams,
- "CsvGridData"
+ "CsvGridData",
)
}.getMessage shouldBe "The provided directoryPath for .csv-files 'somewhere/else' for 'CsvGridData' configuration is invalid! Please correct the path!"
}
@@ -827,13 +837,13 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
val csvParams = BaseCsvParams(
",",
"inputData/common/pekko.conf",
- isHierarchic = false
+ isHierarchic = false,
)
intercept[InvalidConfigParameterException] {
CsvConfigUtil.checkBaseCsvParams(
csvParams,
- "CsvGridData"
+ "CsvGridData",
)
}.getMessage shouldBe "The provided directoryPath for .csv-files 'inputData/common/pekko.conf' for 'CsvGridData' configuration is invalid! Please correct the path!"
}
@@ -844,7 +854,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
noException shouldBe thrownBy {
CsvConfigUtil.checkBaseCsvParams(
csvParams,
- "CsvGridData"
+ "CsvGridData",
)
}
}
@@ -857,7 +867,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
Some(
BaseCsvParams(",", "inputData/vn_simona", isHierarchic = false)
),
- id = ""
+ id = "",
)
intercept[InvalidConfigParameterException] {
@@ -868,7 +878,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
"identify unsupported id" in {
val gridDataSource = SimonaConfig.Simona.Input.Grid.Datasource(
None,
- id = "someWhereUndefined"
+ id = "someWhereUndefined",
)
intercept[InvalidConfigParameterException] {
@@ -879,7 +889,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
"identify missing csv parameters" in {
val gridDataSource = SimonaConfig.Simona.Input.Grid.Datasource(
None,
- id = "csv"
+ id = "csv",
)
intercept[InvalidConfigParameterException] {
@@ -894,10 +904,10 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
BaseCsvParams(
",",
"input/samples/vn_simona",
- isHierarchic = false
+ isHierarchic = false,
)
),
- id = "csv"
+ id = "csv",
)
noException shouldBe thrownBy {
@@ -923,7 +933,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
SimonaConfig.Simona.Input.Weather.Datasource.CoordinateSource
.SampleParams(true)
),
- None
+ None,
),
None,
None,
@@ -935,7 +945,7 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
),
"this won't work",
None,
- Some("yyyy-MM-dd HH:mm")
+ Some("yyyy-MM-dd HH:mm"),
)
intercept[InvalidConfigParameterException] {
ConfigFailFast invokePrivate checkWeatherDataSource(
@@ -943,7 +953,78 @@ class ConfigFailFastSpec extends UnitSpec with ConfigTestData {
)
}.getMessage shouldBe "The weather data scheme 'this won't work' is not supported. Supported schemes:\n\ticon\n\tcosmo"
}
+ }
+
+ "checking the transformer control groups" should {
+ val checkTransformerControl =
+ PrivateMethod[Unit](Symbol("checkTransformerControl"))
+
+ "throw an exception, if the measurements are empty" in {
+ val dut = TransformerControlGroup(
+ List.empty,
+ List("a16cf7ca-8bbf-46e1-a74e-ffa6513c89a8"),
+ 1.02,
+ 0.98,
+ )
+
+ intercept[InvalidConfigParameterException] {
+ ConfigFailFast invokePrivate checkTransformerControl(dut)
+ }.getMessage shouldBe s"A transformer control group (${dut.toString}) cannot have no measurements assigned."
+ }
+
+ "throw an exception, if the transformers are empty" in {
+ val dut = TransformerControlGroup(
+ List("6888c53a-7629-4563-ac8e-840f80b03106"),
+ List.empty,
+ 1.02,
+ 0.98,
+ )
+ intercept[InvalidConfigParameterException] {
+ ConfigFailFast invokePrivate checkTransformerControl(dut)
+ }.getMessage shouldBe s"A transformer control group (${dut.toString}) cannot have no transformers assigned."
+ }
+
+ "throw an exception, if vMax is smaller than vMin" in {
+ val dut = TransformerControlGroup(
+ List("6888c53a-7629-4563-ac8e-840f80b03106"),
+ List("a16cf7ca-8bbf-46e1-a74e-ffa6513c89a8"),
+ 0.98,
+ 1.02,
+ )
+
+ intercept[InvalidConfigParameterException] {
+ ConfigFailFast invokePrivate checkTransformerControl(dut)
+ }.getMessage shouldBe s"The minimum permissible voltage magnitude of a transformer control group (${dut.toString}) must be smaller than the maximum permissible voltage magnitude."
+ }
+
+ "throw Exception if vMin is lower than -20% of nominal Voltage" in {
+ val dut = TransformerControlGroup(
+ List("6888c53a-7629-4563-ac8e-840f80b03106"),
+ List("a16cf7ca-8bbf-46e1-a74e-ffa6513c89a8"),
+ 1.02,
+ 0.79,
+ )
+
+ intercept[InvalidConfigParameterException] {
+ ConfigFailFast invokePrivate checkTransformerControl(dut)
+ }.getMessage shouldBe s"A control group (${dut.toString}) which control boundaries exceed the limit of +- 20% of nominal voltage! This may be caused " +
+ "by invalid parametrization of one control groups where vMin is lower than the lower boundary (0.8 of nominal Voltage)!"
+ }
+
+ "throw Exception if vMax is higher than +20% of nominal Voltage" in {
+ val dut = TransformerControlGroup(
+ List("6888c53a-7629-4563-ac8e-840f80b03106"),
+ List("a16cf7ca-8bbf-46e1-a74e-ffa6513c89a8"),
+ 1.21,
+ 0.98,
+ )
+
+ intercept[InvalidConfigParameterException] {
+ ConfigFailFast invokePrivate checkTransformerControl(dut)
+ }.getMessage shouldBe s"A control group (${dut.toString}) which control boundaries exceed the limit of +- 20% of nominal voltage! This may be caused " +
+ "by invalid parametrization of one control groups where vMax is higher than the upper boundary (1.2 of nominal Voltage)!"
+ }
}
}
diff --git a/src/test/scala/edu/ie3/simona/config/RefSystemParserSpec.scala b/src/test/scala/edu/ie3/simona/config/RefSystemParserSpec.scala
index be5d9b95c4..13dfc0c5d1 100644
--- a/src/test/scala/edu/ie3/simona/config/RefSystemParserSpec.scala
+++ b/src/test/scala/edu/ie3/simona/config/RefSystemParserSpec.scala
@@ -24,7 +24,7 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
+ ),
),
new RefSystemConfig(
gridIds = Some(List("100")),
@@ -33,16 +33,16 @@ class RefSystemParserSpec extends UnitSpec {
voltLvls = Some(
List(
VoltLvlConfig("HS", "110 kV"),
- VoltLvlConfig("HoeS", "380 kV")
+ VoltLvlConfig("HoeS", "380 kV"),
)
- )
+ ),
),
new RefSystemConfig(
gridIds = None,
sNom = "5000 MVA",
vNom = "110 kV",
- voltLvls = None
- )
+ voltLvls = None,
+ ),
)
val configRefSystems = RefSystemParser.parse(validRefSystems)
@@ -71,7 +71,7 @@ class RefSystemParserSpec extends UnitSpec {
18 -> configRefSystemOne,
19 -> configRefSystemOne,
20 -> configRefSystemOne,
- 100 -> configRefSystemTwo
+ 100 -> configRefSystemTwo,
)
// check internal voltLvLRefSystems
@@ -81,7 +81,7 @@ class RefSystemParserSpec extends UnitSpec {
GermanVoltageLevelUtils.MV_10KV -> configRefSystemOne,
GermanVoltageLevelUtils.MV_20KV -> configRefSystemOne,
GermanVoltageLevelUtils.HV -> configRefSystemTwo,
- GermanVoltageLevelUtils.EHV_380KV -> configRefSystemTwo
+ GermanVoltageLevelUtils.EHV_380KV -> configRefSystemTwo,
)
}
@@ -96,7 +96,7 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
+ ),
)
)
intercept[InvalidConfigParameterException] {
@@ -115,7 +115,7 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
+ ),
),
new RefSystemConfig(
gridIds = None,
@@ -123,8 +123,8 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
- )
+ ),
+ ),
)
intercept[InvalidConfigParameterException] {
RefSystemParser.parse(validRefSystems)
@@ -142,7 +142,7 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
+ ),
),
new RefSystemConfig(
gridIds = None,
@@ -150,8 +150,8 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
- )
+ ),
+ ),
)
intercept[InvalidConfigParameterException] {
RefSystemParser.parse(validRefSystems)
@@ -171,7 +171,7 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "10 kV",
voltLvls = Some(
List(VoltLvlConfig("MS", "10 kV"), VoltLvlConfig("MS", "20 kV"))
- )
+ ),
),
new RefSystemConfig(
gridIds = Some(List("100")),
@@ -179,8 +179,8 @@ class RefSystemParserSpec extends UnitSpec {
vNom = "110 kV",
voltLvls = Some(
List(VoltLvlConfig("HS", "110 kV"), VoltLvlConfig("HoeS", "380 kV"))
- )
- )
+ ),
+ ),
)
val configRefSystems = RefSystemParser.parse(validRefSystems)
@@ -199,7 +199,7 @@ class RefSystemParserSpec extends UnitSpec {
configRefSystems.find(
1,
- Some(GermanVoltageLevelUtils.MV_10KV)
+ Some(GermanVoltageLevelUtils.MV_10KV),
) shouldBe Some(
configRefSystemOne
)
@@ -210,7 +210,7 @@ class RefSystemParserSpec extends UnitSpec {
configRefSystems.find(
1000,
- Some(GermanVoltageLevelUtils.HV)
+ Some(GermanVoltageLevelUtils.HV),
) shouldBe Some(
configRefSystemTwo
)
@@ -227,7 +227,7 @@ class RefSystemParserSpec extends UnitSpec {
configRefSystems.find(
1000,
- Some(GermanVoltageLevelUtils.EHV_220KV)
+ Some(GermanVoltageLevelUtils.EHV_220KV),
) shouldBe None
}
diff --git a/src/test/scala/edu/ie3/simona/deploy/DeploySpec.scala b/src/test/scala/edu/ie3/simona/deploy/DeploySpec.scala
index f329122b50..82f2c349db 100644
--- a/src/test/scala/edu/ie3/simona/deploy/DeploySpec.scala
+++ b/src/test/scala/edu/ie3/simona/deploy/DeploySpec.scala
@@ -77,7 +77,7 @@ class DeploySpec extends UnitSpec {
case Failure(exception) =>
fail(
s"Cannot build class from execution class '$classString' in run-simona-cmd.sh!",
- exception
+ exception,
)
case Success(clazz) => clazz
}
diff --git a/src/test/scala/edu/ie3/simona/event/NotifierSpec.scala b/src/test/scala/edu/ie3/simona/event/NotifierSpec.scala
index a654ec289a..23c9062a79 100644
--- a/src/test/scala/edu/ie3/simona/event/NotifierSpec.scala
+++ b/src/test/scala/edu/ie3/simona/event/NotifierSpec.scala
@@ -7,7 +7,13 @@
package edu.ie3.simona.event
import java.util.{Calendar, Date}
-import org.apache.pekko.actor.{ActorLogging, ActorRef, ActorSystem, Props}
+import org.apache.pekko.actor.{
+ Actor,
+ ActorLogging,
+ ActorRef,
+ ActorSystem,
+ Props,
+}
import org.apache.pekko.testkit.ImplicitSender
import org.apache.pekko.util.Timeout
import com.typesafe.config.ConfigFactory
@@ -18,7 +24,6 @@ import edu.ie3.simona.test.common.TestKitWithShutdown
import edu.ie3.simona.util.ConfigUtil.NotifierIdentifier._
import edu.ie3.simona.util.EntityMapperUtil
import org.scalatest.matchers.should.Matchers
-import org.scalatest.wordspec.AnyWordSpecLike
import scala.concurrent.duration._
import scala.language.postfixOps
@@ -31,7 +36,7 @@ class NotifierSpec
.parseString("""
|pekko.loggers =["org.apache.pekko.testkit.TestEventListener"]
|pekko.loglevel="OFF"
- |""".stripMargin)
+ |""".stripMargin),
)
)
with Matchers
@@ -40,6 +45,7 @@ class NotifierSpec
// test listenerActor
class NotifierActor(override val listener: Iterable[ActorRef])
extends Notifier
+ with Actor
with ActorLogging {
override def preStart(): Unit = {
log.debug(s"{} started!", self)
@@ -89,7 +95,7 @@ class NotifierSpec
Storage ->
classOf[StorageResult],
Ev ->
- classOf[EvResult]
+ classOf[EvResult],
)
// TODO: Grid results are not covered, yet.
@@ -107,7 +113,7 @@ object NotifierSpec {
final case class TestEventEnvelope(
testEvent: TestEvent,
- msg: String = "Please notify others of this!"
+ msg: String = "Please notify others of this!",
)
}
diff --git a/src/test/scala/edu/ie3/simona/event/SimonaListenerSpec.scala b/src/test/scala/edu/ie3/simona/event/SimonaListenerSpec.scala
index 538a4732f4..c9c6b0238c 100644
--- a/src/test/scala/edu/ie3/simona/event/SimonaListenerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/event/SimonaListenerSpec.scala
@@ -36,7 +36,7 @@ class SimonaListenerSpec
|pekko.loggers =["edu.ie3.simona.test.common.SilentTestEventListener"]
|pekko.loglevel="debug"
|""".stripMargin
- )
+ ),
)
)
with Matchers {
@@ -78,7 +78,7 @@ class SimonaListenerSpec
EventFilter
.debug(
message = s"$logPrefix Received '$msg' from date $msgDate",
- occurrences = 1
+ occurrences = 1,
)
.intercept {
listener ! TestEvent(msg, msgDate)
@@ -91,7 +91,7 @@ class SimonaListenerSpec
EventFilter
.warning(
message = s"$logPrefix Received unknown event",
- occurrences = 1
+ occurrences = 1,
)
.intercept {
listener ! UnknownEvent
@@ -100,7 +100,7 @@ class SimonaListenerSpec
EventFilter
.warning(
message = s"$logPrefix Received unknown message: $unknownMessage",
- occurrences = 1
+ occurrences = 1,
)
.intercept {
listener ! unknownMessage
diff --git a/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala b/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala
index 6f7aba992e..db8c7df6e1 100644
--- a/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/event/listener/ResultEventListenerSpec.scala
@@ -11,17 +11,16 @@ import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
Transformer2WResult,
- Transformer3WResult
+ Transformer3WResult,
}
import edu.ie3.datamodel.models.result.system.PvResult
import edu.ie3.datamodel.models.result.{NodeResult, ResultEntity}
import edu.ie3.simona.agent.grid.GridResultsSupport.PartialTransformer3wResult
import edu.ie3.simona.event.ResultEvent.{
ParticipantResultEvent,
- PowerFlowResultEvent
+ PowerFlowResultEvent,
}
import edu.ie3.simona.io.result.{ResultEntitySink, ResultSinkType}
-import edu.ie3.simona.ontology.messages.StopMessage
import edu.ie3.simona.test.common.result.PowerFlowResultData
import edu.ie3.simona.test.common.{IOTestCommons, UnitSpec}
import edu.ie3.simona.util.ResultFileHierarchy
@@ -29,10 +28,9 @@ import edu.ie3.simona.util.ResultFileHierarchy.ResultEntityPathConfig
import edu.ie3.util.io.FileIOUtils
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ActorTestKit,
- ScalaTestWithActorTestKit
+ ScalaTestWithActorTestKit,
}
import org.apache.pekko.testkit.TestKit.awaitCond
-import org.apache.pekko.testkit.TestProbe
import java.io.{File, FileInputStream}
import java.util.UUID
@@ -47,7 +45,7 @@ class ResultEventListenerSpec
extends ScalaTestWithActorTestKit(
ActorTestKit.ApplicationTestConfig.withValue(
"org.apache.pekko.actor.testkit.typed.filter-leeway",
- ConfigValueFactory.fromAnyRef("10s")
+ ConfigValueFactory.fromAnyRef("10s"),
)
)
with UnitSpec
@@ -62,7 +60,7 @@ class ResultEventListenerSpec
classOf[Transformer2WResult],
classOf[Transformer3WResult],
classOf[SwitchResult],
- classOf[LineResult]
+ classOf[LineResult],
)
private val timeoutDuration: Duration = 30.seconds
@@ -71,16 +69,16 @@ class ResultEventListenerSpec
private def resultFileHierarchy(
runId: Int,
fileFormat: String,
- classes: Set[Class[_ <: ResultEntity]] = resultEntitiesToBeWritten
+ classes: Set[Class[_ <: ResultEntity]] = resultEntitiesToBeWritten,
): ResultFileHierarchy =
ResultFileHierarchy(
outputDir = testTmpDir + File.separator + runId,
simulationName,
ResultEntityPathConfig(
classes,
- ResultSinkType.Csv(fileFormat = fileFormat)
+ ResultSinkType.Csv(fileFormat = fileFormat),
),
- createDirs = true
+ createDirs = true,
)
def createDir(
@@ -113,7 +111,7 @@ class ResultEventListenerSpec
val fileHierarchy = resultFileHierarchy(1, ".csv")
Await.ready(
Future.sequence(createDir(fileHierarchy)),
- 60 seconds
+ 60 seconds,
)
// after the creation of the listener, it is expected that a corresponding raw result data file is present
@@ -122,7 +120,7 @@ class ResultEventListenerSpec
classOf[PvResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
)
@@ -140,7 +138,7 @@ class ResultEventListenerSpec
)
)
- listener ! StopMessage(true)
+ listener ! DelayedStopHelper.FlushAndStop
deathWatch expectTerminated (listener, 10 seconds)
}
}
@@ -162,7 +160,7 @@ class ResultEventListenerSpec
classOf[PvResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
)
@@ -170,17 +168,17 @@ class ResultEventListenerSpec
awaitCond(
outputFile.exists(),
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
// stop listener so that result is flushed out
- listenerRef ! StopMessage(true)
+ listenerRef ! DelayedStopHelper.FlushAndStop
// wait until all lines have been written out:
awaitCond(
getFileLinesLength(outputFile) == 2,
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
val resultFileSource = Source.fromFile(outputFile)
@@ -210,7 +208,7 @@ class ResultEventListenerSpec
Iterable(dummySwitchResult),
Iterable(dummyLineResult),
Iterable(dummyTrafo2wResult),
- Iterable.empty[PartialTransformer3wResult]
+ Iterable.empty[PartialTransformer3wResult],
)
val outputFiles = Map(
@@ -219,7 +217,7 @@ class ResultEventListenerSpec
classOf[NodeResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[NodeResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
),
dummySwitchResultString -> new File(
@@ -227,7 +225,7 @@ class ResultEventListenerSpec
classOf[SwitchResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[SwitchResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
),
dummyLineResultDataString -> new File(
@@ -235,7 +233,7 @@ class ResultEventListenerSpec
classOf[LineResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[LineResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
),
dummyTrafo2wResultDataString -> new File(
@@ -243,26 +241,26 @@ class ResultEventListenerSpec
classOf[Transformer2WResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[Transformer2WResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
- )
+ ),
)
// wait until all output files exist (headers are flushed out immediately):
awaitCond(
outputFiles.values.map(_.exists()).forall(identity),
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
// stop listener so that result is flushed out
- listenerRef ! StopMessage(true)
+ listenerRef ! DelayedStopHelper.FlushAndStop
// wait until all lines have been written out:
awaitCond(
!outputFiles.values.exists(file => getFileLinesLength(file) < 2),
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
outputFiles.foreach { case (resultRowString, outputFile) =>
@@ -291,7 +289,7 @@ class ResultEventListenerSpec
Iterable.empty[SwitchResult],
Iterable.empty[LineResult],
Iterable.empty[Transformer2WResult],
- Iterable(partialResult)
+ Iterable(partialResult),
)
"correctly reacts on received results" in {
@@ -308,14 +306,14 @@ class ResultEventListenerSpec
classOf[Transformer3WResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[Transformer3WResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
)
/* The result file is created at start up and only contains a head line. */
awaitCond(
outputFile.exists(),
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
getFileLinesLength(outputFile) shouldBe 1
@@ -340,13 +338,13 @@ class ResultEventListenerSpec
listener ! powerflow3wResult(resultB)
// stop listener so that result is flushed out
- listener ! StopMessage(true)
+ listener ! DelayedStopHelper.FlushAndStop
/* Await that the result is written */
awaitCond(
getFileLinesLength(outputFile) == 2,
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
/* Check the result */
val resultFileSource = Source.fromFile(outputFile)
@@ -387,23 +385,23 @@ class ResultEventListenerSpec
classOf[PvResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
),
- ""
+ "",
)
)
awaitCond(
outputFile.exists(),
interval = 500.millis,
- max = timeoutDuration
+ max = timeoutDuration,
)
// stopping the actor should wait until existing messages within an actor are fully processed
// otherwise it might happen, that the shutdown is triggered even before the just send ParticipantResultEvent
// reached the listener
// this also triggers the compression of result files
- listenerRef ! StopMessage(true)
+ listenerRef ! DelayedStopHelper.FlushAndStop
// shutdown the actor system
system.terminate()
@@ -415,10 +413,10 @@ class ResultEventListenerSpec
classOf[PvResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
).exists,
- timeoutDuration
+ timeoutDuration,
)
val resultFileSource = Source.fromInputStream(
@@ -428,7 +426,7 @@ class ResultEventListenerSpec
classOf[PvResult],
fail(
s"Cannot get filepath for raw result file of class '${classOf[PvResult].getSimpleName}' from outputFileHierarchy!'"
- )
+ ),
)
)
)
diff --git a/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerKafkaSpec.scala b/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerKafkaSpec.scala
index 1cfda26aa6..b2f8393ace 100644
--- a/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerKafkaSpec.scala
+++ b/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerKafkaSpec.scala
@@ -31,8 +31,8 @@ import scala.language.postfixOps
class RuntimeEventListenerKafkaSpec
extends ScalaTestWithActorTestKit
- with KafkaSpecLike
with UnitSpec
+ with KafkaSpecLike
with GivenWhenThen
with TableDrivenPropertyChecks {
private var testConsumer: KafkaConsumer[Bytes, SimonaEndMessage] = _
@@ -57,19 +57,19 @@ class RuntimeEventListenerKafkaSpec
deserializer.configure(
Map(SCHEMA_REGISTRY_URL_CONFIG -> mockSchemaRegistryUrl).asJava,
- false
+ false,
)
override def beforeAll(): Unit = {
super.beforeAll()
val config = Map[String, AnyRef](
"group.id" -> "test",
- "bootstrap.servers" -> kafka.bootstrapServers
+ "bootstrap.servers" -> kafka.bootstrapServers,
)
testConsumer = new KafkaConsumer[Bytes, SimonaEndMessage](
config.asJava,
Serdes.Bytes().deserializer(),
- deserializer
+ deserializer,
)
testConsumer.assign(topicPartitions.asJava)
@@ -93,12 +93,12 @@ class RuntimeEventListenerKafkaSpec
linger = 0,
runId = runId.toString,
schemaRegistryUrl = mockSchemaRegistryUrl,
- topic = testTopic.name
+ topic = testTopic.name,
)
- )
+ ),
),
None,
- startDateTimeString
+ startDateTimeString,
)
)
@@ -109,13 +109,13 @@ class RuntimeEventListenerKafkaSpec
("event", "expectedMsg"),
(
Done(1800L, 3L, errorInSim = false),
- SimonaEndMessage(runId, 1, error = false)
+ SimonaEndMessage(runId, 1, error = false),
),
(
Done(3600L, 3L, errorInSim = true),
- SimonaEndMessage(runId, 1, error = true)
+ SimonaEndMessage(runId, 1, error = true),
),
- (Error(errMsg), SimonaEndMessage(runId, -1, error = true))
+ (Error(errMsg), SimonaEndMessage(runId, -1, error = true)),
)
Then("records can be fetched from Kafka")
@@ -128,13 +128,21 @@ class RuntimeEventListenerKafkaSpec
forAll(cases) { case (event, expectedMsg) =>
listenerRef ! event
- val records: List[SimonaEndMessage] =
+ val receivedRecord =
eventually(timeout(20 seconds), interval(1 second)) {
- testConsumer.poll((1 second) toJava).asScala.map(_.value()).toList
+ val records =
+ testConsumer.poll((1 second) toJava).asScala.map(_.value()).toList
+
+ // run until one record is received. After each second, if no record
+ // was received, the length check below fails and we retry
+ records should have length 1
+
+ // return final record to be checked
+ records.headOption.value
}
- records should have length 1
- records should contain(expectedMsg)
+ receivedRecord shouldBe expectedMsg
+
}
}
diff --git a/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerSpec.scala b/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerSpec.scala
index 22ef952a8a..92186149bc 100644
--- a/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/event/listener/RuntimeEventListenerSpec.scala
@@ -9,7 +9,7 @@ package edu.ie3.simona.event.listener
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ActorTestKit,
LoggingTestKit,
- ScalaTestWithActorTestKit
+ ScalaTestWithActorTestKit,
}
import com.typesafe.config.ConfigValueFactory
import edu.ie3.simona.config.SimonaConfig
@@ -22,13 +22,11 @@ import edu.ie3.simona.event.RuntimeEvent.{
Initializing,
PowerFlowFailed,
Ready,
- Simulating
+ Simulating,
}
+import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.util.TickUtil._
import edu.ie3.util.TimeUtil
-import org.scalatest.PrivateMethodTester
-import org.scalatest.matchers.should
-import org.scalatest.wordspec.AnyWordSpecLike
import org.slf4j.event.Level
import java.util.concurrent.{LinkedBlockingQueue, TimeUnit}
@@ -39,38 +37,33 @@ class RuntimeEventListenerSpec
// Timeout for LoggingTestKit via TestKitSettings
// Log message sometimes seem to take a while until caught by the test kit
"org.apache.pekko.actor.testkit.typed.filter-leeway",
- ConfigValueFactory.fromAnyRef("30s")
+ ConfigValueFactory.fromAnyRef("30s"),
)
)
- with AnyWordSpecLike
- with should.Matchers
- with PrivateMethodTester {
+ with UnitSpec {
// global variables
- val eventQueue = new LinkedBlockingQueue[RuntimeEvent]()
val startDateTimeString = "2011-01-01 00:00:00"
val endTick = 3600
val duration = 10805000
- val errMsg =
- "Und wenn du lange in einen Abgrund blickst, blickt der Abgrund auch in dich hinein"
+ val errMsg = "testing error msg"
- // to change for Ready event test cases
val currentTick = 0
- // build the listener
- private val listenerRef = spawn(
- RuntimeEventListener(
- SimonaConfig.Simona.Runtime.Listener(
- None,
- None
- ),
- Some(eventQueue),
- startDateTimeString
- )
- )
-
"A runtime event listener" must {
"add a valid runtime event to the blocking queue for further processing" in {
+ val eventQueue = new LinkedBlockingQueue[RuntimeEvent]()
+
+ val listenerRef = spawn(
+ RuntimeEventListener(
+ SimonaConfig.Simona.Runtime.Listener(
+ None,
+ None,
+ ),
+ Some(eventQueue),
+ startDateTimeString,
+ )
+ )
// valid runtime events
val eventsToQueue: Seq[RuntimeEvent] = List(
@@ -78,7 +71,7 @@ class RuntimeEventListenerSpec
Ready(currentTick, duration),
Simulating(currentTick, 0),
Done(endTick, duration, errorInSim = false),
- Error(errMsg)
+ Error(errMsg),
)
for (event <- eventsToQueue) {
@@ -97,6 +90,17 @@ class RuntimeEventListenerSpec
"return valid log messages for each status event" in {
+ val listenerRef = spawn(
+ RuntimeEventListener(
+ SimonaConfig.Simona.Runtime.Listener(
+ None,
+ None,
+ ),
+ None,
+ startDateTimeString,
+ )
+ )
+
def calcTime(curTick: Long): String = {
TimeUtil.withDefaults.toString(
curTick.toDateTime(
@@ -113,42 +117,46 @@ class RuntimeEventListenerSpec
(
Initializing,
Level.INFO,
- "Initializing Agents and Services for Simulation ... "
+ "Initializing Agents and Services for Simulation ... ",
),
(
InitComplete(0L),
Level.INFO,
- s"Initialization complete. (duration: 0h : 0m : 0s )"
+ s"Initialization complete. (duration: 0h : 0m : 0s )",
),
(
Ready(currentTick, 0L),
Level.INFO,
- s"Switched from 'Simulating' to 'Ready'. Last simulated time: ${calcTime(currentTick)}."
+ s"Switched from 'Simulating' to 'Ready'. Last simulated time: ${calcTime(currentTick)}.",
),
(
Simulating(currentTick, endTick),
Level.INFO,
- s"Simulating from ${calcTime(currentTick)} until ${calcTime(endTick)}."
+ s"Simulating from ${calcTime(currentTick)} until ${calcTime(endTick)}.",
),
(
CheckWindowPassed(currentTick, 0L),
Level.INFO,
- s"Simulation until ${calcTime(currentTick)} completed."
+ s"Simulation until ${calcTime(currentTick)} completed.",
),
(
Done(endTick, duration, errorInSim = false),
Level.INFO,
- s"Simulation completed with \u001b[0;32mSUCCESS (Failed PF: 2)\u001b[0;30m in time step ${calcTime(endTick)}. Total runtime: 3h : 0m : 5s "
+ s"Simulation completed with \u001b[0;32mSUCCESS (Failed PF: 2)\u001b[0;30m in time step ${calcTime(endTick)}. Total runtime: 3h : 0m : 5s ",
),
(
Error(errMsg),
Level.ERROR,
- errMsg
- )
+ errMsg,
+ ),
)
+ val loggingTestKit = LoggingTestKit.empty
+ // logger name for ActorContext loggers (ctx.log) is the class name
+ .withLoggerName(RuntimeEventListener.getClass.getName)
+
events.foreach { case (event, level, msg) =>
- LoggingTestKit.empty
+ loggingTestKit
.withLogLevel(level)
.withMessageContains(msg)
.expect {
diff --git a/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultHandlingSpec.scala b/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultHandlingSpec.scala
index 87a13e05e7..997cef986b 100644
--- a/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultHandlingSpec.scala
+++ b/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultHandlingSpec.scala
@@ -28,19 +28,19 @@ class ThreeWindingResultHandlingSpec
UUID.randomUUID(),
Amperes(1d),
Degrees(2d),
- -5
+ -5,
)
val mockBResult = PartialTransformer3wResult.PortB(
TimeUtil.withDefaults.toZonedDateTime("2021-06-23 20:51:00"),
UUID.randomUUID(),
Amperes(3d),
- Degrees(4d)
+ Degrees(4d),
)
val mockCResult = PartialTransformer3wResult.PortC(
TimeUtil.withDefaults.toZonedDateTime("2021-06-23 20:51:00"),
UUID.randomUUID(),
Amperes(5d),
- Degrees(6d)
+ Degrees(6d),
)
"correctly indicate readiness" in {
@@ -53,7 +53,7 @@ class ThreeWindingResultHandlingSpec
(Some(mockAResult), Some(mockBResult), None, false),
(None, Some(mockBResult), Some(mockCResult), false),
(Some(mockAResult), None, Some(mockCResult), false),
- (Some(mockAResult), Some(mockBResult), Some(mockCResult), true)
+ (Some(mockAResult), Some(mockBResult), Some(mockCResult), true),
)
forAll(cases) {
@@ -61,7 +61,7 @@ class ThreeWindingResultHandlingSpec
a: Option[PartialTransformer3wResult.PortA],
b: Option[PartialTransformer3wResult.PortB],
c: Option[PartialTransformer3wResult.PortC],
- expected: Boolean
+ expected: Boolean,
) =>
val dut = AggregatedTransformer3wResult(a, b, c)
@@ -78,14 +78,14 @@ class ThreeWindingResultHandlingSpec
(None, None, Some(mockCResult)),
(Some(mockAResult), Some(mockBResult), None),
(None, Some(mockBResult), Some(mockCResult)),
- (Some(mockAResult), None, Some(mockCResult))
+ (Some(mockAResult), None, Some(mockCResult)),
)
forAll(cases) {
(
a: Option[PartialTransformer3wResult.PortA],
b: Option[PartialTransformer3wResult.PortB],
- c: Option[PartialTransformer3wResult.PortC]
+ c: Option[PartialTransformer3wResult.PortC],
) =>
val dut = AggregatedTransformer3wResult(a, b, c)
@@ -106,7 +106,7 @@ class ThreeWindingResultHandlingSpec
AggregatedTransformer3wResult(
Some(resultA),
Some(resultB),
- Some(resultC)
+ Some(resultC),
).consolidate match {
case Success(consolidated) =>
consolidated.getTime shouldBe expected.getTime
diff --git a/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultTestData.scala b/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultTestData.scala
index 579efe228f..d02ab4e5ac 100644
--- a/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultTestData.scala
+++ b/src/test/scala/edu/ie3/simona/event/listener/ThreeWindingResultTestData.scala
@@ -27,21 +27,21 @@ trait ThreeWindingResultTestData {
inputModel,
Amperes(1d),
Degrees(2d),
- -5
+ -5,
)
val resultB: PartialTransformer3wResult.PortB =
PartialTransformer3wResult.PortB(
time,
inputModel,
Amperes(3d),
- Degrees(4d)
+ Degrees(4d),
)
val resultC: PartialTransformer3wResult.PortC =
PartialTransformer3wResult.PortC(
time,
inputModel,
Amperes(5d),
- Degrees(6d)
+ Degrees(6d),
)
val expected = new Transformer3WResult(
time,
@@ -52,6 +52,6 @@ trait ThreeWindingResultTestData {
Quantities.getQuantity(4d, StandardUnits.ELECTRIC_CURRENT_ANGLE),
Quantities.getQuantity(5d, StandardUnits.ELECTRIC_CURRENT_MAGNITUDE),
Quantities.getQuantity(6d, StandardUnits.ELECTRIC_CURRENT_ANGLE),
- -5
+ -5,
)
}
diff --git a/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala b/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala
index 39e6aaa368..4ecafebdbf 100644
--- a/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala
+++ b/src/test/scala/edu/ie3/simona/integration/RunSimonaStandaloneIT.scala
@@ -45,15 +45,15 @@ class RunSimonaStandaloneIT
.empty()
.withValue(
"simona.output.base.dir",
- ConfigValueFactory.fromAnyRef(testTmpDir)
+ ConfigValueFactory.fromAnyRef(testTmpDir),
)
.withValue(
"simona.time.startDateTime",
- ConfigValueFactory.fromAnyRef("2011-01-01 00:00:00")
+ ConfigValueFactory.fromAnyRef("2011-01-01 00:00:00"),
)
.withValue(
"simona.time.endDateTime",
- ConfigValueFactory.fromAnyRef("2011-01-01 02:00:00")
+ ConfigValueFactory.fromAnyRef("2011-01-01 02:00:00"),
)
.withFallback(
ConfigFactory
@@ -78,14 +78,16 @@ class RunSimonaStandaloneIT
val simonaStandaloneSetup = SimonaStandaloneSetup(
parsedConfig,
resultFileHierarchy,
- Some(runtimeEventQueue)
+ Some(runtimeEventQueue),
)
/* run simulation */
- RunSimonaStandalone.run(
+ val successful = RunSimonaStandalone.run(
simonaStandaloneSetup
)
+ successful shouldBe true
+
/* check the results */
// check configs
val configOutputDir = new File(resultFileHierarchy.configOutputDir)
@@ -100,7 +102,7 @@ class RunSimonaStandaloneIT
// todo implement if valid result handling is implemented
val pvResultFileContent = getFileSource(
resultFileHierarchy,
- classOf[PvResult]
+ classOf[PvResult],
).getLines().toVector
pvResultFileContent.size shouldBe 190
pvResultFileContent.headOption.map(
@@ -113,12 +115,12 @@ class RunSimonaStandaloneIT
private def getFileSource(
resultFileHierarchy: ResultFileHierarchy,
- entityClass: Class[_ <: ResultEntity]
+ entityClass: Class[_ <: ResultEntity],
): BufferedSource = {
Source.fromFile(
resultFileHierarchy.rawOutputDataFilePaths.getOrElse(
entityClass,
- fail(s"Unable to get output path for result entity: $entityClass")
+ fail(s"Unable to get output path for result entity: $entityClass"),
)
)
}
@@ -126,28 +128,25 @@ class RunSimonaStandaloneIT
private def checkRuntimeEvents(
runtimeEvents: Iterable[RuntimeEvent]
): Unit = {
- runtimeEvents.toVector.size shouldBe 11
- val groupedRuntimeEvents = runtimeEvents.toVector.groupBy {
- case Initializing => Initializing
- case InitComplete(_) => InitComplete
- case Simulating(_, _) => Simulating
- case CheckWindowPassed(_, _) => CheckWindowPassed
- case Done(_, _, _) => Done
- case other => fail(s"Unexpected runtime event: $other")
- }
-
- groupedRuntimeEvents.size shouldBe 5
- groupedRuntimeEvents.keySet should contain allOf (Simulating, CheckWindowPassed, InitComplete, Initializing, Done)
+ val groupedRuntimeEvents = runtimeEvents.groupBy(event => event.getClass)
+
+ groupedRuntimeEvents.keySet should contain only (
+ classOf[Simulating],
+ classOf[CheckWindowPassed],
+ classOf[InitComplete],
+ classOf[Initializing.type],
+ classOf[Done]
+ )
groupedRuntimeEvents
- .get(Simulating)
+ .get(classOf[Simulating])
.foreach(simulatingEvents => {
simulatingEvents.size shouldBe 1
simulatingEvents.headOption.foreach(_ shouldBe Simulating(0, 7200))
})
groupedRuntimeEvents
- .get(CheckWindowPassed)
+ .get(classOf[CheckWindowPassed])
.foreach(checkWindowsPassed => {
checkWindowsPassed.size shouldBe 7
checkWindowsPassed.foreach {
@@ -161,19 +160,19 @@ class RunSimonaStandaloneIT
})
groupedRuntimeEvents
- .get(InitComplete)
+ .get(classOf[InitComplete])
.foreach(initComplets => {
initComplets.size shouldBe 1
})
groupedRuntimeEvents
- .get(Initializing)
+ .get(classOf[Initializing.type])
.foreach(initializings => {
initializings.size shouldBe 1
})
groupedRuntimeEvents
- .get(Done)
+ .get(classOf[Done])
.foreach(dones => {
dones.size shouldBe 1
dones.headOption.foreach {
diff --git a/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala b/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala
index e871b4ffb7..3b89d418f2 100644
--- a/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala
+++ b/src/test/scala/edu/ie3/simona/io/file/ResultFileHierarchySpec.scala
@@ -47,8 +47,8 @@ class ResultFileHierarchySpec
runOutputDir,
ResultEntityPathConfig(
Set(classOf[PvResult]),
- ResultSinkType.Csv("csv", "pref", "suff")
- )
+ ResultSinkType.Csv("csv", "pref", "suff"),
+ ),
)
val runOutputDirWithDate =
@@ -95,9 +95,9 @@ class ResultFileHierarchySpec
runOutputDir,
ResultEntityPathConfig(
Set(classOf[PvResult]),
- ResultSinkType.Csv("csv", "pref", "suff")
+ ResultSinkType.Csv("csv", "pref", "suff"),
),
- createDirs = true
+ createDirs = true,
)
// check for existence of run output dir
diff --git a/src/test/scala/edu/ie3/simona/io/result/ResultEntityCsvSinkSpec.scala b/src/test/scala/edu/ie3/simona/io/result/ResultEntityCsvSinkSpec.scala
index 61e5175ebb..aaed7c1d76 100644
--- a/src/test/scala/edu/ie3/simona/io/result/ResultEntityCsvSinkSpec.scala
+++ b/src/test/scala/edu/ie3/simona/io/result/ResultEntityCsvSinkSpec.scala
@@ -40,7 +40,7 @@ class ResultEntityCsvSinkSpec
|pekko.loglevel="DEBUG"
|pekko.coordinated-shutdown.phases.actor-system-terminate.timeout = 500s
""".stripMargin
- )
+ ),
)
)
with UnitSpec
@@ -58,7 +58,7 @@ class ResultEntityCsvSinkSpec
ResultEntityCsvSink(
outFileName,
resultEntityProcessor,
- outFileName.endsWith(".gz")
+ outFileName.endsWith(".gz"),
)
resultEntitySink.outfileName shouldBe outFileName
@@ -103,7 +103,7 @@ class ResultEntityCsvSinkSpec
.single(testText)
.map(t => ByteString(t))
.runWith(FileIO.toPath(path, Set(WRITE, TRUNCATE_EXISTING, CREATE))),
- 5.seconds
+ 5.seconds,
)
val resultEntityProcessor = new ResultEntityProcessor(classOf[PvResult])
@@ -111,7 +111,7 @@ class ResultEntityCsvSinkSpec
val resultEntitySink = ResultEntityCsvSink(
outFileName,
resultEntityProcessor,
- outFileName.endsWith(".gz")
+ outFileName.endsWith(".gz"),
)
// close sink to ensure that everything is written out
@@ -152,14 +152,14 @@ class ResultEntityCsvSinkSpec
TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"),
UUID.fromString("e5ac84d3-c7a5-4870-a42d-837920aec9bb"),
Quantities.getQuantity(10, StandardUnits.ACTIVE_POWER_IN),
- Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN)
+ Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN),
)
val resultEntitySink =
ResultEntityCsvSink(
outFileName,
resultEntityProcessor,
- outFileName.endsWith(".gz")
+ outFileName.endsWith(".gz"),
)
resultEntitySink.handleResultEntity(dummyPvResult)
@@ -197,14 +197,14 @@ class ResultEntityCsvSinkSpec
TimeUtil.withDefaults.toZonedDateTime("2020-01-30 17:26:44"),
UUID.fromString("e5ac84d3-c7a5-4870-a42d-837920aec9bb"),
Quantities.getQuantity(10, StandardUnits.ACTIVE_POWER_IN),
- Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN)
+ Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN),
)
val resultEntitySink =
ResultEntityCsvSink(
outFileName,
resultEntityProcessor,
- outFileName.endsWith(".gz")
+ outFileName.endsWith(".gz"),
)
val exception = intercept[ProcessResultEventException] {
diff --git a/src/test/scala/edu/ie3/simona/io/result/ResultEntityKafkaSpec.scala b/src/test/scala/edu/ie3/simona/io/result/ResultEntityKafkaSpec.scala
index 9edf05b5ee..ed88041bc0 100644
--- a/src/test/scala/edu/ie3/simona/io/result/ResultEntityKafkaSpec.scala
+++ b/src/test/scala/edu/ie3/simona/io/result/ResultEntityKafkaSpec.scala
@@ -65,19 +65,19 @@ class ResultEntityKafkaSpec
deserializer.configure(
Map(SCHEMA_REGISTRY_URL_CONFIG -> mockSchemaRegistryUrl).asJava,
- false
+ false,
)
override def beforeAll(): Unit = {
super.beforeAll()
val config = Map[String, AnyRef](
"group.id" -> "test",
- "bootstrap.servers" -> kafka.bootstrapServers
+ "bootstrap.servers" -> kafka.bootstrapServers,
)
testConsumer = new KafkaConsumer[Bytes, PlainNodeResult](
config.asJava,
Serdes.Bytes().deserializer(),
- deserializer
+ deserializer,
)
testConsumer.assign(topicPartitions.asJava)
@@ -103,9 +103,9 @@ class ResultEntityKafkaSpec
runId,
kafka.bootstrapServers,
mockSchemaRegistryUrl,
- 20
- )
- )
+ 20,
+ ),
+ ),
)
)
)
@@ -115,19 +115,19 @@ class ResultEntityKafkaSpec
ZonedDateTime.parse("2021-01-01T00:00:00+01:00[Europe/Berlin]"),
UUID.randomUUID(),
Quantities.getQuantity(1d, PowerSystemUnits.PU),
- Quantities.getQuantity(0d, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(0d, PowerSystemUnits.DEGREE_GEOM),
)
val nodeRes2 = new NodeResult(
ZonedDateTime.parse("2021-01-01T00:00:00+01:00[Europe/Berlin]"),
UUID.randomUUID(),
Quantities.getQuantity(0.8d, PowerSystemUnits.PU),
- Quantities.getQuantity(15d, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(15d, PowerSystemUnits.DEGREE_GEOM),
)
val nodeRes3 = new NodeResult(
ZonedDateTime.parse("2021-01-10T00:00:00+01:00[Europe/Berlin]"),
UUID.randomUUID(),
Quantities.getQuantity(0.75d, PowerSystemUnits.PU),
- Quantities.getQuantity(90d, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(90d, PowerSystemUnits.DEGREE_GEOM),
)
When("receiving the NodeResults")
@@ -136,7 +136,7 @@ class ResultEntityKafkaSpec
Iterable.empty,
Iterable.empty,
Iterable.empty,
- Iterable.empty
+ Iterable.empty,
)
Then("records can be fetched from Kafka")
@@ -155,7 +155,7 @@ class ResultEntityKafkaSpec
nodeRes1.getUuid,
nodeRes1.getInputModel,
nodeRes1.getvMag().getValue.doubleValue(),
- nodeRes1.getvAng().getValue.doubleValue()
+ nodeRes1.getvAng().getValue.doubleValue(),
)
)
records should contain(
@@ -165,7 +165,7 @@ class ResultEntityKafkaSpec
nodeRes2.getUuid,
nodeRes2.getInputModel,
nodeRes2.getvMag().getValue.doubleValue(),
- nodeRes2.getvAng().getValue.doubleValue()
+ nodeRes2.getvAng().getValue.doubleValue(),
)
)
records should contain(
@@ -175,7 +175,7 @@ class ResultEntityKafkaSpec
nodeRes3.getUuid,
nodeRes3.getInputModel,
nodeRes3.getvMag().getValue.doubleValue(),
- nodeRes3.getvAng().getValue.doubleValue()
+ nodeRes3.getvAng().getValue.doubleValue(),
)
)
}
diff --git a/src/test/scala/edu/ie3/simona/io/result/ResultSinkTypeSpec.scala b/src/test/scala/edu/ie3/simona/io/result/ResultSinkTypeSpec.scala
index 66b6986c47..2c93aced13 100644
--- a/src/test/scala/edu/ie3/simona/io/result/ResultSinkTypeSpec.scala
+++ b/src/test/scala/edu/ie3/simona/io/result/ResultSinkTypeSpec.scala
@@ -22,11 +22,11 @@ class ResultSinkTypeSpec extends UnitSpec {
fileFormat = ".csv",
filePrefix = "",
fileSuffix = "",
- isHierarchic = false
+ isHierarchic = false,
)
),
influxDb1x = None,
- kafka = None
+ kafka = None,
)
inside(ResultSinkType(conf, "testRun")) {
@@ -46,10 +46,10 @@ class ResultSinkTypeSpec extends UnitSpec {
SimonaConfig.Simona.Output.Sink.InfluxDb1x(
database = "test",
port = 1,
- url = "localhost/"
+ url = "localhost/",
)
),
- kafka = None
+ kafka = None,
)
val runName = "testRun"
@@ -73,9 +73,9 @@ class ResultSinkTypeSpec extends UnitSpec {
12,
"00000000-0000-0000-0000-000000000000",
"https://reg:123",
- "topic"
+ "topic",
)
- )
+ ),
)
val runName = "testRun"
@@ -85,7 +85,7 @@ class ResultSinkTypeSpec extends UnitSpec {
runId,
bootstrapServers,
schemaRegistryUrl,
- linger
+ linger,
) =>
topicNodeRes shouldBe "topic"
runId shouldBe UUID.fromString("00000000-0000-0000-0000-000000000000")
@@ -104,17 +104,17 @@ class ResultSinkTypeSpec extends UnitSpec {
fileFormat = ".csv",
filePrefix = "",
fileSuffix = "",
- isHierarchic = false
+ isHierarchic = false,
)
),
influxDb1x = Some(
SimonaConfig.Simona.Output.Sink.InfluxDb1x(
database = "test",
port = 1,
- url = "localhost"
+ url = "localhost",
)
),
- kafka = None
+ kafka = None,
)
assertThrows[IllegalArgumentException](ResultSinkType(conf, "testRun"))
@@ -124,7 +124,7 @@ class ResultSinkTypeSpec extends UnitSpec {
val conf = SimonaConfig.Simona.Output.Sink(
csv = None,
influxDb1x = None,
- kafka = None
+ kafka = None,
)
assertThrows[IllegalArgumentException](ResultSinkType(conf, "testRun"))
diff --git a/src/test/scala/edu/ie3/simona/io/result/plain/PlainWriterSpec.scala b/src/test/scala/edu/ie3/simona/io/result/plain/PlainWriterSpec.scala
index ab3c7a8390..14554d60a5 100644
--- a/src/test/scala/edu/ie3/simona/io/result/plain/PlainWriterSpec.scala
+++ b/src/test/scala/edu/ie3/simona/io/result/plain/PlainWriterSpec.scala
@@ -43,7 +43,7 @@ class PlainWriterSpec extends UnitSpec with GivenWhenThen {
time,
inputModelId,
vMag,
- vAng
+ vAng,
)
When("converting to a plain result")
@@ -77,7 +77,7 @@ class PlainWriterSpec extends UnitSpec with GivenWhenThen {
eventId,
inputModelId,
vMag,
- vAng
+ vAng,
)
When("converting to a full NodeResult")
@@ -91,7 +91,7 @@ class PlainWriterSpec extends UnitSpec with GivenWhenThen {
.getvMag() shouldBe Quantities.getQuantity(vMag, PowerSystemUnits.PU)
plainResult.getvAng() shouldBe Quantities.getQuantity(
vAng,
- PowerSystemUnits.DEGREE_GEOM
+ PowerSystemUnits.DEGREE_GEOM,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/assets/control/QControlSpec.scala b/src/test/scala/edu/ie3/simona/model/assets/control/QControlSpec.scala
index 23676e71ca..d3a77f3c4a 100644
--- a/src/test/scala/edu/ie3/simona/model/assets/control/QControlSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/assets/control/QControlSpec.scala
@@ -10,14 +10,14 @@ import edu.ie3.datamodel.models.input.system.characteristic
import edu.ie3.datamodel.models.input.system.characteristic.{
CharacteristicPoint,
CosPhiP => CosPhiPInput,
- QV => QVInput
+ QV => QVInput,
}
import edu.ie3.simona.exceptions.QControlException
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.participant.control.QControl.{
CosPhiFixed,
CosPhiP,
- QV
+ QV,
}
import edu.ie3.simona.model.system.Characteristic.XYPair
import edu.ie3.simona.test.common.UnitSpec
@@ -47,7 +47,7 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
def createXYPair(
d1: Double,
- d2: Double
+ d2: Double,
): XYPair[squants.Dimensionless, squants.Dimensionless] = {
XYPair(Each(d1), Each(d2))
}
@@ -59,12 +59,12 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
util.Arrays.asList(
new CharacteristicPoint[Dimensionless, Dimensionless](
getQuantity(1d, PU),
- getQuantity(2d, PU)
+ getQuantity(2d, PU),
),
new CharacteristicPoint[Dimensionless, Dimensionless](
getQuantity(3d, PU),
- getQuantity(4d, PU)
- )
+ getQuantity(4d, PU),
+ ),
)
)
val invalidInput =
@@ -90,7 +90,7 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
Vector(
createXYPair(0.0, -1.0),
createXYPair(0.5, -0.8),
- createXYPair(1.0, -0.2)
+ createXYPair(1.0, -0.2),
)
)
)
@@ -103,7 +103,7 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
createXYPair(0.9, -1.0),
createXYPair(0.95, 0.0),
createXYPair(1.05, 0.0),
- createXYPair(1.1, 1.0)
+ createXYPair(1.1, 1.0),
)
)
)
@@ -118,20 +118,19 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
"provide correct values when the requested value is part of the containing xy coordinates" in {
val requestedValue = Each(0.5)
- (validCosPhiP.cosPhi(requestedValue) ~= Each(-0.8)) shouldBe true
+
+ validCosPhiP.cosPhi(requestedValue) should approximate(Each(-0.8))
}
"provide an interpolated value when the requested value is not part of the containing xy coordinates" in {
val requestedValue = Each(0.75)
- (validCosPhiP.cosPhi(requestedValue) ~= Each(-0.5)) shouldBe true
+ validCosPhiP.cosPhi(requestedValue) should approximate(Each(-0.5))
}
"provide the last known value when the requested value is outside of the containing xy coordinates" in {
-
- (validCosPhiP.cosPhi(Each(2.0)) ~= Each(-0.2)) shouldBe true
-
- (validCosPhiP.cosPhi(Each(-1.0)) ~= Each(-1.0)) shouldBe true
+ validCosPhiP.cosPhi(Each(2.0)) should approximate(Each(-0.2))
+ validCosPhiP.cosPhi(Each(-1.0)) should approximate(Each(-1.0))
}
}
@@ -159,11 +158,11 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
(1.03, 0.375),
(1.04, 0.5),
(1.05, 0.625),
- (1.10, 0.625)
+ (1.10, 0.625),
)
forAll(testingPoints) { (v: Double, scaleExpected: Double) =>
- (validQV.q(Each(v), qMax) ~= qMax * scaleExpected) shouldBe true
+ validQV.q(Each(v), qMax) should approximate(qMax * scaleExpected)
}
}
@@ -197,11 +196,11 @@ class QControlSpec extends UnitSpec with TableDrivenPropertyChecks {
(1.08, 0.6),
(1.09, 0.8),
(1.1, 1.0),
- (1.12, 1.0)
+ (1.12, 1.0),
)
forAll(testingPoints) { (v: Double, scaleExpected: Double) =>
- (validQV.q(Each(v), qMax) ~= qMax * scaleExpected) shouldBe true
+ validQV.q(Each(v), qMax) should approximate(qMax * scaleExpected)
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/control/TransformerControlGroupModelSpec.scala b/src/test/scala/edu/ie3/simona/model/control/TransformerControlGroupModelSpec.scala
new file mode 100644
index 0000000000..67d762dca7
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/control/TransformerControlGroupModelSpec.scala
@@ -0,0 +1,131 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.control
+
+import breeze.linalg.DenseMatrix
+import breeze.math.Complex
+import edu.ie3.powerflow.model.NodeData.StateData
+import edu.ie3.powerflow.model.PowerFlowResult.SuccessFullPowerFlowResult.ValidNewtonRaphsonPFResult
+import edu.ie3.powerflow.model.enums.NodeType
+import edu.ie3.simona.model.control.TransformerControlGroupModel.RegulationCriterion
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.simona.test.matchers.QuantityMatchers
+import squants.{Dimensionless, Each}
+
+import java.util.UUID
+
+class TransformerControlGroupModelSpec extends UnitSpec with QuantityMatchers {
+
+ implicit val tolerance: Dimensionless = Each(1e-10)
+
+ "Checking the function of transformer control groups" should {
+ val regulationFunction =
+ PrivateMethod[RegulationCriterion](Symbol("regulationFunction"))
+
+ val regulationCriterion =
+ TransformerControlGroupModel invokePrivate regulationFunction(1.1, 0.9)
+
+ val dut = TransformerControlGroupModel(
+ Set(
+ UUID.fromString(
+ "d4d650be-87b7-4cf6-be7f-03f0bbcde3e3"
+ ),
+ UUID.fromString(
+ "08b8d2ca-993d-45cd-9456-f009ecb47bc0"
+ ),
+ UUID.fromString(
+ "324f49e5-1c35-4c49-afb1-3cf41696bf93"
+ ),
+ ),
+ regulationCriterion,
+ )
+
+ val uuidToIndex = Map(
+ UUID.fromString(
+ "d4d650be-87b7-4cf6-be7f-03f0bbcde3e3"
+ ) -> 0,
+ UUID.fromString(
+ "08b8d2ca-993d-45cd-9456-f009ecb47bc0"
+ ) -> 1,
+ UUID.fromString(
+ "324f49e5-1c35-4c49-afb1-3cf41696bf93"
+ ) -> 2,
+ )
+
+ "return no regulation need, if everything is fine" in {
+ val result = ValidNewtonRaphsonPFResult(
+ 0,
+ Array(
+ StateData(0, NodeType.SL, Complex.one, Complex.zero),
+ StateData(1, NodeType.PQ, Complex.one, Complex.zero),
+ StateData(2, NodeType.PQ, Complex.one, Complex.zero),
+ ),
+ DenseMatrix.zeros(1, 1),
+ )
+
+ val actual = dut.determineRegulationNeed(result, uuidToIndex)
+
+ actual shouldBe None
+ }
+
+ "return no regulation need, if requests are contradictory" in {
+ val result = ValidNewtonRaphsonPFResult(
+ 0,
+ Array(
+ StateData(0, NodeType.SL, Complex.one, Complex.zero),
+ StateData(1, NodeType.PQ, Complex.one * 0.88, Complex.zero),
+ StateData(2, NodeType.PQ, Complex.one * 1.11, Complex.zero),
+ ),
+ DenseMatrix.zeros(1, 1),
+ )
+
+ val actual = dut.determineRegulationNeed(result, uuidToIndex)
+
+ actual shouldBe None
+ }
+
+ "return the biggest positive regulation need" in {
+ val result = ValidNewtonRaphsonPFResult(
+ 0,
+ Array(
+ StateData(0, NodeType.SL, Complex.one, Complex.zero),
+ StateData(1, NodeType.PQ, Complex.one * 0.85, Complex.zero),
+ StateData(2, NodeType.PQ, Complex.one * 0.88, Complex.zero),
+ ),
+ DenseMatrix.zeros(1, 1),
+ )
+
+ val actual = dut.determineRegulationNeed(result, uuidToIndex)
+
+ actual match {
+ case Some(regulationNeed) =>
+ regulationNeed should approximate(Each(0.05))
+ case None => fail("Did expect to receive a regulation need.")
+ }
+ }
+
+ "return the biggest negative regulation need" in {
+ val result = ValidNewtonRaphsonPFResult(
+ 0,
+ Array(
+ StateData(0, NodeType.SL, Complex.one, Complex.zero),
+ StateData(1, NodeType.PQ, Complex.one * 1.15, Complex.zero),
+ StateData(2, NodeType.PQ, Complex.one * 1.11, Complex.zero),
+ ),
+ DenseMatrix.zeros(1, 1),
+ )
+
+ val actual = dut.determineRegulationNeed(result, uuidToIndex)
+
+ actual match {
+ case Some(regulationNeed) =>
+ regulationNeed should approximate(Each(-0.05))
+ case None => fail("Did expect to receive a regulation need.")
+ }
+ }
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala
index 3b73c59dfd..53165fc018 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/GridSpec.scala
@@ -10,19 +10,27 @@ import breeze.linalg.DenseMatrix
import breeze.math.Complex
import breeze.numerics.abs
import edu.ie3.datamodel.exceptions.InvalidGridException
+import edu.ie3.datamodel.models.input.MeasurementUnitInput
+import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.exceptions.GridInconsistencyException
+import edu.ie3.simona.model.control.{GridControls, TransformerControlGroupModel}
import edu.ie3.simona.model.grid.GridModel.GridComponents
import edu.ie3.simona.test.common.input.{GridInputTestData, LineInputTestData}
import edu.ie3.simona.test.common.model.grid.{
BasicGrid,
BasicGridWithSwitches,
- FiveLinesWithNodes
+ FiveLinesWithNodes,
}
-import edu.ie3.simona.test.common.{DefaultTestData, UnitSpec}
+import edu.ie3.simona.test.common.{ConfigTestData, DefaultTestData, UnitSpec}
+import testutils.TestObjectFactory
import java.util.UUID
-class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
+class GridSpec
+ extends UnitSpec
+ with LineInputTestData
+ with DefaultTestData
+ with ConfigTestData {
private val _printAdmittanceMatrixOnMismatch
: (DenseMatrix[Complex], DenseMatrix[Complex]) => Unit = {
@@ -66,12 +74,12 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
)
val getLinesAdmittance: (
Map[UUID, Int],
- LineModel
+ LineModel,
) => (Int, Int, Complex, Complex, Complex) =
(nodeUuidToIndexMap, line) =>
GridModel invokePrivate getLinesAdmittanceMethod(
nodeUuidToIndexMap,
- line
+ line,
)
// result of method call
@@ -79,7 +87,7 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
GridModel invokePrivate buildAssetAdmittanceMatrix(
nodeUuidToIndexMap,
lines,
- getLinesAdmittance
+ getLinesAdmittance,
)
_printAdmittanceMatrixOnMismatch(actualResult, lineAdmittanceMatrix)
@@ -94,7 +102,7 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
GridModel.updateUuidToIndexMap(withClosedSwitches)
private val admittanceMatixClosed = GridModel.composeAdmittanceMatrix(
withClosedSwitches.nodeUuidToIndexMap,
- withClosedSwitches.gridComponents
+ withClosedSwitches.gridComponents,
)
private val withOpenSwitches = createGridCopy()
@@ -102,7 +110,7 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
GridModel.updateUuidToIndexMap(withOpenSwitches)
private val admittanceMatrixOpen = GridModel.composeAdmittanceMatrix(
withOpenSwitches.nodeUuidToIndexMap,
- withOpenSwitches.gridComponents
+ withOpenSwitches.gridComponents,
)
// dimension of admittance matrix with closed switches should be the dimension
@@ -120,7 +128,7 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
.flatMap { switch =>
Iterable(
switch.nodeAUuid -> switch.nodeBUuid,
- switch.nodeBUuid -> switch.nodeAUuid
+ switch.nodeBUuid -> switch.nodeAUuid,
)
}
.groupMap { case (key, _) => key } { case (_, value) => value }
@@ -168,7 +176,7 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
admittanceMatixClosed.valueAt(
iClosed,
- jClosed
+ jClosed,
) shouldBe sumOfAdmittancesOpenSwitches withClue s" at \n\tposition ($iClosed, $jClosed) of the grid with closed switches/" +
s"\n\tpositions (${iOpenAll.mkString(",")}) x (${jOpenAll.mkString(",")}) of the grid with open switches"
@@ -203,8 +211,9 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
lines,
Set(transformer2wModel),
Set.empty[Transformer3wModel],
- switches
- )
+ switches,
+ ),
+ GridControls.empty,
)
// get the private method for validation
val validateConnectivity: PrivateMethod[Unit] =
@@ -231,8 +240,9 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
adaptedLines,
Set(transformer2wModel),
Set.empty[Transformer3wModel],
- Set.empty[SwitchModel]
- )
+ Set.empty[SwitchModel],
+ ),
+ GridControls.empty,
)
// get the private method for validation
@@ -259,7 +269,7 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
"SecondSwitch1",
defaultOperationInterval,
node1.uuid,
- node13.uuid
+ node13.uuid,
)
// add the second switch + enable switches
override val switches: Set[SwitchModel] = super.switches + secondSwitch
@@ -276,8 +286,9 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
lines,
Set(transformer2wModel),
Set.empty[Transformer3wModel],
- switches
- )
+ switches,
+ ),
+ GridControls.empty,
)
// get the private method for validation
@@ -380,8 +391,9 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
lines,
Set(transformer2wModel),
Set.empty[Transformer3wModel],
- switches
- )
+ switches,
+ ),
+ GridControls.empty,
)
// update the uuidToIndexMap
@@ -431,8 +443,9 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
lines,
Set(transformer2wModel),
Set.empty[Transformer3wModel],
- Set.empty[SwitchModel]
- )
+ Set.empty[SwitchModel],
+ ),
+ GridControls.empty,
)
// update the uuidToIndexMap
@@ -453,7 +466,88 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
gridModel.nodeUuidToIndexMap.keySet.toVector.sorted should be(
nodes.map(node => node.uuid).toVector.sorted
)
+ }
+ }
+
+ "build correct transformer control models" should {
+ /* Testing of distinct transformer control group building can be found in the spec for transformer control groups */
+
+ "determine node uuids correctly" in {
+ val determineNodeUuids =
+ PrivateMethod[Set[UUID]](Symbol("determineNodeUuids"))
+
+ val node0 = TestObjectFactory.buildNodeInput(
+ false,
+ GermanVoltageLevelUtils.MV_10KV,
+ 1,
+ )
+ val node1 = TestObjectFactory.buildNodeInput(
+ false,
+ GermanVoltageLevelUtils.MV_10KV,
+ 1,
+ )
+ val node2 = TestObjectFactory.buildNodeInput(
+ false,
+ GermanVoltageLevelUtils.MV_10KV,
+ 1,
+ )
+ val node3 = TestObjectFactory.buildNodeInput(
+ false,
+ GermanVoltageLevelUtils.MV_10KV,
+ 1,
+ )
+ val measurementUnits = Set(
+ new MeasurementUnitInput(
+ UUID.fromString("3ad9e076-c02b-4cf9-8720-18e2bb541ede"),
+ "measurement_unit_0",
+ node0,
+ true,
+ false,
+ false,
+ false,
+ ),
+ new MeasurementUnitInput(
+ UUID.fromString("ab66fbb0-ece1-44b9-9341-86a884233ec4"),
+ "measurement_unit_1",
+ node1,
+ true,
+ false,
+ false,
+ false,
+ ),
+ new MeasurementUnitInput(
+ UUID.fromString("93b4d0d8-cc67-41f5-9d5c-1cd6dbb2e70d"),
+ "measurement_unit_2",
+ node2,
+ true,
+ false,
+ false,
+ false,
+ ),
+ new MeasurementUnitInput(
+ UUID.fromString("8e84eb8a-2940-4900-b0ce-0eeb6bca8bae"),
+ "measurement_unit_3",
+ node3,
+ false,
+ false,
+ false,
+ false,
+ ),
+ )
+ val selectedMeasurements = Set(
+ "ab66fbb0-ece1-44b9-9341-86a884233ec4",
+ "93b4d0d8-cc67-41f5-9d5c-1cd6dbb2e70d",
+ "8e84eb8a-2940-4900-b0ce-0eeb6bca8bae",
+ )
+ val expectedUuids = Set(node1, node2).map(_.getUuid)
+
+ val actual =
+ TransformerControlGroupModel invokePrivate determineNodeUuids(
+ measurementUnits,
+ selectedMeasurements,
+ )
+ actual should contain theSameElementsAs expectedUuids
}
}
@@ -463,7 +557,8 @@ class GridSpec extends UnitSpec with LineInputTestData with DefaultTestData {
validTestGridInputModel,
gridInputModelTestDataRefSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
+ simonaConfig,
)
}
diff --git a/src/test/scala/edu/ie3/simona/model/grid/LineSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/LineSpec.scala
index baebd2efe3..c1a3fe9ebf 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/LineSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/LineSpec.scala
@@ -41,7 +41,7 @@ class LineSpec extends UnitSpec with LineInputTestData {
Each(0.0013109999999999999d),
Each(0.0010680000000000002d),
Each(0d),
- Each(0.60375d)
+ Each(0.60375d),
)
}
@@ -63,7 +63,7 @@ class LineSpec extends UnitSpec with LineInputTestData {
lineInputMs10Kv,
refSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(validLineModel) {
@@ -78,7 +78,7 @@ class LineSpec extends UnitSpec with LineInputTestData {
r,
x,
g,
- b
+ b,
) =>
uuid shouldBe lineInputMs10Kv.getUuid
id shouldBe lineInputMs10Kv.getId
@@ -86,21 +86,20 @@ class LineSpec extends UnitSpec with LineInputTestData {
nodeAUuid shouldBe lineInputMs10Kv.getNodeA.getUuid
nodeBUuid shouldBe lineInputMs10Kv.getNodeB.getUuid
amount shouldBe lineInputMs10Kv.getParallelDevices
- (iMax ~= Amperes(
- lineInputMs10Kv.getType.getiMax().getValue.doubleValue()
- )) shouldBe true
-
- (r ~= Each(0.0013109999999999999d)) shouldBe true
- (x ~= Each(0.0010680000000000002d)) shouldBe true
- (g ~= Each(0d)) shouldBe true
- (b ~= Each(0.00000060375d)) shouldBe true
+ iMax should approximate(
+ Amperes(lineInputMs10Kv.getType.getiMax().getValue.doubleValue())
+ )
+
+ r should approximate(Each(0.0013109999999999999d))
+ x should approximate(Each(0.0010680000000000002d))
+ g should approximate(Each(0d))
+ b should approximate(Each(0.00000060375d))
}
- (validLineModel.b0() ~= Each(0.000000301875d)) shouldBe true
- (validLineModel.bij() ~= Each(-373.5121155369499d)) shouldBe true
- (validLineModel.g0() ~= Each(0d)) shouldBe true
- (validLineModel.gij() ~= Each(458.4966137349637d)) shouldBe true
-
+ validLineModel.b0() should approximate(Each(0.000000301875d))
+ validLineModel.bij() should approximate(Each(-373.5121155369499d))
+ validLineModel.g0() should approximate(Each(0d))
+ validLineModel.gij() should approximate(Each(458.4966137349637d))
}
}
@@ -137,7 +136,7 @@ class LineSpec extends UnitSpec with LineInputTestData {
"calculate the branch admittance Y_ij of a given line model correctly" in new ValidLineModel {
LineModel.yij(validLineModel) shouldBe Complex(
1375.489841204891,
- -1120.5363466108497
+ -1120.5363466108497,
)
}
@@ -152,10 +151,9 @@ class LineSpec extends UnitSpec with LineInputTestData {
val iNodeB: squants.electro.ElectricCurrent =
Amperes(145d)
- (LineModel.utilisation(validLineModel, iNodeA, iNodeB) ~= Each(
- 22.222222222222218
- )) shouldBe true
-
+ LineModel.utilisation(validLineModel, iNodeA, iNodeB) should approximate(
+ Each(22.222222222222218)
+ )
}
"be able to be enabled and disabled on request" in new FiveLinesWithNodes {
diff --git a/src/test/scala/edu/ie3/simona/model/grid/NodeInputModelSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/NodeInputModelSpec.scala
index 4f6f89a0a8..140ecec17f 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/NodeInputModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/NodeInputModelSpec.scala
@@ -27,7 +27,7 @@ class NodeInputModelSpec extends UnitSpec with NodeInputTestData {
NodeModel(
nodeInputNoSlackNs04KvA,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(validNodeModel) {
@@ -37,15 +37,15 @@ class NodeInputModelSpec extends UnitSpec with NodeInputTestData {
operationInterval,
isSlack,
vTarget,
- voltLvl
+ voltLvl,
) =>
uuid shouldBe nodeInputNoSlackNs04KvA.getUuid
id shouldBe nodeInputNoSlackNs04KvA.getId
operationInterval shouldBe defaultOperationInterval
isSlack shouldBe nodeInputNoSlackNs04KvA.isSlack
- (vTarget ~= Each(
- nodeInputNoSlackNs04KvA.getvTarget.getValue.doubleValue()
- )) shouldBe true
+ vTarget should approximate(
+ Each(nodeInputNoSlackNs04KvA.getvTarget.getValue.doubleValue())
+ )
voltLvl shouldBe nodeInputNoSlackNs04KvA.getVoltLvl
}
diff --git a/src/test/scala/edu/ie3/simona/model/grid/SwitchModelSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/SwitchModelSpec.scala
index 8ed9db9023..be7814fc23 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/SwitchModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/SwitchModelSpec.scala
@@ -21,7 +21,7 @@ class SwitchModelSpec extends UnitSpec with DefaultTestData {
val switchModel: SwitchModel = SwitchModel(
switchInput,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(switchModel) {
@@ -30,7 +30,7 @@ class SwitchModelSpec extends UnitSpec with DefaultTestData {
id,
operationInterval,
nodeAUuid,
- nodeBUuid
+ nodeBUuid,
) =>
uuid should be(switchInput.getUuid)
id should be(switchInput.getId)
diff --git a/src/test/scala/edu/ie3/simona/model/grid/SystemComponentSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/SystemComponentSpec.scala
index 4ff98b2b02..c9668e16d2 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/SystemComponentSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/SystemComponentSpec.scala
@@ -70,10 +70,10 @@ object SystemComponentSpec {
final case class SystemComponentMock(
uuid: UUID = UUID.fromString("94b633a2-dfc0-4c28-acf5-d756150e5cde"),
id: String = "SystemComponentMock",
- operationInterval: OperationInterval
+ operationInterval: OperationInterval,
) extends SystemComponent(
uuid,
id,
- operationInterval
+ operationInterval,
) // concrete implementation for testing
}
diff --git a/src/test/scala/edu/ie3/simona/model/grid/Transformer3wModelSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/Transformer3wModelSpec.scala
index e5082e6a3b..4f865f58af 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/Transformer3wModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/Transformer3wModelSpec.scala
@@ -11,7 +11,7 @@ import edu.ie3.simona.exceptions.InvalidActionRequestException
import edu.ie3.simona.model.grid.Transformer3wPowerFlowCase.{
PowerFlowCaseA,
PowerFlowCaseB,
- PowerFlowCaseC
+ PowerFlowCaseC,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.test.common.input.Transformer3wTestData
@@ -42,7 +42,7 @@ class Transformer3wModelSpec
transformer3wInput.getType.getTapMax,
transformer3wInput.getType.getTapMin,
transformer3wInput.getType.getTapNeutr,
- transformer3wInput.isAutoTap
+ transformer3wInput.isAutoTap,
)
val transformerModel: Transformer3wModel =
@@ -51,7 +51,7 @@ class Transformer3wModelSpec
mainRefSystemEhv,
1,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(transformerModel) {
@@ -70,7 +70,7 @@ class Transformer3wModelSpec
r,
x,
g,
- b
+ b,
) =>
uuid shouldBe transformer3wInput.getUuid
id shouldBe transformer3wInput.getId
@@ -85,21 +85,21 @@ class Transformer3wModelSpec
transformerTappingModel shouldBe expectedTappingModel
amount shouldBe transformer3wInput.getParallelDevices
powerFlowCase shouldBe PowerFlowCaseA
- (r ~= Each(1.03878e-3)) shouldBe true
- (x ~= Each(166.34349e-3)) shouldBe true
- (g ~= Each(1.874312e-6)) shouldBe true
- (b ~= Each(-75.012912e-6)) shouldBe true
+ r should approximate(Each(1.03878e-3))
+ x should approximate(Each(166.34349e-3))
+ g should approximate(Each(1.874312e-6))
+ b should approximate(Each(-75.012912e-6))
}
val yii: Complex = Transformer3wModel.y0(
transformerModel,
- Transformer3wModel.Transformer3wPort.A
+ Transformer3wModel.Transformer3wPort.A,
)
yii shouldBe Complex.zero
val yjj: Complex =
Transformer3wModel.y0(
transformerModel,
- Transformer3wModel.Transformer3wPort.INTERNAL
+ Transformer3wModel.Transformer3wPort.INTERNAL,
)
implicit val doubleTolerance: Double = 1e-11
yjj.real shouldBe 1.874312e-6 +- doubleTolerance
@@ -117,7 +117,7 @@ class Transformer3wModelSpec
transformer3wInput.getType.getTapMax,
transformer3wInput.getType.getTapMin,
transformer3wInput.getType.getTapNeutr,
- transformer3wInput.isAutoTap
+ transformer3wInput.isAutoTap,
)
val transformerModel: Transformer3wModel =
@@ -126,7 +126,7 @@ class Transformer3wModelSpec
mainRefSystemHv,
2,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(transformerModel) {
@@ -145,7 +145,7 @@ class Transformer3wModelSpec
r,
x,
g,
- b
+ b,
) =>
uuid shouldBe transformer3wInput.getUuid
id shouldBe transformer3wInput.getId
@@ -160,21 +160,21 @@ class Transformer3wModelSpec
transformerTappingModel shouldBe expectedTappingModel
amount shouldBe transformer3wInput.getParallelDevices
powerFlowCase shouldBe PowerFlowCaseB
- (r ~= Each(240.9972299e-6)) shouldBe true
- (x ~= Each(24.99307479224e-3)) shouldBe true
- (g ~= Each(0d)) shouldBe true
- (b ~= Each(0d)) shouldBe true
+ r should approximate(Each(240.9972299e-6))
+ x should approximate(Each(24.99307479224e-3))
+ g should approximate(Each(0d))
+ b should approximate(Each(0d))
}
val yii: Complex = Transformer3wModel.y0(
transformerModel,
- Transformer3wModel.Transformer3wPort.A
+ Transformer3wModel.Transformer3wPort.A,
)
yii shouldBe Complex.zero
val yjj: Complex =
Transformer3wModel.y0(
transformerModel,
- Transformer3wModel.Transformer3wPort.INTERNAL
+ Transformer3wModel.Transformer3wPort.INTERNAL,
)
yjj shouldBe Complex.zero
@@ -192,7 +192,7 @@ class Transformer3wModelSpec
transformer3wInput.getType.getTapMax,
transformer3wInput.getType.getTapMin,
transformer3wInput.getType.getTapNeutr,
- transformer3wInput.isAutoTap
+ transformer3wInput.isAutoTap,
)
val transformerModel: Transformer3wModel =
@@ -201,7 +201,7 @@ class Transformer3wModelSpec
mainRefSystemLv,
3,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(transformerModel) {
@@ -220,7 +220,7 @@ class Transformer3wModelSpec
r,
x,
g,
- b
+ b,
) =>
uuid shouldBe transformer3wInput.getUuid
id shouldBe transformer3wInput.getId
@@ -235,21 +235,21 @@ class Transformer3wModelSpec
transformerTappingModel shouldBe expectedTappingModel
amount shouldBe transformer3wInput.getParallelDevices
powerFlowCase shouldBe PowerFlowCaseC
- (r ~= Each(3.185595567e-6)) shouldBe true
- (x ~= Each(556.0941828e-6)) shouldBe true
- (g ~= Each(0d)) shouldBe true
- (b ~= Each(0d)) shouldBe true
+ r should approximate(Each(3.185595567e-6))
+ x should approximate(Each(556.0941828e-6))
+ g should approximate(Each(0d))
+ b should approximate(Each(0d))
}
val yii: Complex = Transformer3wModel.y0(
transformerModel,
- Transformer3wModel.Transformer3wPort.A
+ Transformer3wModel.Transformer3wPort.A,
)
yii shouldBe Complex.zero
val yjj: Complex =
Transformer3wModel.y0(
transformerModel,
- Transformer3wModel.Transformer3wPort.INTERNAL
+ Transformer3wModel.Transformer3wPort.INTERNAL,
)
yjj shouldBe Complex.zero
val yij: Complex = Transformer3wModel.yij(transformerModel)
@@ -265,7 +265,7 @@ class Transformer3wModelSpec
mainRefSystemEhv,
1,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerModelHvTemp: Transformer3wModel =
Transformer3wModel(
@@ -273,7 +273,7 @@ class Transformer3wModelSpec
mainRefSystemHv,
2,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerModelLvTemp: Transformer3wModel =
Transformer3wModel(
@@ -281,7 +281,7 @@ class Transformer3wModelSpec
mainRefSystemLv,
3,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
transformerModelEhvTemp.isInOperation shouldBe true
@@ -296,7 +296,7 @@ class Transformer3wModelSpec
mainRefSystemEhv,
1,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerModelHvTemp: Transformer3wModel =
Transformer3wModel(
@@ -304,7 +304,7 @@ class Transformer3wModelSpec
mainRefSystemHv,
2,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerModelLvTemp: Transformer3wModel =
Transformer3wModel(
@@ -312,7 +312,7 @@ class Transformer3wModelSpec
mainRefSystemLv,
3,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
transformerModelEhvTemp.isInOperation shouldBe false
@@ -330,7 +330,7 @@ class Transformer3wModelSpec
mainRefSystemEhv,
1,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerModelHvTemp: Transformer3wModel =
Transformer3wModel(
@@ -338,7 +338,7 @@ class Transformer3wModelSpec
mainRefSystemHv,
2,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerModelLvTemp: Transformer3wModel =
Transformer3wModel(
@@ -346,7 +346,7 @@ class Transformer3wModelSpec
mainRefSystemLv,
3,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
transformerModelEhvTemp invokePrivate tapRatio() shouldBe 1.15
@@ -363,7 +363,7 @@ class Transformer3wModelSpec
mainRefSystemEhv,
1,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
transformerModel.disable().isSuccess shouldBe true
@@ -462,8 +462,8 @@ class Transformer3wModelSpec
-8,
-0.05d,
0.75d,
- -2
- ) /* Limit to min tap (should be -3 limited to -2) */
+ -2,
+ ), /* Limit to min tap (should be -3 limited to -2) */
)
val transformerModel: Transformer3wModel = transformerModelEhv
@@ -473,7 +473,7 @@ class Transformer3wModelSpec
currentTapPos: Int,
vChangeVal: Double,
deadBandVal: Double,
- expected: Int
+ expected: Int,
) =>
{
val vChange = Quantities.getQuantity(vChangeVal, PU)
@@ -493,19 +493,19 @@ class Transformer3wModelSpec
tapPos: Int,
yijExpected: Complex,
yiiExpected: Complex,
- yjjExpected: Complex
+ yjjExpected: Complex,
) =>
{
transformer.updateTapPos(tapPos)
val yijActual = Transformer3wModel.yij(transformer)
val yiiActual = Transformer3wModel.y0(
transformer,
- Transformer3wModel.Transformer3wPort.A
+ Transformer3wModel.Transformer3wPort.A,
)
val yjjActual =
Transformer3wModel.y0(
transformer,
- Transformer3wModel.Transformer3wPort.INTERNAL
+ Transformer3wModel.Transformer3wPort.INTERNAL,
)
/* Remark: This is not really precise. At the moment, double-based calculations do
diff --git a/src/test/scala/edu/ie3/simona/model/grid/TransformerModelSpec.scala b/src/test/scala/edu/ie3/simona/model/grid/TransformerModelSpec.scala
index f69c0e4fb3..880ca12116 100644
--- a/src/test/scala/edu/ie3/simona/model/grid/TransformerModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/grid/TransformerModelSpec.scala
@@ -11,18 +11,18 @@ import breeze.numerics.abs
import edu.ie3.datamodel.exceptions.InvalidGridException
import edu.ie3.datamodel.models.input.connector.{
ConnectorPort,
- Transformer2WInput
+ Transformer2WInput,
}
import edu.ie3.powerflow.NewtonRaphsonPF
import edu.ie3.powerflow.model.NodeData.{PresetData, StateData}
import edu.ie3.powerflow.model.StartData.WithForcedStartVoltages
import edu.ie3.powerflow.model.enums.NodeType
import edu.ie3.powerflow.model.{NodeData, PowerFlowResult}
-import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.simona.test.common.{ConfigTestData, UnitSpec}
import edu.ie3.simona.test.common.model.grid.{
TapTestData,
TransformerTestData,
- TransformerTestGrid
+ TransformerTestGrid,
}
import edu.ie3.util.quantities.PowerSystemUnits._
import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor4}
@@ -36,20 +36,16 @@ import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.util.UUID
-class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
+class TransformerModelSpec
+ extends UnitSpec
+ with TableDrivenPropertyChecks
+ with ConfigTestData {
val quantityTolerance: Double = 1e-5
val testingTolerancePf = 1e-9
implicit val electricCurrentTolerance: squants.electro.ElectricCurrent =
Amperes(1e-9)
implicit val dimensionlessTolerance: squants.Dimensionless = Each(1e-9)
- def mainRefSystem: RefSystem = {
- val nominalPower = Kilowatts(400d)
- val nominalVoltage = Kilovolts(0.4d)
- RefSystem(nominalPower, nominalVoltage)
- /* Z_Ref = 0.4 Ω, Y_Ref = 2.5 Siemens */
- }
-
"A valid TransformerInput " should {
"be validated without an exception" in new TransformerTestData {
val unmodifiedTransformerInputModel: Transformer2WInput =
@@ -58,7 +54,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
noException shouldBe thrownBy {
TransformerModel.validateInputModel(
unmodifiedTransformerInputModel,
- mainRefSystem
+ mainRefSystem,
)
}
}
@@ -77,7 +73,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
ConnectorPort.B
else
ConnectorPort.A
- }
+ },
)
val dut: TransformerModel =
@@ -85,7 +81,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
inputModel,
mainRefSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(dut) {
@@ -103,7 +99,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
r,
x,
g,
- b
+ b,
) =>
uuid should be(inputModel.getUuid)
id should be(inputModel.getId)
@@ -121,7 +117,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
_,
_,
_,
- tapSide
+ tapSide,
) =>
_currentTapPos shouldBe 0
tapSide shouldBe ConnectorPort.A
@@ -129,12 +125,12 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
amount shouldBe inputModel.getParallelDevices
voltRatioNominal shouldBe BigDecimal("25")
- (iNomHv ~= Amperes(36.373066958946424d)) shouldBe true
- (iNomLv ~= Amperes(909.3266739736606d)) shouldBe true
- (r ~= Each(7.357e-3)) shouldBe true
- (x ~= Each(24.30792e-3)) shouldBe true
- (g ~= Each(0.0)) shouldBe true
- (b ~= Each(-3.75e-3)) shouldBe true
+ iNomHv should approximate(Amperes(36.373066958946424d))
+ iNomLv should approximate(Amperes(909.3266739736606d))
+ r should approximate(Each(7.357e-3))
+ x should approximate(Each(24.30792e-3))
+ g should approximate(Each(0.0))
+ b should approximate(Each(-3.75e-3))
}
/* The following tests are with regard to the tap position = 0 */
@@ -166,7 +162,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
transformerInputTapHv,
mainRefSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
transformer2w.isInOperation shouldBe true
@@ -180,7 +176,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
transformerInputTapHv,
mainRefSystem,
earlySimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
transformer2w.isInOperation shouldBe false
@@ -195,7 +191,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
transformerInputTapHv,
mainRefSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
dut invokePrivate tapRatio() shouldBe 1.0
@@ -267,8 +263,8 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
-8,
-0.08d,
0.75d,
- -2
- ) /* Limit to min tap (should be -3 limited to -2) */
+ -2,
+ ), /* Limit to min tap (should be -3 limited to -2) */
)
forAll(cases) {
@@ -276,7 +272,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
currentTapPos: Int,
vChangeVal: Double,
deadBandVal: Double,
- expected: Int
+ expected: Int,
) =>
{
val vChange = Quantities.getQuantity(vChangeVal, PU)
@@ -285,7 +281,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
transformerModelTapHv.updateTapPos(currentTapPos)
transformerModelTapHv.computeDeltaTap(
vChange,
- deadBand
+ deadBand,
) shouldBe expected
}
}
@@ -298,7 +294,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
tapPos: Int,
yijExpected: Complex,
yiiExpected: Complex,
- yjjExpected: Complex
+ yjjExpected: Complex,
) =>
{
val transformer = tapSide match {
@@ -362,7 +358,7 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
tapPos: Int,
p: BigDecimal,
e: Double,
- f: Double
+ f: Double,
) =>
{
logger.debug(
@@ -384,7 +380,8 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
grid,
refSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
+ simonaConfig,
)
gridModel.gridComponents.transformers
@@ -393,20 +390,20 @@ class TransformerModelSpec extends UnitSpec with TableDrivenPropertyChecks {
val admittanceMatrix =
GridModel.composeAdmittanceMatrix(
nodeUuidToIndexMap,
- gridModel.gridComponents
+ gridModel.gridComponents,
)
val operationPoint =
Array(
PresetData(0, NodeType.SL, Complex.zero),
- PresetData(1, NodeType.PQ, Complex(p.doubleValue, 0d))
+ PresetData(1, NodeType.PQ, Complex(p.doubleValue, 0d)),
)
val powerFlow = new NewtonRaphsonPF(
epsilon,
maxIterations,
admittanceMatrix,
- Option.apply(Vector(0, 1))
+ Option.apply(Vector(0, 1)),
)
val result = powerFlow.calculate(operationPoint, startData)
diff --git a/src/test/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatSpec.scala
index b77e2a8ed9..43f4ef8baa 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/ApparentPowerAndHeatSpec.scala
@@ -9,12 +9,15 @@ package edu.ie3.simona.model.participant
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ApparentPowerAndHeat
import edu.ie3.simona.model.participant.ApparentPowerAndHeatSpec.ApparentPowerAndHeatMock
import edu.ie3.simona.model.participant.CalcRelevantData.FixedRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
import edu.ie3.simona.model.participant.control.QControl.CosPhiFixed
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
+import edu.ie3.simona.ontology.messages.flex.FlexibilityMessage
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.scala.OperationInterval
import edu.ie3.util.scala.quantities.{Megavars, ReactivePower, Vars}
-import squants.Each
-import squants.energy.{Kilowatts, Megawatts, Power, Watts}
+import squants.energy.{Kilowatts, Megawatts, Watts}
+import squants.{Each, Power}
import java.util.UUID
@@ -27,13 +30,13 @@ class ApparentPowerAndHeatSpec extends UnitSpec {
ApparentPowerAndHeatMock.calculatePower(
50L,
Each(1.0d),
- FixedRelevantData
+ ConstantState,
+ FixedRelevantData,
) match {
case ApparentPowerAndHeat(p, q, qDot) =>
- (p =~ Megawatts(0d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
- (qDot =~ Megawatts(0d)) shouldBe true
-
+ p should approximate(Megawatts(0d))
+ q should approximate(Megavars(0d))
+ qDot should approximate(Megawatts(0d))
}
}
}
@@ -42,13 +45,13 @@ class ApparentPowerAndHeatSpec extends UnitSpec {
ApparentPowerAndHeatMock.calculatePower(
10L,
Each(1.0d),
- FixedRelevantData
+ ConstantState,
+ FixedRelevantData,
) match {
case ApparentPowerAndHeat(p, q, qDot) =>
- (p =~ Megawatts(43d)) shouldBe true
- (q =~ Megavars(0d)) shouldBe true
- (qDot =~ Megawatts(42d)) shouldBe true
-
+ p should approximate(Megawatts(43d))
+ q should approximate(Megavars(0d))
+ qDot should approximate(Megawatts(42d))
}
}
}
@@ -57,16 +60,23 @@ class ApparentPowerAndHeatSpec extends UnitSpec {
object ApparentPowerAndHeatSpec {
object ApparentPowerAndHeatMock
- extends SystemParticipant[FixedRelevantData.type, ApparentPowerAndHeat](
+ extends SystemParticipant[
+ FixedRelevantData.type,
+ ApparentPowerAndHeat,
+ ConstantState.type,
+ ](
UUID.randomUUID(),
"ParticipantMock",
OperationInterval.apply(0L, 42L),
1.0,
CosPhiFixed(0.97),
Kilowatts(42d),
- 0.97
+ 0.97,
)
- with ApparentPowerAndHeatParticipant[FixedRelevantData.type] {
+ with ApparentPowerAndHeatParticipant[
+ FixedRelevantData.type,
+ ConstantState.type,
+ ] {
this.enable()
/** Calculate the heat of the asset. As for electrical assets, positive
@@ -81,9 +91,9 @@ object ApparentPowerAndHeatSpec {
*/
override def calculateHeat(
tick: Long,
- data: CalcRelevantData.FixedRelevantData.type
- ): Power =
- Megawatts(42d)
+ modelState: ConstantState.type,
+ data: CalcRelevantData.FixedRelevantData.type,
+ ): Power = Megawatts(42d)
/** Calculate the active power behaviour of the model
*
@@ -93,8 +103,37 @@ object ApparentPowerAndHeatSpec {
* Active power
*/
override protected def calculateActivePower(
- data: CalcRelevantData.FixedRelevantData.type
- ): Power =
- Megawatts(43d)
+ modelState: ConstantState.type,
+ data: CalcRelevantData.FixedRelevantData.type,
+ ): Power = Megawatts(43d)
+
+ /** @param data
+ * @param lastState
+ * @return
+ * flex options
+ */
+ override def determineFlexOptions(
+ data: CalcRelevantData.FixedRelevantData.type,
+ lastState: ModelState.ConstantState.type,
+ ): FlexibilityMessage.ProvideFlexOptions =
+ ProvideMinMaxFlexOptions.noFlexOption(
+ this.getUuid,
+ calculateActivePower(ConstantState, data),
+ )
+
+ /** @param data
+ * @param lastState
+ * @param setPower
+ * power that has been set by EmAgent
+ * @return
+ * updated relevant data and an indication at which circumstances flex
+ * options will change next
+ */
+ override def handleControlledPowerChange(
+ data: CalcRelevantData.FixedRelevantData.type,
+ lastState: ModelState.ConstantState.type,
+ setPower: Power,
+ ): (ModelState.ConstantState.type, FlexChangeIndicator) =
+ (lastState, FlexChangeIndicator())
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/CharacteristicSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/CharacteristicSpec.scala
index 0a5aa02595..1a0baf5b1f 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/CharacteristicSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/CharacteristicSpec.scala
@@ -45,28 +45,28 @@ class CharacteristicSpec extends UnitSpec with CharacteristicTestData {
interpolation1 match {
case (x, y) =>
- (x ~= Each(1)) shouldBe true
- (y ~= Each(2)) shouldBe true
+ x should approximate(Each(1))
+ y should approximate(Each(2))
}
interpolation2 match {
case (x, y) =>
- (x ~= Each(2)) shouldBe true
- (y ~= Each(4)) shouldBe true
+ x should approximate(Each(2))
+ y should approximate(Each(4))
}
interpolation3 match {
case (x, y) =>
- (x ~= Each(3)) shouldBe true
- (y ~= Each(8)) shouldBe true
+ x should approximate(Each(3))
+ y should approximate(Each(8))
}
interpolation4 match {
case (x, y) =>
- (x ~= Each(1.5)) shouldBe true
- (y ~= Each(3)) shouldBe true
+ x should approximate(Each(1.5))
+ y should approximate(Each(3))
}
interpolation5 match {
case (x, y) =>
- (x ~= Each(2.5)) shouldBe true
- (y ~= Each(6)) shouldBe true
+ x should approximate(Each(2.5))
+ y should approximate(Each(6))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/FixedFeedInModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/FixedFeedInModelSpec.scala
index 8f1698f7be..16665692f6 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/FixedFeedInModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/FixedFeedInModelSpec.scala
@@ -35,7 +35,7 @@ class FixedFeedInModelSpec
val simonaConfig: SimonaConfig =
createSimonaConfig(
LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0.0))
+ LoadReference.ActivePower(Kilowatts(0.0)),
)
val modelConfig = ConfigUtil
.ParticipantConfigUtil(
@@ -47,7 +47,7 @@ class FixedFeedInModelSpec
fixedFeedInput,
modelConfig,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
inside(actualModel) {
@@ -58,16 +58,18 @@ class FixedFeedInModelSpec
scalingFactor,
qControl,
sRated,
- cosPhiRated
+ cosPhiRated,
) =>
uuid shouldBe fixedFeedInput.getUuid
id shouldBe fixedFeedInput.getId
operationInterval shouldBe defaultOperationInterval
scalingFactor shouldBe foreSeenScalingFactor
qControl shouldBe QControl(fixedFeedInput.getqCharacteristics)
- (sRated ~= Megawatts(
- fixedFeedInput.getsRated().to(MEGAVOLTAMPERE).getValue.doubleValue
- )) shouldBe true
+ sRated should approximate(
+ Megawatts(
+ fixedFeedInput.getsRated().to(MEGAVOLTAMPERE).getValue.doubleValue
+ )
+ )
cosPhiRated shouldBe fixedFeedInput.getCosPhiRated
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala
index b1723c3650..ef2e082265 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelSpec.scala
@@ -8,22 +8,29 @@ package edu.ie3.simona.model.participant
import edu.ie3.simona.model.participant.HpModel.HpState
import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState
+import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState
+import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{
+ HouseTemperatureLowerBoundaryReached,
+ HouseTemperatureUpperBoundaryReached,
+}
+import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.util.scala.quantities.WattsPerKelvin
import org.scalatest.prop.TableDrivenPropertyChecks
-import squants.energy.{Kilowatts, Watts}
+import squants.energy.{KilowattHours, Kilowatts, Watts}
import squants.thermal.Celsius
-import squants.{Power, Temperature}
+import squants.{Kelvin, Power, Temperature}
class HpModelSpec
extends UnitSpec
with TableDrivenPropertyChecks
with HpModelTestData {
- implicit val tempTolerance: Temperature = Celsius(1e-3)
+ implicit val tempTolerance: Temperature = Kelvin(1e-3)
implicit val powerTolerance: Power = Watts(1e-3)
"Testing the heat pump model" when {
-
"calculating the next state with different states" should {
"deliver correct tick, power and running state" in {
val cases = Table(
@@ -31,128 +38,159 @@ class HpModelSpec
"state",
"expectedRunningState",
"expectedActivePower",
- "expectedInnerTemperature"
+ "expectedInnerTemperature",
+ "expectedNextThreshold",
),
(
HpState(
isRunning = false,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(0d),
Kilowatts(0d),
- thermalState(Celsius(17d))
+ thermalState(Celsius(17d)),
+ None,
),
true,
95,
- 15.6
+ 15.6,
+ Some(HouseTemperatureUpperBoundaryReached(31711L)),
),
(
HpState(
isRunning = false,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(0d),
Kilowatts(0d),
- thermalState(Celsius(18))
+ thermalState(Celsius(18)),
+ None,
),
true,
95,
- 16.4
+ 16.4,
+ Some(HouseTemperatureUpperBoundaryReached(30642L)),
),
(
HpState(
isRunning = false,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(0d),
Kilowatts(0d),
- thermalState(Celsius(20))
+ thermalState(Celsius(20)),
+ None,
),
true,
95,
- 18.0
+ 18.0,
+ Some(HouseTemperatureUpperBoundaryReached(27771L)),
),
(
HpState(
isRunning = false,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(0d),
Kilowatts(0d),
- thermalState(Celsius(22))
+ thermalState(Celsius(22)),
+ None,
),
false,
0,
- 19.6
+ 19.6,
+ Some(HouseTemperatureLowerBoundaryReached(13200L)),
),
(
HpState(
isRunning = false,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(0d),
Kilowatts(0d),
- thermalState(Celsius(23))
+ thermalState(Celsius(23)),
+ None,
),
false,
0,
- 20.4
+ 20.4,
+ Some(HouseTemperatureLowerBoundaryReached(15508L)),
),
(
HpState(
isRunning = true,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(95d),
Kilowatts(80d),
- thermalState(Celsius(17))
+ thermalState(Celsius(17)),
+ None,
),
true,
95,
- 15.6
+ 15.6,
+ Some(HouseTemperatureUpperBoundaryReached(31711L)),
),
(
HpState(
isRunning = true,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(95d),
Kilowatts(80d),
- thermalState(Celsius(18))
+ thermalState(Celsius(18)),
+ None,
),
true,
95,
- 16.4
+ 16.4,
+ Some(HouseTemperatureUpperBoundaryReached(30642L)),
),
(
HpState(
isRunning = true,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(95d),
Kilowatts(80d),
- thermalState(Celsius(20))
+ thermalState(Celsius(20)),
+ None,
),
true,
95,
- 18.0
+ 18.0,
+ Some(HouseTemperatureUpperBoundaryReached(27771L)),
),
(
HpState(
isRunning = true,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(95d),
Kilowatts(80d),
- thermalState(Celsius(22))
+ thermalState(Celsius(22)),
+ None,
),
true,
95,
- 19.6
+ 19.6,
+ Some(HouseTemperatureUpperBoundaryReached(23200L)),
),
(
HpState(
isRunning = true,
0,
+ Some(hpData.ambientTemperature),
Kilowatts(95d),
Kilowatts(80d),
- thermalState(Celsius(25))
+ thermalState(Celsius(25)),
+ None,
),
false,
0,
- 22.0
- )
+ 22.0,
+ Some(HouseTemperatureLowerBoundaryReached(19200L)),
+ ),
)
forAll(cases) {
@@ -160,30 +198,88 @@ class HpModelSpec
state,
expectedRunningState,
expectedActivePower,
- expectedInnerTemperature
+ expectedInnerTemperature,
+ expectedNextThreshold,
) =>
- val data = hpData(state)
+ val data = hpData
val house = thermalHouse(18, 22)
val grid = thermalGrid(house)
val hp = hpModel(grid)
- hp.calculateNextState(data) match {
+ hp.determineState(state, data) match {
case HpState(
isRunning,
- lastTimeTick,
+ _,
+ _,
activePower,
_,
- ThermalGridState(Some(thermalHouseState), _)
+ ThermalGridState(Some(thermalHouseState), _),
+ maybeThreshold,
) =>
isRunning shouldBe expectedRunningState
- (activePower =~ Kilowatts(expectedActivePower)) shouldBe true
-
- (thermalHouseState.innerTemperature =~ Celsius(
- expectedInnerTemperature
- )) shouldBe true
+ activePower should approximate(Kilowatts(expectedActivePower))
+ thermalHouseState.innerTemperature should approximate(
+ Celsius(
+ expectedInnerTemperature
+ )
+ )
+ maybeThreshold shouldBe expectedNextThreshold
+ case unexpected =>
+ fail(s"Expected a hp state but got none $unexpected.")
}
}
}
}
+
+ "determining the flexibility options" when {
+ "the house is heated up and storage has space" should {
+ "deliver positive flexibility" in {
+ val house = thermalHouse(18, 22)
+ .copy(ethLosses = WattsPerKelvin(200))
+ val grid = thermalGrid(house, Some(thermalStorage))
+ val hp = hpModel(grid)
+ // Tick, at which the house is heated up
+ val relevantData = hpData.copy(currentTick = 2763L)
+ val thermalState = ThermalGridState(
+ Some(
+ ThermalHouseState(
+ 0L,
+ Celsius(21),
+ Kilowatts(80),
+ )
+ ),
+ Some(
+ ThermalStorageState(
+ 0L,
+ KilowattHours(20),
+ Kilowatts(0),
+ )
+ ),
+ )
+ val lastState = HpState(
+ isRunning = true,
+ 0,
+ Some(hpData.ambientTemperature),
+ Kilowatts(95.0),
+ Kilowatts(80.0),
+ thermalState,
+ Some(HouseTemperatureUpperBoundaryReached(7995L)),
+ )
+
+ hp.determineFlexOptions(relevantData, lastState) match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ referencePower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe hp.uuid
+ referencePower should approximate(Kilowatts(95.0))
+ minPower should approximate(Kilowatts(0.0))
+ maxPower should approximate(Kilowatts(95.0))
+ }
+ }
+ }
+ }
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/HpModelTestData.scala b/src/test/scala/edu/ie3/simona/model/participant/HpModelTestData.scala
index 464e8f9653..561b6a6074 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/HpModelTestData.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/HpModelTestData.scala
@@ -6,17 +6,17 @@
package edu.ie3.simona.model.participant
-import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput}
import edu.ie3.datamodel.models.input.system.HpInput
import edu.ie3.datamodel.models.input.system.`type`.HpTypeInput
import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
import edu.ie3.datamodel.models.input.thermal.{
ThermalBusInput,
- ThermalHouseInput
+ ThermalHouseInput,
}
+import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.datamodel.models.{OperationTime, StandardUnits}
-import edu.ie3.simona.model.participant.HpModel.{HpRelevantData, HpState}
+import edu.ie3.simona.model.participant.HpModel.HpRelevantData
import edu.ie3.simona.model.participant.control.QControl
import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState
@@ -24,7 +24,7 @@ import edu.ie3.simona.model.thermal.{
CylindricalThermalStorage,
ThermalGrid,
ThermalHouse,
- ThermalStorage
+ ThermalStorage,
}
import edu.ie3.util.quantities.PowerSystemUnits
import edu.ie3.util.scala.OperationInterval
@@ -48,7 +48,7 @@ trait HpModelTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.LV,
- 2
+ 2,
)
protected val hpTypeInput = new HpTypeInput(
@@ -58,7 +58,7 @@ trait HpModelTestData {
Quantities.getQuantity(200d, PowerSystemUnits.EURO_PER_MEGAWATTHOUR),
Quantities.getQuantity(100, PowerSystemUnits.KILOVOLTAMPERE),
0.95,
- Quantities.getQuantity(15, PowerSystemUnits.KILOWATT)
+ Quantities.getQuantity(15, PowerSystemUnits.KILOWATT),
)
protected val hpInputModel = new HpInput(
@@ -69,7 +69,7 @@ trait HpModelTestData {
nodeInput,
thermalBus,
new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
- hpTypeInput
+ hpTypeInput,
)
protected def hpModel(thermalGrid: ThermalGrid) = new HpModel(
@@ -81,16 +81,16 @@ trait HpModelTestData {
Kilowatts(100d),
0.95,
Kilowatts(15d),
- thermalGrid
+ thermalGrid,
)
protected def thermalGrid(
thermalHouse: ThermalHouse,
- thermalStorage: Option[ThermalStorage] = None
+ thermalStorage: Option[ThermalStorage] = None,
): ThermalGrid =
ThermalGrid(
Some(thermalHouse),
- thermalStorage
+ thermalStorage,
)
private val thermHouseUuid: UUID =
@@ -101,7 +101,7 @@ trait HpModelTestData {
protected def thermalHouse(
lowerTemperatureBoundary: Double,
- upperTemperatureBoundary: Double
+ upperTemperatureBoundary: Double,
): ThermalHouse = ThermalHouse(
new ThermalHouseInput(
thermHouseUuid,
@@ -111,10 +111,10 @@ trait HpModelTestData {
Quantities.getQuantity(10.0, StandardUnits.HEAT_CAPACITY),
Quantities.getQuantity(
(lowerTemperatureBoundary + upperTemperatureBoundary) / 2.0,
- Units.CELSIUS
+ Units.CELSIUS,
),
Quantities.getQuantity(upperTemperatureBoundary, Units.CELSIUS),
- Quantities.getQuantity(lowerTemperatureBoundary, Units.CELSIUS)
+ Quantities.getQuantity(lowerTemperatureBoundary, Units.CELSIUS),
)
)
@@ -123,28 +123,28 @@ trait HpModelTestData {
"thermal storage",
OperatorInput.NO_OPERATOR_ASSIGNED,
OperationTime.notLimited(),
- null,
+ thermalBus,
KilowattHours(20d),
KilowattHours(500d),
Kilowatts(10d),
- KilowattHours(0d)
+ KilowattHours(0d),
)
- def thermalState(
+ protected def thermalState(
temperature: Temperature,
- qDot: Power = Kilowatts(0d)
+ qDot: Power = Kilowatts(0d),
): ThermalGridState = ThermalGridState(
Some(
ThermalHouseState(
0L,
temperature,
- qDot
+ qDot,
)
),
- None
+ None,
)
- protected def hpData(hpState: HpState): HpRelevantData =
- HpRelevantData(hpState, 7200, Celsius(10d))
+ protected def hpData: HpRelevantData =
+ HpRelevantData(7200, Celsius(10d))
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala
new file mode 100644
index 0000000000..358090b0a3
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/participant/evcs/EvcsModelSpec.scala
@@ -0,0 +1,973 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs
+
+import edu.ie3.datamodel.models.input.system.`type`.evcslocation.EvcsLocationType
+import edu.ie3.simona.model.participant.FlexChangeIndicator
+import edu.ie3.simona.model.participant.evcs.EvcsModel.{
+ EvcsRelevantData,
+ EvcsState,
+ ScheduleEntry,
+}
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.simona.test.common.input.EvcsInputTestData
+import edu.ie3.simona.test.common.model.MockEvModel
+import edu.ie3.simona.test.helper.TableDrivenHelper
+import edu.ie3.simona.util.TickUtil.TickLong
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
+import org.scalatest.prop.TableDrivenPropertyChecks
+import squants.Each
+import squants.energy.{KilowattHours, Kilowatts}
+
+import java.util.UUID
+import scala.collection.immutable.SortedSet
+
+class EvcsModelSpec
+ extends UnitSpec
+ with TableDrivenPropertyChecks
+ with TableDrivenHelper
+ with EvcsInputTestData {
+
+ private val simulationStart = evcsStandardModel.simulationStartDate
+
+ private implicit val energyTolerance: squants.Energy = KilowattHours(1e-10)
+ private implicit val powerTolerance: squants.Power = Kilowatts(1e-10)
+
+ "An EVCS model" should {
+
+ "calculate new schedules correctly" when {
+
+ "configured as a charging hub" in {
+ val evcsModel = evcsStandardModel.copy(
+ strategy = ChargingStrategy.CONSTANT_POWER,
+ locationType = EvcsLocationType.CHARGING_HUB_TOWN,
+ )
+
+ val evModel = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV",
+ 10.0.asKiloWatt, // AC is relevant,
+ 20.0.asKiloWatt, // DC is not
+ 20.0.asKiloWattHour,
+ 5.0.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ val actualSchedule = evcsModel.calculateNewScheduling(
+ EvcsRelevantData(
+ 3600L,
+ Seq.empty,
+ ),
+ Seq(evModel),
+ )
+
+ actualSchedule shouldBe Map(
+ evModel.uuid ->
+ // ending early at 9000 because of max power charging
+ SortedSet(ScheduleEntry(3600L, 9000L, Kilowatts(10.0)))
+ )
+ }
+
+ "configured as a home cs with constant power strategy" in {
+ val evcsModel = evcsStandardModel.copy(
+ strategy = ChargingStrategy.CONSTANT_POWER,
+ locationType = EvcsLocationType.HOME,
+ )
+
+ val evModel = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV",
+ 10.0.asKiloWatt, // AC is relevant,
+ 20.0.asKiloWatt, // DC is not
+ 20.0.asKiloWattHour,
+ 15.0.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ val actualSchedule = evcsModel.calculateNewScheduling(
+ EvcsRelevantData(
+ 3600L,
+ Seq.empty,
+ ),
+ Seq(evModel),
+ )
+
+ actualSchedule shouldBe Map(
+ evModel.uuid ->
+ // using 2.5 kW with constant power charging
+ SortedSet(ScheduleEntry(3600L, 10800L, Kilowatts(2.5)))
+ )
+ }
+ }
+
+ "apply schedules correctly" when {
+
+ "being provided with a ChargingSchedule consisting of one entry" in {
+ val evcsModel = evcsStandardModel
+
+ val currentTick = 3600L
+
+ val cases = Table(
+ (
+ "storedEnergy",
+ "chargeStart",
+ "chargeEnd",
+ "lastCalcTick",
+ "power",
+ "expectedStored",
+ ),
+ // charging ends before currentTick
+ (0.0, 0L, 2700L, 0L, 5.0, 3.75),
+ (0.0, 0L, 1800L, 0L, 2.5, 1.25),
+ (0.0, 900L, 2700L, 0L, 5.0, 2.5),
+ (2.5, 0L, 2700L, 1800L, 5.0, 3.75),
+ (2.5, 0L, 2700L, 900L, 5.0, 5.0),
+ // charging ends at currentTick
+ (0.0, 0L, 3600L, 0L, 5.0, 5.0),
+ (0.0, 0L, 3600L, 0L, 2.5, 2.5),
+ (0.0, 900L, 3600L, 0L, 5.0, 3.75),
+ (2.5, 0L, 3600L, 1800L, 5.0, 5.0),
+ (2.5, 0L, 3600L, 2700L, 5.0, 3.75),
+ // charging ends after currentTick
+ (0.0, 0L, 7200L, 0L, 5.0, 5.0),
+ (0.0, 0L, 7200L, 0L, 2.5, 2.5),
+ (0.0, 900L, 7200L, 0L, 5.0, 3.75),
+ (2.5, 0L, 7200L, 1800L, 5.0, 5.0),
+ (2.5, 0L, 7200L, 2700L, 5.0, 3.75),
+ )
+
+ forAll(cases) {
+ (
+ storedEnergy,
+ chargeStart,
+ chargeEnd,
+ lastCalcTick,
+ power,
+ expectedStored,
+ ) =>
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "TestEv1",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ storedEnergy.asKiloWattHour,
+ 7200L, // is ignored here
+ )
+ )
+
+ val entry =
+ SortedSet(ScheduleEntry(chargeStart, chargeEnd, Kilowatts(power)))
+
+ val state = EvcsState(
+ Seq(ev),
+ Map(ev.uuid -> entry),
+ lastCalcTick,
+ )
+
+ val actualOutput = evcsModel.applySchedule(
+ state,
+ currentTick,
+ )
+
+ actualOutput should have size 1
+ val actualEv =
+ actualOutput.headOption.getOrElse(
+ fail("No charging schedule provided.")
+ )
+
+ actualEv.uuid shouldBe ev.uuid
+ actualEv.id shouldBe ev.id
+ actualEv.sRatedAc shouldBe ev.sRatedAc
+ actualEv.sRatedDc shouldBe ev.sRatedDc
+ actualEv.eStorage shouldBe ev.eStorage
+ actualEv.storedEnergy should approximate(
+ KilowattHours(expectedStored)
+ )
+ actualEv.departureTick shouldBe ev.departureTick
+
+ }
+
+ }
+ }
+
+ "calculate results correctly" when {
+
+ "one EV is parked and charging" in {
+
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "TestEv1",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ 0d.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ val schedule = SortedSet(
+ ScheduleEntry(3600L, 5400L, Kilowatts(2d)),
+ ScheduleEntry(7200L, 9000L, Kilowatts(4d)),
+ )
+
+ // tick, p in kW
+ val generalEvResults =
+ IndexedSeq(
+ (0L, 0d),
+ (3600L, 2d),
+ (5400L, 0d),
+ (7200L, 4d),
+ (9000L, 0d),
+ )
+
+ val cases = Table(
+ (
+ "lastTick",
+ "currentTick",
+ "firstResultIndex",
+ "lastResultIndex",
+ ),
+ (1800L, 10800L, 0, 5),
+ (3600L, 10000L, 1, 5),
+ (2700L, 9000L, 0, 4),
+ (3600L, 9000L, 1, 4),
+ (3660L, 9000L, 1, 4),
+ (5400L, 9001L, 2, 5),
+ (5400L, 8999L, 2, 4),
+ (8999L, 9000L, 3, 4),
+ (8999L, 9060L, 3, 5),
+ )
+
+ forAll(cases) {
+ (
+ lastTick,
+ currentTick,
+ firstResultIndex,
+ lastResultIndex,
+ ) =>
+ val lastState = EvcsState(
+ Seq(ev),
+ Map(ev.uuid -> schedule),
+ lastTick,
+ )
+
+ val (actualEvResults, actualEvcsResults) =
+ evcsStandardModel.createResults(
+ lastState,
+ currentTick,
+ Each(1d),
+ )
+
+ val (_, firstPower) = generalEvResults(firstResultIndex)
+
+ val expectedEvResults = generalEvResults
+ .slice(firstResultIndex + 1, lastResultIndex)
+ .prepended(lastTick, firstPower)
+
+ actualEvResults should have size expectedEvResults.size
+ actualEvResults.zip(expectedEvResults).foreach {
+ case (actual, (startTick, p)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getInputModel shouldBe ev.uuid
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ }
+
+ actualEvcsResults should have size expectedEvResults.size
+ actualEvcsResults.zip(expectedEvResults).foreach {
+ case (actual, (startTick, p)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getInputModel shouldBe evcsStandardModel.getUuid
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ }
+ }
+
+ }
+
+ "two EVs are parked and charging" in {
+
+ val ev1 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "TestEv1",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ 0d.asKiloWattHour,
+ 18000L,
+ )
+ )
+
+ val ev2 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "TestEv2",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ 0d.asKiloWattHour,
+ 18000L,
+ )
+ )
+
+ val schedule1 =
+ SortedSet(
+ ScheduleEntry(3600L, 7200L, Kilowatts(2d)),
+ ScheduleEntry(9000L, 14400L, Kilowatts(3d)),
+ )
+
+ val schedule2 = SortedSet(
+ ScheduleEntry(5400L, 9000L, Kilowatts(2d))
+ )
+
+ val lastTick = 1800L
+ val currentTick = 10800L
+
+ val lastState = EvcsState(
+ Seq(ev1, ev2),
+ Map(ev1.uuid -> schedule1, ev2.uuid -> schedule2),
+ lastTick,
+ )
+
+ val (actualEvResults, actualEvcsResults) =
+ evcsStandardModel.createResults(
+ lastState,
+ currentTick,
+ Each(1d),
+ )
+
+ // tick, p in kW, soc in %
+ val expectedEv1Results =
+ Seq(
+ (1800L, 0d, 0d),
+ (3600L, 2d, 0d),
+ (7200L, 0d, 20d),
+ (9000L, 3d, 20d),
+ )
+
+ // tick, p in kW, soc in %
+ val expectedEv2Results =
+ Seq(
+ (1800L, 0d, 0d),
+ (5400L, 2d, 0d),
+ (9000L, 0d, 20d),
+ )
+
+ // tick, p in kW
+ val expectedEvcsResults =
+ Seq(
+ (1800L, 0d),
+ (3600L, 2d),
+ (5400L, 4d),
+ (7200L, 2d),
+ (9000L, 3d),
+ )
+
+ actualEvResults should have size expectedEv1Results.size + expectedEv2Results.size
+
+ val actualEv1Results =
+ actualEvResults.filter(_.getInputModel == ev1.uuid)
+ actualEv1Results should have size expectedEv1Results.size
+ actualEv1Results.zip(expectedEv1Results).foreach {
+ case (actual, (startTick, p, soc)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ actual.getSoc should beEquivalentTo(soc.asPercent)
+ }
+
+ val actualEv2Results =
+ actualEvResults.filter(_.getInputModel == ev2.uuid)
+ actualEv2Results should have size expectedEv2Results.size
+ actualEv2Results.zip(expectedEv2Results).foreach {
+ case (actual, (startTick, p, soc)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ actual.getSoc should beEquivalentTo(soc.asPercent)
+ }
+
+ actualEvcsResults should have size expectedEvcsResults.size
+ actualEvcsResults.zip(expectedEvcsResults).foreach {
+ case (actual, (startTick, p)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getInputModel shouldBe evcsStandardModel.getUuid
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ }
+ }
+
+ "EV is departing at current tick" in {
+
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "TestEv",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ 0d.asKiloWattHour,
+ 7200L, // equals the current tick
+ )
+ )
+
+ val schedule = SortedSet(
+ ScheduleEntry(3600L, 7200L, Kilowatts(2d))
+ )
+
+ val lastTick = 1800L
+ val currentTick = 7200L
+
+ val lastState = EvcsState(
+ Seq(ev),
+ Map(ev.uuid -> schedule),
+ lastTick,
+ )
+
+ val (actualEvResults, actualEvcsResults) =
+ evcsStandardModel.createResults(
+ lastState,
+ currentTick,
+ Each(1d),
+ )
+
+ // tick, p in kW, soc in %
+ val expectedEvResults =
+ Seq(
+ (1800L, 0d, 0d),
+ (3600L, 2d, 0d),
+ // this result normally does not appear
+ // if EV does not depart at current tick
+ (7200L, 0d, 20d),
+ )
+
+ // tick, p in kW
+ val expectedEvcsResults =
+ Seq(
+ (1800L, 0d),
+ (3600L, 2d),
+ )
+
+ actualEvResults should have size expectedEvResults.size
+
+ actualEvResults should have size expectedEvResults.size
+ actualEvResults.zip(expectedEvResults).foreach {
+ case (actual, (startTick, p, soc)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ actual.getSoc should beEquivalentTo(soc.asPercent)
+ }
+
+ actualEvcsResults should have size expectedEvcsResults.size
+ actualEvcsResults.zip(expectedEvcsResults).foreach {
+ case (actual, (startTick, p)) =>
+ actual.getTime shouldBe startTick.toDateTime(simulationStart)
+ actual.getInputModel shouldBe evcsStandardModel.getUuid
+ actual.getP should beEquivalentTo(p.asKiloWatt)
+ actual.getQ should beEquivalentTo(0d.asKiloVar)
+ }
+ }
+
+ }
+
+ "calculate flex options correctly" when {
+
+ "charging with constant power and allowing v2g" in {
+ val evcsModel =
+ evcsStandardModel.copy(strategy = ChargingStrategy.CONSTANT_POWER)
+
+ val currentTick = 7200L
+
+ val data = EvcsRelevantData(
+ currentTick,
+ Seq.empty,
+ )
+
+ val cases = Table(
+ (
+ "lastStored1",
+ "lastStored2",
+ "lastPower2",
+ "expectedPRef",
+ "expectedPMin",
+ "expectedPMax",
+ ),
+
+ /* 1: empty */
+ // 2: empty
+ (0.0, 0.0, 0.0, 15.0, 15.0, 15.0),
+ // 2: at lower margin
+ (0.0, 3.0, 0.0, 15.0, 10.0, 15.0),
+ // 2: mid-way full (charged to 7.5 kWh), forced charging
+ (0.0, 0.0, 5.0, 13.75, 10.0, 15.0),
+ // 2: mid-way full (set to 7.5 kWh), forced charging
+ (0.0, 7.5, 0.0, 13.75, 10.0, 15.0),
+ // 2: almost full (12.5 kWh), forced charging
+ (0.0, 5.0, 5.0, 11.25, 10.0, 15.0),
+ // 2: full (set), forced charging
+ (0.0, 15.0, 0.0, 10.0, 10.0, 10.0),
+
+ /* 1: at lower margin (set to 2 kWh) */
+ // 2: empty
+ (2.0, 0.0, 0.0, 13.0, 5.0, 15.0),
+ // 2: at lower margin
+ (2.0, 3.0, 0.0, 13.0, 0.0, 15.0),
+ // 2: mid-way full (charged to 7.5 kWh)
+ (2.0, 0.0, 5.0, 11.75, -5.0, 15.0),
+ // 2: mid-way full (set to 7.5 kWh)
+ (2.0, 7.5, 0.0, 11.75, -5.0, 15.0),
+ // 2: almost full (12.5 kWh)
+ (2.0, 5.0, 5.0, 9.25, -5.0, 15.0),
+ // 2: full (set)
+ (2.0, 15.0, 0.0, 8.0, -5.0, 10.0),
+
+ /* 1: mid-way full (set to 5 kWh) */
+ // 2: empty, forced charging
+ (5.0, 0.0, 0.0, 10.0, 5.0, 15.0),
+ // 2: mid-way full (charged to 7.5 kWh)
+ (5.0, 0.0, 5.0, 8.75, -15.0, 15.0),
+ // 2: mid-way full (set to 7.5 kWh)
+ (5.0, 7.5, 0.0, 8.75, -15.0, 15.0),
+ // 2: almost full (12.5 kWh)
+ (5.0, 5.0, 5.0, 6.25, -15.0, 15.0),
+ // 2: full (set)
+ (5.0, 15.0, 0.0, 5.0, -15.0, 10.0),
+
+ /* 1: full (set to 10 kWh) */
+ // 2: empty, forced charging
+ (10.0, 0.0, 0.0, 5.0, 5.0, 5.0),
+ // 2: mid-way full (charged to 7.5 kWh)
+ (10.0, 0.0, 5.0, 3.75, -15.0, 5.0),
+ // 2: mid-way full (set to 7.5 kWh)
+ (10.0, 7.5, 0.0, 3.75, -15.0, 5.0),
+ // 2: almost full (12.5 kWh)
+ (10.0, 5.0, 5.0, 1.25, -15.0, 5.0),
+ // 2: full (set)
+ (10.0, 15.0, 0.0, 0.0, -15.0, 0.0),
+ )
+
+ forAll(cases) {
+ (
+ lastStored1,
+ lastStored2,
+ lastPower2,
+ expectedPRef,
+ expectedPMin,
+ expectedPMax,
+ ) =>
+ // stays one more hour
+ val ev1 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 1",
+ 10.0.asKiloWatt, // AC is relevant,
+ 20.0.asKiloWatt, // DC is not
+ 10.0.asKiloWattHour,
+ lastStored1.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ // has not been charging before
+ val schedule1 = SortedSet(
+ ScheduleEntry(3600L, 7200L, Kilowatts(0d))
+ )
+
+ // stays two more hours
+ val ev2 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 2",
+ 5.0.asKiloWatt, // AC is relevant,
+ 10.0.asKiloWatt, // DC is not
+ 15.0.asKiloWattHour,
+ lastStored2.asKiloWattHour,
+ 14400L,
+ )
+ )
+
+ // has been charging for 1.5 hours with given power
+ val schedule2 = SortedSet(
+ ScheduleEntry(0L, 5400L, Kilowatts(lastPower2))
+ )
+
+ evcsModel.determineFlexOptions(
+ data,
+ EvcsState(
+ Seq(ev1, ev2),
+ Map(ev1.uuid -> schedule1, ev2.uuid -> schedule2),
+ 0L,
+ ),
+ ) match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ refPower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsModel.getUuid
+ refPower should approximate(Kilowatts(expectedPRef))
+ minPower should approximate(Kilowatts(expectedPMin))
+ maxPower should approximate(Kilowatts(expectedPMax))
+ }
+ }
+
+ }
+
+ "charging with maximum power and allowing v2g" in {
+ val evcsModel =
+ evcsStandardModel.copy(strategy = ChargingStrategy.MAX_POWER)
+
+ val currentTick = 7200L
+
+ val data = EvcsRelevantData(
+ currentTick,
+ Seq.empty,
+ )
+
+ val cases = Table(
+ (
+ "lastStored1",
+ "lastStored2",
+ "lastPower2",
+ "expectedPRef",
+ "expectedPMin",
+ "expectedPMax",
+ ),
+
+ /* 1: empty */
+ // 2: empty
+ (0.0, 0.0, 0.0, 15.0, 15.0, 15.0),
+ // 2: at lower margin
+ (0.0, 3.0, 0.0, 15.0, 10.0, 15.0),
+ // 2: mid-way full (charged to 7.5 kWh), forced charging
+ (0.0, 0.0, 5.0, 15.0, 10.0, 15.0),
+ // 2: mid-way full (set to 7.5 kWh), forced charging
+ (0.0, 7.5, 0.0, 15.0, 10.0, 15.0),
+ // 2: almost full (charged to 12.5 kWh), forced charging
+ (0.0, 5.0, 5.0, 15.0, 10.0, 15.0),
+ // 2: full (set to 15 kWh)
+ (0.0, 15.0, 0.0, 10.0, 10.0, 10.0),
+
+ /* 1: at lower margin (set to 2 kWh) */
+ // 2: empty
+ (2.0, 0.0, 0.0, 15.0, 5.0, 15.0),
+ // 2: at lower margin
+ (2.0, 3.0, 0.0, 15.0, 0.0, 15.0),
+ // 2: mid-way full (charged to 7.5 kWh)
+ (2.0, 0.0, 5.0, 15.0, -5.0, 15.0),
+ // 2: mid-way full (set to 7.5 kWh)
+ (2.0, 7.5, 0.0, 15.0, -5.0, 15.0),
+ // 2: almost full (charged to 12.5 kWh)
+ (2.0, 5.0, 5.0, 15.0, -5.0, 15.0),
+ // 2: full (set to 15 kWh)
+ (2.0, 15.0, 0.0, 10.0, -5.0, 10.0),
+
+ /* 1: mid-way full (set to 5 kWh) */
+ // 2: empty, forced charging
+ (5.0, 0.0, 0.0, 15.0, 5.0, 15.0),
+ // 2: mid-way full (charged to 7.5 kWh)
+ (5.0, 0.0, 5.0, 15.0, -15.0, 15.0),
+ // 2: mid-way full (set to 7.5 kWh)
+ (5.0, 7.5, 0.0, 15.0, -15.0, 15.0),
+ // 2: almost full (charged to 12.5 kWh)
+ (5.0, 5.0, 5.0, 15.0, -15.0, 15.0),
+ // 2: full (set to 15 kWh)
+ (5.0, 15.0, 0.0, 10.0, -15.0, 10.0),
+
+ /* 1: full (set to 10 kWh) */
+ // 2: empty, forced charging
+ (10.0, 0.0, 0.0, 5.0, 5.0, 5.0),
+ // 2: mid-way full (charged to 7.5 kWh)
+ (10.0, 0.0, 5.0, 5.0, -15.0, 5.0),
+ // 2: mid-way full (set to 7.5 kWh)
+ (10.0, 7.5, 0.0, 5.0, -15.0, 5.0),
+ // 2: almost full (charged to 12.5 kWh)
+ (10.0, 5.0, 5.0, 5.0, -15.0, 5.0),
+ // 2: full (set to 15 kWh)
+ (10.0, 15.0, 0.0, 0.0, -15.0, 0.0),
+ )
+
+ forAll(cases) {
+ (
+ lastStored1,
+ lastStored2,
+ lastPower2,
+ expectedPRef,
+ expectedPMin,
+ expectedPMax,
+ ) =>
+ val ev1 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 1",
+ 10.0.asKiloWatt, // AC is relevant,
+ 20.0.asKiloWatt, // DC is not
+ 10.0.asKiloWattHour,
+ lastStored1.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ val schedule1 = SortedSet(
+ ScheduleEntry(3600L, 7200L, Kilowatts(0d))
+ )
+
+ val ev2 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 2",
+ 5.0.asKiloWatt, // AC is relevant,
+ 10.0.asKiloWatt, // DC is not
+ 15.0.asKiloWattHour,
+ lastStored2.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ val schedule2 = SortedSet(
+ ScheduleEntry(0L, 5400L, Kilowatts(lastPower2))
+ )
+
+ evcsModel.determineFlexOptions(
+ data,
+ EvcsState(
+ Seq(ev1, ev2),
+ Map(ev1.uuid -> schedule1, ev2.uuid -> schedule2),
+ 0L,
+ ),
+ ) match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ refPower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsModel.getUuid
+ refPower should approximate(Kilowatts(expectedPRef))
+ minPower should approximate(Kilowatts(expectedPMin))
+ maxPower should approximate(Kilowatts(expectedPMax))
+ }
+ }
+
+ }
+
+ "disallowing v2g" in {
+ val evcsModel = evcsStandardModel.copy(
+ vehicle2grid = false,
+ strategy = ChargingStrategy.CONSTANT_POWER,
+ )
+
+ val currentTick = 7200L
+
+ val data = EvcsRelevantData(
+ currentTick,
+ Seq.empty,
+ )
+
+ val ev1 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 1",
+ 10.0.asKiloWatt, // AC is relevant,
+ 20.0.asKiloWatt, // DC is not
+ 10.0.asKiloWattHour,
+ 0.0.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ val schedule1 = SortedSet(
+ ScheduleEntry(3600L, 7200L, Kilowatts(5.0))
+ )
+
+ evcsModel.determineFlexOptions(
+ data,
+ EvcsState(
+ Seq(ev1),
+ Map(ev1.uuid -> schedule1),
+ 0L,
+ ),
+ ) match {
+ case ProvideMinMaxFlexOptions(
+ modelUuid,
+ refPower,
+ minPower,
+ maxPower,
+ ) =>
+ modelUuid shouldBe evcsModel.getUuid
+ refPower should approximate(Kilowatts(5.0)) // one hour left
+ minPower should approximate(Kilowatts(0d)) // no v2g allowed!
+ maxPower should approximate(ev1.sRatedAc)
+ }
+
+ }
+
+ }
+
+ "handle flexibility correctly" when {
+ val evcsModel =
+ evcsStandardModel.copy(strategy = ChargingStrategy.CONSTANT_POWER)
+
+ "handle controlled power change for two evs correctly" in {
+ val currentTick = 3600L
+
+ val data = EvcsRelevantData(
+ currentTick,
+ Seq.empty,
+ )
+
+ val cases = Table(
+ (
+ "stored1",
+ "stored2",
+ "setPower",
+ "expPowerAndTick1",
+ "expPowerAndTick2",
+ "expNextActivation",
+ "expNextTick",
+ ),
+
+ /* setPower is 0 kWh */
+ (0.0, 0.0, 0.0, N, N, false, N),
+ (10.0, 5.0, 0.0, N, N, false, N),
+ (5.0, 15.0, 0.0, N, N, false, N),
+ (10.0, 15.0, 0.0, N, N, false, N),
+
+ /* setPower is positive (charging) */
+ (0.0, 0.0, 4.0, S(2.0, 7200L), S(2.0, 9000L), true, S(7200L)),
+ (5.0, 0.0, 4.0, N, S(4.0, 6300L), true, S(6300L)),
+ (0.0, 7.5, 4.0, S(4.0, 5400L), N, true, S(5400L)),
+ (9.0, 0.0, 4.0, N, S(4.0, 6300L), true, S(6300L)),
+ (5.0, 14.0, 4.0, S(2.0, 7200L), S(2.0, 5400L), false, S(5400L)),
+ (9.0, 14.0, 4.0, S(2.0, 5400L), S(2.0, 5400L), false, S(5400L)),
+ (10.0, 14.0, 4.0, N, S(4.0, 4500L), false, S(4500L)),
+ (6.0, 15.0, 4.0, S(4.0, 7200L), N, false, S(7200L)),
+
+ /* setPower is set to > (ev2 * 2) (charging) */
+ (0.0, 0.0, 13.0, S(8.0, 4500L), S(5.0, 5760L), true, S(4500L)),
+ (7.0, 0.0, 11.0, S(6.0, 5400L), S(5.0, 5760L), true, S(5400L)),
+ (0.0, 5.0, 15.0, S(10.0, 4320L), S(5.0, 10800L), true, S(4320L)),
+ (0.0, 12.5, 15.0, S(10.0, 4320L), S(5.0, 5400L), true, S(4320L)),
+ (0.0, 0.0, 15.0, S(10.0, 4320L), S(5.0, 5760L), true, S(4320L)),
+ (5.0, 7.5, 15.0, S(10.0, 5400L), S(5.0, 9000L), false, S(5400L)),
+
+ /* setPower is negative (discharging) */
+ (10.0, 15.0, -4.0, S(-2.0, 7200L), S(-2.0, 10800L), true, S(7200L)),
+ (5.0, 15.0, -4.0, S(-2.0, 7200L), S(-2.0, 10800L), true, S(7200L)),
+ (10.0, 7.5, -4.0, S(-2.0, 7200L), S(-2.0, 10800L), true, S(7200L)),
+ (3.0, 15.0, -4.0, S(-2.0, 5400L), S(-2.0, 10800L), true, S(5400L)),
+ (5.0, 4.0, -4.0, S(-2.0, 7200L), S(-2.0, 5400L), false, S(5400L)),
+ (3.0, 4.0, -4.0, S(-2.0, 5400L), S(-2.0, 5400L), false, S(5400L)),
+ (0.0, 4.0, -4.0, N, S(-4.0, 4500L), false, S(4500L)),
+ (6.0, 0.0, -4.0, S(-4.0, 7200L), N, false, S(7200L)),
+
+ /* setPower is set to > (ev2 * 2) (discharging) */
+ (10.0, 15.0, -13.0, S(-8.0, 7200L), S(-5.0, 10800L), true, S(7200L)),
+ (5.0, 15.0, -11.0, S(-6.0, 5400L), S(-5.0, 10800L), true, S(5400L)),
+ (10.0, 8.0, -15.0, S(-10.0, 6480L), S(-5.0, 7200L), true, S(6480L)),
+ (10.0, 5.5, -15.0, S(-10.0, 6480L), S(-5.0, 5400L), true, S(5400L)),
+ (10.0, 15.0, -15.0, S(-10.0, 6480L), S(-5.0, 10800L), true, S(6480L)),
+ (7.0, 10.5, -15.0, S(-10.0, 5400L), S(-5.0, 9000L), false, S(5400L)),
+ )
+
+ forAll(cases) {
+ (
+ stored1: Double,
+ stored2: Double,
+ setPower: Double,
+ expPowerAndTick1: Option[(Double, Long)],
+ expPowerAndTick2: Option[(Double, Long)],
+ expNextActivation: Boolean,
+ expNextTick: Option[Long],
+ ) =>
+ val ev1 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 1",
+ 10.0.asKiloWatt, // AC is relevant,
+ 20.0.asKiloWatt, // DC is not
+ 10.0.asKiloWattHour,
+ stored1.asKiloWattHour,
+ 7200L,
+ )
+ )
+
+ val ev2 = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Mock EV 2",
+ 5.0.asKiloWatt, // AC is relevant,
+ 10.0.asKiloWatt, // DC is not
+ 15.0.asKiloWattHour,
+ stored2.asKiloWattHour,
+ 10800L,
+ )
+ )
+
+ evcsModel.handleControlledPowerChange(
+ data,
+ EvcsState(
+ Seq(ev1, ev2),
+ Map.empty,
+ 0L,
+ ),
+ Kilowatts(setPower),
+ ) match {
+ case (
+ EvcsState(actualEvs, actualSchedules, actualTick),
+ FlexChangeIndicator(actualNextActivation, actualNextTick),
+ ) =>
+ // evs have not changed here since no schedules were given as input
+ actualEvs should have size 2
+ actualEvs should contain allOf (ev1, ev2)
+
+ actualSchedules.get(ev1.uuid).map { entries =>
+ entries.size shouldBe 1
+ val entry = entries.headOption
+ .getOrElse(fail("No charging schedule entry for ev1"))
+ entry.tickStart shouldBe currentTick
+
+ (
+ entry.chargingPower.toKilowatts,
+ entry.tickStop,
+ )
+ } shouldBe expPowerAndTick1
+ actualSchedules.get(ev2.uuid).map { entries =>
+ entries.size shouldBe 1
+ val entry = entries.headOption
+ .getOrElse(fail("No charging schedule entry for ev2"))
+ entry.tickStart shouldBe currentTick
+
+ (
+ entry.chargingPower.toKilowatts,
+ entry.tickStop,
+ )
+ } shouldBe expPowerAndTick2
+
+ actualTick shouldBe currentTick
+
+ actualNextActivation shouldBe expNextActivation
+ actualNextTick shouldBe expNextTick
+ }
+ }
+
+ }
+ }
+ }
+
+}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/ConstantPowerChargingSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/ConstantPowerChargingSpec.scala
new file mode 100644
index 0000000000..59209ce259
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/ConstantPowerChargingSpec.scala
@@ -0,0 +1,166 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs.uncontrolled
+
+import edu.ie3.simona.model.participant.evcs.EvModelWrapper
+import edu.ie3.simona.model.participant.evcs.EvcsModel.ScheduleEntry
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.simona.test.common.input.EvcsInputTestData
+import edu.ie3.simona.test.common.model.MockEvModel
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
+import org.scalatest.prop.TableDrivenPropertyChecks
+import squants.energy.Kilowatts
+
+import java.util.UUID
+import scala.collection.immutable.SortedSet
+
+class ConstantPowerChargingSpec
+ extends UnitSpec
+ with TableDrivenPropertyChecks
+ with EvcsInputTestData {
+
+ "Calculating constant power charging schedules" should {
+ val evcsModel = evcsStandardModel
+
+ "not charge evs if they are fully charged" in {
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Test EV",
+ 5.0.asKiloWatt,
+ 10.0.asKiloWatt,
+ 20.0.asKiloWattHour,
+ 20.0.asKiloWattHour,
+ 3600L,
+ )
+ )
+
+ val actualSchedule = evcsModel.chargeWithConstantPower(
+ 1800L,
+ Seq(ev),
+ )
+
+ actualSchedule shouldBe Map.empty
+ }
+
+ "work correctly with one ev" in {
+ val offset = 1800L
+
+ val cases = Table(
+ ("stayingTicks", "storedEnergy", "expectedPower"),
+ // empty battery
+ (3600L, 0.0, 5.0), // more than max power, limited
+ (7200L, 0.0, 5.0), // exactly max power
+ (14400L, 0.0, 2.5), // less than max power
+ (360000L, 0.0, 0.1), // long stay: 100 hours
+ // half full battery
+ (1800L, 5.0, 5.0), // more than max power, limited
+ (3600L, 5.0, 5.0), // exactly max power
+ (7200L, 5.0, 2.5), // less than max power
+ (180000L, 5.0, 0.1), // long stay: 100 hours
+ )
+
+ forAll(cases) { (stayingTicks, storedEnergy, expectedPower) =>
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Test EV",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ storedEnergy.asKiloWattHour,
+ offset + stayingTicks,
+ )
+ )
+
+ val chargingMap = evcsModel.chargeWithConstantPower(
+ offset,
+ Seq(ev),
+ )
+
+ chargingMap shouldBe Map(
+ ev.uuid -> SortedSet(
+ ScheduleEntry(
+ offset,
+ offset + stayingTicks,
+ Kilowatts(expectedPower),
+ )
+ )
+ )
+ }
+
+ }
+
+ "work correctly with two evs" in {
+ val offset = 3600L
+
+ val cases = Table(
+ ("stayingTicks", "storedEnergy", "expectedPower"),
+ // empty battery
+ (3600L, 0.0, 5.0), // more than max power, limited
+ (7200L, 0.0, 5.0), // exactly max power
+ (14400L, 0.0, 2.5), // less than max power
+ (360000L, 0.0, 0.1), // long stay: 100 hours
+ // half full battery
+ (1800L, 5.0, 5.0), // more than max power, limited
+ (3600L, 5.0, 5.0), // exactly max power
+ (7200L, 5.0, 2.5), // less than max power
+ (180000L, 5.0, 0.1), // long stay: 100 hours
+ )
+
+ forAll(cases) { (stayingTicks, storedEnergy, expectedPower) =>
+ val givenEv = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "First EV",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ 5.0.asKiloWattHour,
+ offset + 3600L,
+ )
+ )
+
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Test EV",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ storedEnergy.asKiloWattHour,
+ offset + stayingTicks,
+ )
+ )
+
+ val chargingMap = evcsModel.chargeWithConstantPower(
+ offset,
+ Seq(givenEv, ev),
+ )
+
+ chargingMap shouldBe Map(
+ givenEv.uuid -> SortedSet(
+ ScheduleEntry(
+ offset,
+ offset + 3600L,
+ Kilowatts(5.0),
+ )
+ ),
+ ev.uuid -> SortedSet(
+ ScheduleEntry(
+ offset,
+ offset + stayingTicks,
+ Kilowatts(expectedPower),
+ )
+ ),
+ )
+ }
+
+ }
+ }
+
+}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/MaximumPowerChargingSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/MaximumPowerChargingSpec.scala
new file mode 100644
index 0000000000..a53b82981e
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/participant/evcs/uncontrolled/MaximumPowerChargingSpec.scala
@@ -0,0 +1,161 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.evcs.uncontrolled
+
+import edu.ie3.simona.model.participant.evcs.EvModelWrapper
+import edu.ie3.simona.model.participant.evcs.EvcsModel.ScheduleEntry
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.simona.test.common.input.EvcsInputTestData
+import edu.ie3.simona.test.common.model.MockEvModel
+import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
+import org.scalatest.prop.TableDrivenPropertyChecks
+import squants.energy.Kilowatts
+
+import java.util.UUID
+import scala.collection.immutable.SortedSet
+
+class MaximumPowerChargingSpec
+ extends UnitSpec
+ with TableDrivenPropertyChecks
+ with EvcsInputTestData {
+
+ "Calculating maximum power charging schedules" should {
+ val evcsModel = evcsStandardModel
+
+ "not charge evs if they are fully charged" in {
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Test EV",
+ 5.0.asKiloWatt,
+ 10.0.asKiloWatt,
+ 20.0.asKiloWattHour,
+ 20.0.asKiloWattHour,
+ 3600,
+ )
+ )
+
+ val actualSchedule = evcsModel.chargeWithMaximumPower(
+ 1800L,
+ Seq(ev),
+ )
+
+ actualSchedule shouldBe Map.empty
+ }
+
+ "work correctly with one ev" in {
+ val offset = 1800L
+
+ val cases = Table(
+ ("stayingTicks", "storedEnergy", "expectedDuration"),
+ // empty battery
+ (3600L, 0.0, 3600L), // stay shorter than full
+ (7200L, 0.0, 7200L), // exactly full
+ (14400L, 0.0, 7200L), // full before end of stay
+ // half full battery
+ (1800L, 5.0, 1800L), // stay shorter than full
+ (3600L, 5.0, 3600L), // exactly full
+ (14400L, 5.0, 3600L), // full before end of stay
+ )
+
+ forAll(cases) { (stayingTicks, storedEnergy, expectedDuration) =>
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Test EV",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ storedEnergy.asKiloWattHour,
+ offset + stayingTicks,
+ )
+ )
+
+ val chargingMap = evcsModel.chargeWithMaximumPower(
+ offset,
+ Seq(ev),
+ )
+
+ chargingMap shouldBe Map(
+ ev.uuid -> SortedSet(
+ ScheduleEntry(
+ offset,
+ offset + expectedDuration,
+ ev.sRatedAc,
+ )
+ )
+ )
+ }
+
+ }
+
+ "work correctly with two evs" in {
+ val offset = 3600L
+
+ val cases = Table(
+ ("stayingTicks", "storedEnergy", "expectedDuration"),
+ // empty battery
+ (3600L, 0.0, 3600L), // stay shorter than full
+ (7200L, 0.0, 7200L), // exactly full
+ (14400L, 0.0, 7200L), // full before end of stay
+ // half full battery
+ (1800L, 5.0, 1800L), // stay shorter than full
+ (3600L, 5.0, 3600L), // exactly full
+ (14400L, 5.0, 3600L), // full before end of stay
+ )
+
+ forAll(cases) { (stayingTicks, storedEnergy, expectedDuration) =>
+ val givenEv = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "First EV",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ 5.0.asKiloWattHour,
+ offset + 3600L,
+ )
+ )
+
+ val ev = EvModelWrapper(
+ new MockEvModel(
+ UUID.randomUUID(),
+ "Test EV",
+ 5.0.asKiloWatt, // using AC charging here
+ 10.0.asKiloWatt,
+ 10.0.asKiloWattHour,
+ storedEnergy.asKiloWattHour,
+ offset + stayingTicks,
+ )
+ )
+
+ val chargingMap = evcsModel.chargeWithMaximumPower(
+ offset,
+ Seq(givenEv, ev),
+ )
+
+ chargingMap shouldBe Map(
+ givenEv.uuid -> SortedSet(
+ ScheduleEntry(
+ offset,
+ offset + 3600L,
+ Kilowatts(5.0),
+ )
+ ),
+ ev.uuid -> SortedSet(
+ ScheduleEntry(
+ offset,
+ offset + expectedDuration,
+ Kilowatts(5.0),
+ )
+ ),
+ )
+ }
+
+ }
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala
new file mode 100644
index 0000000000..b8dc25b01a
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelScalingSpec.scala
@@ -0,0 +1,440 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.load
+
+import edu.ie3.datamodel.models.OperationTime
+import edu.ie3.datamodel.models.input.system.LoadInput
+import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
+import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput}
+import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile
+import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
+import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.CalcRelevantData.LoadRelevantData
+import edu.ie3.simona.model.participant.ModelState.ConstantState
+import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.model.participant.load.LoadReference.{
+ ActivePower,
+ EnergyConsumption,
+}
+import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel
+import edu.ie3.simona.model.participant.load.random.RandomLoadModel
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.util.TimeUtil
+import edu.ie3.util.quantities.PowerSystemUnits
+import org.scalatest.prop.TableDrivenPropertyChecks
+import squants.energy.{KilowattHours, Kilowatts, Watts}
+import squants.time.Minutes
+import squants.{Dimensionless, Each, Energy, Percent, Power, Quantity}
+import tech.units.indriya.quantity.Quantities
+
+import java.time.ZonedDateTime
+import java.time.temporal.ChronoUnit
+import java.util.UUID
+
+class LoadModelScalingSpec extends UnitSpec with TableDrivenPropertyChecks {
+
+ "Testing correct scaling of load models" when {
+ val simulationStartDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
+ val simulationEndDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-12-31 23:59:00")
+
+ "having a profile load model" should {
+ val profileLoadInput =
+ new LoadInput(
+ UUID.fromString("4eeaf76a-ec17-4fc3-872d-34b7d6004b03"),
+ "testLoad",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ new NodeInput(
+ UUID.fromString("e5c1cde5-c161-4a4f-997f-fcf31fecbf57"),
+ "TestNodeInputModel",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ Quantities.getQuantity(1d, PowerSystemUnits.PU),
+ false,
+ NodeInput.DEFAULT_GEO_POSITION,
+ GermanVoltageLevelUtils.LV,
+ -1,
+ ),
+ new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
+ BdewStandardLoadProfile.H0,
+ false,
+ Quantities.getQuantity(3000d, PowerSystemUnits.KILOWATTHOUR),
+ Quantities.getQuantity(282.74d, PowerSystemUnits.VOLTAMPERE),
+ 0.95,
+ )
+ val foreSeenOperationInterval =
+ SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ profileLoadInput.getOperationTime,
+ )
+
+ val targetEnergyConsumption = KilowattHours(3000d)
+
+ "reach the targeted annual energy consumption" in {
+ forAll(
+ Table(
+ "profile",
+ BdewStandardLoadProfile.H0,
+ BdewStandardLoadProfile.L0,
+ BdewStandardLoadProfile.G0,
+ )
+ ) { profile =>
+ val model = ProfileLoadModel(
+ profileLoadInput.getUuid,
+ profileLoadInput.getId,
+ foreSeenOperationInterval,
+ 1.0d,
+ QControl.apply(profileLoadInput.getqCharacteristics()),
+ Kilowatts(
+ profileLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ profileLoadInput.getCosPhiRated,
+ profile,
+ EnergyConsumption(targetEnergyConsumption),
+ )
+ model.enable()
+
+ /* Test against a permissible deviation of 2 %. As per official documentation of the bdew load profiles
+ * [https://www.bdew.de/media/documents/2000131_Anwendung-repraesentativen_Lastprofile-Step-by-step.pdf] 1.5 %
+ * are officially permissible. But, as we currently do not take (bank) holidays into account, we cannot reach
+ * this accuracy. */
+
+ calculateEnergyDiffForYear(
+ model,
+ simulationStartDate,
+ targetEnergyConsumption,
+ ) should be < Percent(2d)
+ }
+ }
+
+ "correctly account for the scaling factor, when targeting a given annual energy consumption" in {
+ val scalingFactor = 1.5
+ val expectedEnergy = KilowattHours(4500d)
+
+ val model = ProfileLoadModel(
+ profileLoadInput.getUuid,
+ profileLoadInput.getId,
+ foreSeenOperationInterval,
+ scalingFactor,
+ QControl.apply(profileLoadInput.getqCharacteristics()),
+ Kilowatts(
+ profileLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ profileLoadInput.getCosPhiRated,
+ BdewStandardLoadProfile.H0,
+ EnergyConsumption(targetEnergyConsumption),
+ )
+ model.enable()
+
+ calculateEnergyDiffForYear(
+ model,
+ simulationStartDate,
+ expectedEnergy,
+ ) should be < Percent(2d)
+ }
+
+ val targetMaximumPower = Watts(268.6)
+
+ "approximately reach the maximum power" in {
+ forAll(
+ Table(
+ "profile",
+ BdewStandardLoadProfile.H0,
+ BdewStandardLoadProfile.L0,
+ BdewStandardLoadProfile.G0,
+ )
+ ) { profile =>
+ val model = ProfileLoadModel(
+ profileLoadInput.getUuid,
+ profileLoadInput.getId,
+ foreSeenOperationInterval,
+ 1.0,
+ QControl.apply(profileLoadInput.getqCharacteristics()),
+ Kilowatts(
+ profileLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ profileLoadInput.getCosPhiRated,
+ profile,
+ ActivePower(targetMaximumPower),
+ )
+ model.enable()
+
+ val maximumPower = calculatePowerForYear(
+ model,
+ simulationStartDate,
+ ).maxOption.value
+
+ implicit val tolerance: Power = Watts(1d)
+ maximumPower should approximate(targetMaximumPower)
+ }
+ }
+
+ "correctly account for the scaling factor when targeting at maximum power" in {
+ val scalingFactor = 1.5
+ val expectedMaximum = Watts(402.9)
+
+ val model = ProfileLoadModel(
+ profileLoadInput.getUuid,
+ profileLoadInput.getId,
+ foreSeenOperationInterval,
+ scalingFactor,
+ QControl.apply(profileLoadInput.getqCharacteristics()),
+ Kilowatts(
+ profileLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ profileLoadInput.getCosPhiRated,
+ BdewStandardLoadProfile.H0,
+ ActivePower(targetMaximumPower),
+ )
+ model.enable()
+
+ val maximumPower = calculatePowerForYear(
+ model,
+ simulationStartDate,
+ ).maxOption.value
+
+ implicit val tolerance: Power = Watts(1.5d)
+ maximumPower should approximate(expectedMaximum)
+ }
+ }
+
+ "having a random load model" should {
+ val randomLoadInput =
+ new LoadInput(
+ UUID.fromString("4eeaf76a-ec17-4fc3-872d-34b7d6004b03"),
+ "testLoad",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ new NodeInput(
+ UUID.fromString("e5c1cde5-c161-4a4f-997f-fcf31fecbf57"),
+ "TestNodeInputModel",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ Quantities.getQuantity(1d, PowerSystemUnits.PU),
+ false,
+ NodeInput.DEFAULT_GEO_POSITION,
+ GermanVoltageLevelUtils.LV,
+ -1,
+ ),
+ new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
+ BdewStandardLoadProfile.H0,
+ false,
+ Quantities.getQuantity(3000d, PowerSystemUnits.KILOWATTHOUR),
+ Quantities.getQuantity(282.74d, PowerSystemUnits.VOLTAMPERE),
+ 0.95,
+ )
+ val foreSeenOperationInterval =
+ SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ randomLoadInput.getOperationTime,
+ )
+
+ val targetEnergyConsumption = KilowattHours(3000d)
+
+ "reach the targeted annual energy consumption" in {
+ val model = RandomLoadModel(
+ randomLoadInput.getUuid,
+ randomLoadInput.getId,
+ foreSeenOperationInterval,
+ 1.0,
+ QControl.apply(randomLoadInput.getqCharacteristics()),
+ Kilowatts(
+ randomLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ randomLoadInput.getCosPhiRated,
+ EnergyConsumption(targetEnergyConsumption),
+ )
+ model.enable()
+
+ calculateEnergyDiffForYear(
+ model,
+ simulationStartDate,
+ targetEnergyConsumption,
+ ) should be < Percent(1d)
+ }
+
+ "correctly account for the scaling factor, when targeting a given annual energy consumption" in {
+ val scalingFactor = 1.5
+ val expectedEnergy = KilowattHours(4500d)
+
+ val model = RandomLoadModel(
+ randomLoadInput.getUuid,
+ randomLoadInput.getId,
+ foreSeenOperationInterval,
+ scalingFactor,
+ QControl.apply(randomLoadInput.getqCharacteristics()),
+ Kilowatts(
+ randomLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ randomLoadInput.getCosPhiRated,
+ EnergyConsumption(targetEnergyConsumption),
+ )
+ model.enable()
+
+ calculateEnergyDiffForYear(
+ model,
+ simulationStartDate,
+ expectedEnergy,
+ ) should be < Percent(2d)
+ }
+
+ val targetMaximumPower = Watts(268.6)
+ "approximately reach the maximum power" in {
+ val model = RandomLoadModel(
+ randomLoadInput.getUuid,
+ randomLoadInput.getId,
+ foreSeenOperationInterval,
+ 1.0,
+ QControl.apply(randomLoadInput.getqCharacteristics()),
+ Kilowatts(
+ randomLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ randomLoadInput.getCosPhiRated,
+ ActivePower(targetMaximumPower),
+ )
+ model.enable()
+
+ val powers = calculatePowerForYear(
+ model,
+ simulationStartDate,
+ ).toIndexedSeq.sorted.toArray
+
+ val quantile95 = RandomLoadModelSpec.get95Quantile(powers)
+
+ getRelativeDifference(
+ quantile95,
+ targetMaximumPower,
+ ) should be < Percent(2d)
+ }
+
+ "correctly account for the scaling factor when targeting at maximum power" in {
+ val scalingFactor = 1.5
+ val expectedMaximum = targetMaximumPower * scalingFactor
+
+ val model = RandomLoadModel(
+ randomLoadInput.getUuid,
+ randomLoadInput.getId,
+ foreSeenOperationInterval,
+ scalingFactor,
+ QControl.apply(randomLoadInput.getqCharacteristics()),
+ Kilowatts(
+ randomLoadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue
+ ),
+ randomLoadInput.getCosPhiRated,
+ ActivePower(targetMaximumPower),
+ )
+ model.enable()
+
+ val powers = calculatePowerForYear(
+ model,
+ simulationStartDate,
+ ).toIndexedSeq.sorted.toArray
+
+ /* Tolerance is equivalent to 10 W difference between the 95%-percentile of the obtained random results and the
+ * target maximum power. Because of the stochastic nature, the maximum power cannot be met perfectly */
+ implicit val tolerance: Power = Watts(10d)
+ RandomLoadModelSpec.get95Quantile(powers) should
+ approximate(expectedMaximum)
+ }
+ }
+ }
+
+ def calculateEnergyDiffForYear[C <: LoadRelevantData](
+ model: LoadModel[C],
+ simulationStartDate: ZonedDateTime,
+ expectedEnergy: Energy,
+ ): Dimensionless = {
+ val duration = Minutes(15d)
+
+ val avgEnergy = calculatePowerForYear(
+ model: LoadModel[C],
+ simulationStartDate: ZonedDateTime,
+ ).foldLeft(KilowattHours(0)) { case (energySum, power) =>
+ energySum + (power * duration)
+ }
+
+ getRelativeDifference(
+ avgEnergy,
+ expectedEnergy,
+ )
+ }
+
+ def calculatePowerForYear[C <: LoadRelevantData](
+ model: LoadModel[C],
+ simulationStartDate: ZonedDateTime,
+ ): Iterable[Power] = {
+ val quarterHoursInYear = 365L * 96L
+
+ (0L until quarterHoursInYear)
+ .map { quarterHour =>
+ val tick = quarterHour * 15 * 60
+ val relevantData = createRelevantData(model)(
+ simulationStartDate.plus(quarterHour * 15, ChronoUnit.MINUTES)
+ )
+
+ model
+ .calculatePower(
+ tick,
+ Each(0d),
+ ConstantState,
+ relevantData,
+ )
+ .p
+ }
+ }
+
+ def createRelevantData[C <: LoadRelevantData](
+ model: LoadModel[C]
+ ): ZonedDateTime => C =
+ model match {
+ case _: RandomLoadModel => RandomLoadModel.RandomRelevantData
+ case _: ProfileLoadModel => ProfileLoadModel.ProfileRelevantData
+ }
+
+ def getRelativeDifference[Q <: Quantity[Q]](
+ actualResult: Q,
+ expectedResult: Q,
+ ): Dimensionless =
+ Each((expectedResult - actualResult).abs / expectedResult)
+
+}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala
index b5317a8b71..8129394a54 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/LoadModelSpec.scala
@@ -33,12 +33,12 @@ class LoadModelSpec
("reference", "sRated"),
(
LoadReference.ActivePower(Watts(268.6)),
- Watts(282.7368421052632)
+ Watts(282.7368421052632),
),
(
LoadReference.EnergyConsumption(KilowattHours(3000.0)),
- Watts(848.2105263157896)
- )
+ Watts(848.2105263157896),
+ ),
)
forAll(params) {
@@ -48,7 +48,7 @@ class LoadModelSpec
loadInput,
defaultOperationInterval,
foreSeenScalingFactor,
- foreSeenReference
+ foreSeenReference,
)
inside(actual) {
case ProfileLoadModel(
@@ -60,14 +60,14 @@ class LoadModelSpec
sRated,
cosPhiRated,
loadProfile,
- reference
+ reference,
) =>
uuid shouldBe loadInput.getUuid
id shouldBe loadInput.getId
operationInterval shouldBe defaultOperationInterval
scalingFactor shouldBe foreSeenScalingFactor
qControl shouldBe QControl(loadInput.getqCharacteristics)
- (sRated ~= expsRated) shouldBe true
+ sRated should approximate(expsRated)
cosPhiRated shouldBe loadInput.getCosPhiRated
loadProfile shouldBe loadInput.getLoadProfile
reference shouldBe foreSeenReference
@@ -82,12 +82,12 @@ class LoadModelSpec
("reference", "sRated"),
(
LoadReference.ActivePower(Watts(268.6)),
- Watts(311.0105263157895)
+ Watts(311.0105263157895),
),
(
LoadReference.EnergyConsumption(KilowattHours(3000.0)),
- Watts(770.8076055515501)
- )
+ Watts(770.8076055515501),
+ ),
)
forAll(params) {
@@ -97,7 +97,7 @@ class LoadModelSpec
loadInput,
defaultOperationInterval,
foreSeenScalingFactor,
- foreSeenReference
+ foreSeenReference,
)
inside(actual) {
case RandomLoadModel(
@@ -108,14 +108,14 @@ class LoadModelSpec
qControl,
sRated,
cosPhiRated,
- reference
+ reference,
) =>
uuid shouldBe loadInput.getUuid
id shouldBe loadInput.getId
operationInterval shouldBe defaultOperationInterval
scalingFactor shouldBe foreSeenScalingFactor
qControl shouldBe QControl(loadInput.getqCharacteristics)
- (sRated ~= expsRated) shouldBe true
+ sRated should approximate(expsRated)
cosPhiRated shouldBe loadInput.getCosPhiRated
reference shouldBe foreSeenReference
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala
index dd66916cb1..d8ca0da750 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/LoadProfileStoreSpec.scala
@@ -12,7 +12,7 @@ import edu.ie3.datamodel.models.profile.StandardLoadProfile
import edu.ie3.simona.model.participant.load.profile.{
LoadProfileKey,
LoadProfileStore,
- TypeDayProfile
+ TypeDayProfile,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.TimeUtil
@@ -48,30 +48,30 @@ class LoadProfileStoreSpec
(
"2019-04-01T05:00:00+02:00[Europe/Berlin]",
L0,
- 55.6d
+ 55.6d,
), // Weekday, transitional, 20th quarter hour
(
"2019-06-02T00:00:00+02:00[Europe/Berlin]",
G0,
- 68.8d
+ 68.8d,
), // Sunday, summer, 0th quarter hour
(
"2019-01-05T02:15:00+01:00[Europe/Berlin]",
H0,
- 52.8d
+ 52.8d,
), // Saturday, winter, 9th quarter hour, 5th day -> dynamization factor 1.2473
(
"2019-07-21T01:00:00+02:00[Europe/Berlin]",
H0,
- 58.1d
- ) // Sunday, summer, 4th quarter hour, 202nd day -> dynamization factor 0.7847
+ 58.1d,
+ ), // Sunday, summer, 4th quarter hour, 202nd day -> dynamization factor 0.7847
)
forAll(params) {
(
timestamp: String,
loadProfile: StandardLoadProfile,
- paramValue: Double
+ paramValue: Double,
) =>
val time = ZonedDateTime.parse(timestamp)
val param = Watts(paramValue)
@@ -86,7 +86,7 @@ class LoadProfileStoreSpec
("profile", "maxParamValue"),
(H0, 268.6d),
(L0, 240.4d),
- (G0, 240.4d)
+ (G0, 240.4d),
)
forAll(maxParams) {
@@ -110,7 +110,7 @@ class LoadProfileStoreSpec
H0 -> KilowattHours(1000.0),
L0 -> KilowattHours(1002.0),
/* TODO: Check, if this is correct */
- G0 -> KilowattHours(1022.0)
+ G0 -> KilowattHours(1022.0),
)
/* Collect all available time steps in 2020 */
@@ -143,7 +143,7 @@ class LoadProfileStoreSpec
implicit val powerTolerance: squants.Energy = KilowattHours(1.0)
/* Check the deviation against the expected value (deviation should be smaller than 1 kWh) */
- (annualEnergy ~= expected) shouldBe true
+ annualEnergy should approximate(expected)
})
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala
new file mode 100644
index 0000000000..a66aac0a78
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/ProfileLoadModelSpec.scala
@@ -0,0 +1,127 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.load
+
+import edu.ie3.datamodel.models.OperationTime
+import edu.ie3.datamodel.models.input.system.LoadInput
+import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
+import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput}
+import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile
+import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
+import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.load.LoadReference.{
+ ActivePower,
+ EnergyConsumption,
+}
+import edu.ie3.simona.model.participant.load.profile.ProfileLoadModel
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.util.TimeUtil
+import edu.ie3.util.quantities.PowerSystemUnits
+import org.scalatest.prop.TableDrivenPropertyChecks
+import squants.Power
+import squants.energy.{KilowattHours, Watts}
+import tech.units.indriya.quantity.Quantities
+
+import java.util.UUID
+
+class ProfileLoadModelSpec extends UnitSpec with TableDrivenPropertyChecks {
+
+ private implicit val tolerance: Power = Watts(1d)
+
+ "Having a profile load model" when {
+ val loadInput =
+ new LoadInput(
+ UUID.fromString("4eeaf76a-ec17-4fc3-872d-34b7d6004b03"),
+ "testLoad",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ new NodeInput(
+ UUID.fromString("e5c1cde5-c161-4a4f-997f-fcf31fecbf57"),
+ "TestNodeInputModel",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ Quantities.getQuantity(1d, PowerSystemUnits.PU),
+ false,
+ NodeInput.DEFAULT_GEO_POSITION,
+ GermanVoltageLevelUtils.LV,
+ -1,
+ ),
+ new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
+ BdewStandardLoadProfile.H0,
+ false,
+ Quantities.getQuantity(3000d, PowerSystemUnits.KILOWATTHOUR),
+ Quantities.getQuantity(282.74d, PowerSystemUnits.VOLTAMPERE),
+ 0.95,
+ )
+
+ val simulationStartDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
+ val simulationEndDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-12-31 23:59:00")
+ val foreSeenOperationInterval =
+ SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ loadInput.getOperationTime,
+ )
+
+ "instantiating it" should {
+ "deliver a proper model" in {
+ forAll(
+ Table(
+ ("profile", "reference", "expectedsRated"),
+ (
+ BdewStandardLoadProfile.H0,
+ ActivePower(Watts(268.6)),
+ Watts(282.74d),
+ ),
+ (
+ BdewStandardLoadProfile.H0,
+ EnergyConsumption(
+ KilowattHours(3000d)
+ ),
+ Watts(848.22d),
+ ),
+ (
+ BdewStandardLoadProfile.L0,
+ ActivePower(Watts(268.6)),
+ Watts(282.74d),
+ ),
+ (
+ BdewStandardLoadProfile.L0,
+ EnergyConsumption(
+ KilowattHours(3000d)
+ ),
+ Watts(759.158d),
+ ),
+ (
+ BdewStandardLoadProfile.G0,
+ ActivePower(Watts(268.6)),
+ Watts(282.74d),
+ ),
+ (
+ BdewStandardLoadProfile.G0,
+ EnergyConsumption(
+ KilowattHours(3000d)
+ ),
+ Watts(759.158d),
+ ),
+ )
+ ) { (profile, reference, expectedSRated) =>
+ val actual = ProfileLoadModel(
+ loadInput.copy().loadprofile(profile).build(),
+ foreSeenOperationInterval,
+ 1.0,
+ reference,
+ )
+
+ actual.sRated should approximate(expectedSRated)
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala
new file mode 100644
index 0000000000..393657f9d8
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadModelSpec.scala
@@ -0,0 +1,149 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.model.participant.load
+
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.GeneralizedExtremeValueDistribution
+import edu.ie3.datamodel.models.OperationTime
+import edu.ie3.datamodel.models.input.system.LoadInput
+import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
+import edu.ie3.datamodel.models.input.{NodeInput, OperatorInput}
+import edu.ie3.datamodel.models.profile.BdewStandardLoadProfile
+import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
+import edu.ie3.simona.model.SystemComponent
+import edu.ie3.simona.model.participant.control.QControl
+import edu.ie3.simona.model.participant.load.LoadReference.{
+ ActivePower,
+ EnergyConsumption,
+}
+import edu.ie3.simona.model.participant.load.random.{
+ RandomLoadModel,
+ RandomLoadParameters,
+}
+import edu.ie3.simona.test.common.UnitSpec
+import edu.ie3.util.TimeUtil
+import edu.ie3.util.quantities.PowerSystemUnits
+import org.scalatest.prop.TableDrivenPropertyChecks
+import squants.Power
+import squants.energy.{KilowattHours, Kilowatts, Watts}
+import tech.units.indriya.quantity.Quantities
+
+import java.util.UUID
+
+class RandomLoadModelSpec extends UnitSpec with TableDrivenPropertyChecks {
+ implicit val tolerance: Power = Watts(1d)
+ "Having a random load model" when {
+ val loadInput =
+ new LoadInput(
+ UUID.fromString("4eeaf76a-ec17-4fc3-872d-34b7d6004b03"),
+ "testLoad",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ new NodeInput(
+ UUID.fromString("e5c1cde5-c161-4a4f-997f-fcf31fecbf57"),
+ "TestNodeInputModel",
+ OperatorInput.NO_OPERATOR_ASSIGNED,
+ OperationTime.notLimited(),
+ Quantities.getQuantity(1d, PowerSystemUnits.PU),
+ false,
+ NodeInput.DEFAULT_GEO_POSITION,
+ GermanVoltageLevelUtils.LV,
+ -1,
+ ),
+ new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
+ BdewStandardLoadProfile.H0,
+ false,
+ Quantities.getQuantity(3000d, PowerSystemUnits.KILOWATTHOUR),
+ Quantities.getQuantity(282.74d, PowerSystemUnits.VOLTAMPERE),
+ 0.95,
+ )
+
+ val simulationStartDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-01-01 00:00:00")
+ val simulationEndDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-12-31 23:59:00")
+ val foreSeenOperationInterval =
+ SystemComponent.determineOperationInterval(
+ simulationStartDate,
+ simulationEndDate,
+ loadInput.getOperationTime,
+ )
+
+ "instantiating it" should {
+ "deliver a proper model" in {
+
+ val testData = Table(
+ ("reference", "expectedSRated"),
+ (ActivePower(Watts(268.6)), Watts(311.0105263157895d)),
+ (EnergyConsumption(KilowattHours(2000d)), Watts(513.871737d)),
+ )
+
+ forAll(testData) { (reference, expectedSRated: Power) =>
+ val actual = RandomLoadModel(
+ loadInput,
+ foreSeenOperationInterval,
+ 1.0,
+ reference,
+ )
+
+ actual.sRated should approximate(expectedSRated)
+ }
+ }
+ }
+
+ "calculating results" should {
+ "deliver the correct distribution on request" in {
+ val dut = new RandomLoadModel(
+ loadInput.getUuid,
+ loadInput.getId,
+ foreSeenOperationInterval,
+ 1.0,
+ QControl.apply(loadInput.getqCharacteristics()),
+ Kilowatts(
+ loadInput
+ .getsRated()
+ .to(PowerSystemUnits.KILOWATT)
+ .getValue
+ .doubleValue()
+ ),
+ loadInput.getCosPhiRated,
+ ActivePower(Watts(268.6)),
+ )
+ /* Working day, 61th quarter hour */
+ val queryDate =
+ TimeUtil.withDefaults.toZonedDateTime("2019-07-19 15:21:00")
+ val expectedParams = new RandomLoadParameters(
+ 0.405802458524704,
+ 0.0671483352780342,
+ 0.0417016632854939,
+ )
+
+ /* First query leeds to generation of distribution */
+ val getGevDistribution =
+ PrivateMethod[GeneralizedExtremeValueDistribution](
+ Symbol("getGevDistribution")
+ )
+
+ def firstHit = dut invokePrivate getGevDistribution(queryDate)
+
+ firstHit.getK shouldBe expectedParams.k
+ firstHit.getMu shouldBe expectedParams.my
+ firstHit.getSigma shouldBe expectedParams.sigma
+
+ /* Second query is only look up in storage */
+ def secondHit = dut invokePrivate getGevDistribution(queryDate)
+
+ secondHit shouldBe firstHit
+ }
+ }
+ }
+}
+
+object RandomLoadModelSpec {
+ def get95Quantile[V](sortedArray: Array[V]): V = sortedArray(
+ (sortedArray.length * 0.95).toInt
+ )
+}
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala
index 2bbe997702..7af564121a 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/RandomLoadParamStoreSpec.scala
@@ -11,7 +11,7 @@ import java.io.InputStreamReader
import edu.ie3.simona.model.participant.load.random.{
RandomLoadParamStore,
RandomLoadParameters,
- TypeDayParameters
+ TypeDayParameters,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.TimeUtil
@@ -40,25 +40,25 @@ class RandomLoadParamStoreSpec
"sigmaSa",
"sigmaSu",
"sigmaWd",
- "quarterHour"
+ "quarterHour",
)
)
val expected = Map(
DayType.weekday -> Map(
RandomLoadParameters.K -> 2,
RandomLoadParameters.MY -> 5,
- RandomLoadParameters.SIGMA -> 8
+ RandomLoadParameters.SIGMA -> 8,
),
DayType.saturday -> Map(
RandomLoadParameters.K -> 0,
RandomLoadParameters.MY -> 3,
- RandomLoadParameters.SIGMA -> 6
+ RandomLoadParameters.SIGMA -> 6,
),
DayType.sunday -> Map(
RandomLoadParameters.K -> 1,
RandomLoadParameters.MY -> 4,
- RandomLoadParameters.SIGMA -> 7
- )
+ RandomLoadParameters.SIGMA -> 7,
+ ),
)
actual shouldBe expected
@@ -91,31 +91,31 @@ class RandomLoadParamStoreSpec
RandomLoadParameters(
0.146539300680161,
0.0430354326963425,
- 0.0201929099857807
- )
+ 0.0201929099857807,
+ ),
), // Weekday
(
"2019-06-02 00:00:00",
RandomLoadParameters(
0.295997023582459,
0.0630703344941139,
- 0.0370676517486572
- )
+ 0.0370676517486572,
+ ),
), // Sunday
(
"2019-01-05 02:15:00",
RandomLoadParameters(
0.132398754358292,
0.0439879409968853,
- 0.0208074823021889
- )
- ) // Saturday
+ 0.0208074823021889,
+ ),
+ ), // Saturday
)
forAll(params) {
(
timestamp: String,
- expected: RandomLoadParameters
+ expected: RandomLoadParameters,
) =>
val time = TimeUtil.withDefaults.toZonedDateTime(timestamp)
randomParameterStore.parameters(time) shouldBe expected
diff --git a/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala b/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala
index e8af1101ba..4f0a119297 100644
--- a/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/participant/load/TypeDayParametersSpec.scala
@@ -8,7 +8,7 @@ package edu.ie3.simona.model.participant.load
import edu.ie3.simona.model.participant.load.random.{
RandomLoadParameters,
- TypeDayParameters
+ TypeDayParameters,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.TimeUtil
@@ -21,7 +21,7 @@ class TypeDayParametersSpec extends UnitSpec {
RandomLoadParameters(
cnt.toDouble,
(cnt + 1).toDouble,
- (cnt + 2).toDouble
+ (cnt + 2).toDouble,
)
)
.toArray
@@ -37,7 +37,7 @@ class TypeDayParametersSpec extends UnitSpec {
RandomLoadParameters(
cnt.toDouble,
(cnt + 1).toDouble,
- (cnt + 2).toDouble
+ (cnt + 2).toDouble,
)
)
.toArray
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala
index 80a20585ce..e4c1c14c70 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridSpec.scala
@@ -26,8 +26,8 @@ class ThermalGridSpec extends UnitSpec {
val energyDemand = ThermalEnergyDemand(required, possible)
- (energyDemand.required =~ possible) shouldBe true
- (energyDemand.possible =~ possible) shouldBe true
+ energyDemand.required should approximate(possible)
+ energyDemand.possible should approximate(possible)
}
"set the correct values, if they are sensible" in {
@@ -36,8 +36,8 @@ class ThermalGridSpec extends UnitSpec {
val energyDemand = ThermalEnergyDemand(required, possible)
- (energyDemand.required =~ required) shouldBe true
- (energyDemand.possible =~ possible) shouldBe true
+ energyDemand.required should approximate(required)
+ energyDemand.possible should approximate(possible)
}
}
@@ -45,9 +45,8 @@ class ThermalGridSpec extends UnitSpec {
"actually have no demand" in {
val energyDemand = ThermalEnergyDemand.noDemand
- (energyDemand.required =~ MegawattHours(0d)) shouldBe true
-
- (energyDemand.possible =~ MegawattHours(0d)) shouldBe true
+ energyDemand.required should approximate(MegawattHours(0d))
+ energyDemand.possible should approximate(MegawattHours(0d))
}
}
@@ -84,18 +83,17 @@ class ThermalGridSpec extends UnitSpec {
"deliver proper results" in {
val energyDemand1 = ThermalEnergyDemand(
MegawattHours(45d),
- MegawattHours(47d)
+ MegawattHours(47d),
)
val energyDemand2 = ThermalEnergyDemand(
MegawattHours(23d),
- MegawattHours(28d)
+ MegawattHours(28d),
)
val totalDemand = energyDemand1 + energyDemand2
- (totalDemand.required =~ MegawattHours(68d)) shouldBe true
-
- (totalDemand.possible =~ MegawattHours(75d)) shouldBe true
+ totalDemand.required should approximate(MegawattHours(68d))
+ totalDemand.possible should approximate(MegawattHours(75d))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala
index 4ee4fbcf33..a7af090fb6 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridTestData.scala
@@ -9,8 +9,8 @@ package edu.ie3.simona.model.thermal
import edu.ie3.datamodel.models.OperationTime
import edu.ie3.datamodel.models.input.OperatorInput
import edu.ie3.datamodel.models.input.thermal.ThermalBusInput
-import squants.energy.Kilowatts
-import squants.thermal.Celsius
+import squants.energy.{Kilowatts, Power}
+import squants.thermal.{Celsius, Temperature}
import java.util.UUID
@@ -19,10 +19,10 @@ trait ThermalGridTestData {
UUID.randomUUID(),
"Thermal Bus",
OperatorInput.NO_OPERATOR_ASSIGNED,
- OperationTime.notLimited()
+ OperationTime.notLimited(),
)
- protected val testGridambientTemperature = Celsius(12d)
- protected val testGridQDotInfeed = Kilowatts(15d)
- protected val testGridQDotConsumption = Kilowatts(-42d)
- protected val testGridQDotConsumptionHigh = Kilowatts(-200d)
+ protected val testGridambientTemperature: Temperature = Celsius(12d)
+ protected val testGridQDotInfeed: Power = Kilowatts(15d)
+ protected val testGridQDotConsumption: Power = Kilowatts(-42d)
+ protected val testGridQDotConsumptionHigh: Power = Kilowatts(-200d)
}
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala
index 4baff53e4e..6955b3c705 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseAndStorageSpec.scala
@@ -11,17 +11,17 @@ import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{
HouseTemperatureLowerBoundaryReached,
- HouseTemperatureUpperBoundaryReached
+ HouseTemperatureUpperBoundaryReached,
}
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.{
StorageEmpty,
- StorageFull
+ StorageFull,
}
import edu.ie3.simona.test.common.UnitSpec
import squants.energy._
import squants.thermal.Celsius
-import squants.{Energy, Power, Temperature}
+import squants.{Energy, Kelvin, Power, Temperature}
import tech.units.indriya.unit.Units
import scala.jdk.CollectionConverters._
@@ -31,7 +31,7 @@ class ThermalGridWithHouseAndStorageSpec
with ThermalHouseTestData
with ThermalStorageTestData {
- implicit val tempTolerance: Temperature = Celsius(1e-3)
+ implicit val tempTolerance: Temperature = Kelvin(1e-3)
implicit val powerTolerance: Power = Watts(1e-3)
implicit val energyTolerance: Energy = WattHours(1e-3)
@@ -41,13 +41,13 @@ class ThermalGridWithHouseAndStorageSpec
new edu.ie3.datamodel.models.input.container.ThermalGrid(
thermalBusInput,
Set(thermalHouseInput).asJava,
- Set[ThermalStorageInput](thermalStorageInput).asJava
+ Set[ThermalStorageInput](thermalStorageInput).asJava,
)
ThermalGrid(thermalGridInput) match {
case ThermalGrid(
Some(thermalHouseGenerated),
- Some(thermalStorageGenerated)
+ Some(thermalStorageGenerated),
) =>
thermalHouseGenerated shouldBe thermalHouse
thermalStorageGenerated shouldBe thermalStorage
@@ -62,7 +62,7 @@ class ThermalGridWithHouseAndStorageSpec
new edu.ie3.datamodel.models.input.container.ThermalGrid(
thermalBusInput,
Set(thermalHouseInput).asJava,
- Set[ThermalStorageInput](thermalStorageInput).asJava
+ Set[ThermalStorageInput](thermalStorageInput).asJava,
)
)
@@ -73,14 +73,19 @@ class ThermalGridWithHouseAndStorageSpec
Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)),
Some(
ThermalStorageState(storageTick, storedEnergy, qDotStorage)
- )
+ ),
) =>
houseTick shouldBe expectedHouseStartingState.tick
storageTick shouldBe expectedHouseStartingState.tick
- (innerTemperature =~ expectedHouseStartingState.innerTemperature) shouldBe true
- (storedEnergy =~ expectedStorageStartingState.storedEnergy) shouldBe true
- (qDotHouse =~ expectedHouseStartingState.qDot) shouldBe true
- (qDotStorage =~ expectedStorageStartingState.qDot) shouldBe true
+
+ innerTemperature should approximate(
+ expectedHouseStartingState.innerTemperature
+ )
+ storedEnergy should approximate(
+ expectedStorageStartingState.storedEnergy
+ )
+ qDotHouse should approximate(expectedHouseStartingState.qDot)
+ qDotStorage should approximate(expectedStorageStartingState.qDot)
case _ => fail("Determination of starting state failed")
}
@@ -94,11 +99,13 @@ class ThermalGridWithHouseAndStorageSpec
val gridDemand = thermalGrid.energyDemand(
tick,
testGridambientTemperature,
- ThermalGrid.startingState(thermalGrid)
+ ThermalGrid.startingState(thermalGrid),
)
- (gridDemand.required =~ KilowattHours(0d)) shouldBe true
- (gridDemand.possible =~ KilowattHours(31.05009722 + 920)) shouldBe true
+ gridDemand.required should approximate(KilowattHours(0d))
+ gridDemand.possible should approximate(
+ KilowattHours(31.05009722 + 920)
+ )
}
"consider stored energy to reduce house demand" in {
@@ -112,11 +119,11 @@ class ThermalGridWithHouseAndStorageSpec
startingState.houseState.map(
_.copy(innerTemperature = Celsius(16d))
)
- )
+ ),
)
- (gridDemand.required =~ KilowattHours(0d)) shouldBe true
- (gridDemand.possible =~ KilowattHours(1041.200111111)) shouldBe true
+ gridDemand.required should approximate(KilowattHours(0d))
+ gridDemand.possible should approximate(KilowattHours(1041.200111111))
}
"consider stored energy to reduce house demand if stored energy is not enough" in {
@@ -130,10 +137,10 @@ class ThermalGridWithHouseAndStorageSpec
startingState.houseState.map(
_.copy(innerTemperature = Celsius(3d))
)
- )
+ ),
)
- (gridDemand.required =~ KilowattHours(8.64987499999)) shouldBe true
- (gridDemand.possible =~ KilowattHours(1418.64987499999)) shouldBe true
+ gridDemand.required should approximate(KilowattHours(8.64987499999))
+ gridDemand.possible should approximate(KilowattHours(1418.64987499999))
}
}
@@ -159,7 +166,7 @@ class ThermalGridWithHouseAndStorageSpec
tick,
testGridambientTemperature,
gridState,
- externalQDot
+ externalQDot,
)
updatedGridState match {
@@ -167,15 +174,15 @@ class ThermalGridWithHouseAndStorageSpec
_,
Some(
ThermalStorageState(storageTick, storedEnergy, qDotStorage)
- )
+ ),
) =>
storageTick shouldBe 0L
- (storedEnergy =~ initialLoading) shouldBe true
- (qDotStorage =~ externalQDot) shouldBe true
+ storedEnergy should approximate(initialLoading)
+ qDotStorage should approximate(externalQDot)
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(
- HouseTemperatureLowerBoundaryReached(154284L)
+ HouseTemperatureLowerBoundaryReached(154285L)
)
}
@@ -195,7 +202,7 @@ class ThermalGridWithHouseAndStorageSpec
tick,
testGridambientTemperature,
gridState,
- externalQDot
+ externalQDot,
)
updatedGridState match {
@@ -203,25 +210,25 @@ class ThermalGridWithHouseAndStorageSpec
Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)),
Some(
ThermalStorageState(storageTick, storedEnergy, qDotStorage)
- )
+ ),
) =>
houseTick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDotHouse =~ Kilowatts(0d)) shouldBe true
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDotHouse should approximate(Kilowatts(0d))
storageTick shouldBe 0L
- (storedEnergy =~ initialLoading) shouldBe true
- (qDotStorage =~ externalQDot) shouldBe true
+ storedEnergy should approximate(initialLoading)
+ qDotStorage should approximate(externalQDot)
case _ => fail("Thermal grid state has been calculated wrong.")
}
- reachedThreshold shouldBe Some(StorageEmpty(17142L))
+ reachedThreshold shouldBe Some(StorageEmpty(17143L))
}
}
"revising infeed from storage to house" should {
val zeroInflux = Kilowatts(0d)
val tick = 3600L
- val testGridambientTemperature = Celsius(14d)
+ val ambientTemperature = Celsius(14d)
"hand back unaltered information if needed information is missing" in {
val maybeHouseState = Some(
(
@@ -231,11 +238,11 @@ class ThermalGridWithHouseAndStorageSpec
thermalHouseInput.getTargetTemperature
.to(Units.CELSIUS)
.getValue
- .doubleValue()
+ .doubleValue
),
- zeroInflux
+ zeroInflux,
),
- None
+ None,
)
)
val maybeStorageState = None
@@ -247,7 +254,7 @@ class ThermalGridWithHouseAndStorageSpec
maybeHouseState.map(_._1),
None,
testGridambientTemperature,
- testGridQDotConsumption
+ testGridQDotConsumption,
) match {
case (maybeRevisedHouseState, maybeRevisedStorageState) =>
maybeRevisedHouseState shouldBe maybeHouseState
@@ -264,11 +271,11 @@ class ThermalGridWithHouseAndStorageSpec
thermalHouseInput.getTargetTemperature
.to(Units.CELSIUS)
.getValue
- .doubleValue()
+ .doubleValue
),
- zeroInflux
+ zeroInflux,
),
- None
+ None,
)
)
val maybeStorageState = Some(
@@ -276,9 +283,9 @@ class ThermalGridWithHouseAndStorageSpec
ThermalStorageState(
tick,
KilowattHours(50d),
- zeroInflux
+ zeroInflux,
),
- None
+ None,
)
)
@@ -288,8 +295,8 @@ class ThermalGridWithHouseAndStorageSpec
maybeStorageState,
maybeHouseState.map(_._1),
maybeStorageState.map(_._1),
- testGridambientTemperature,
- zeroInflux
+ ambientTemperature,
+ zeroInflux,
) match {
case (maybeRevisedHouseState, maybeRevisedStorageState) =>
maybeRevisedHouseState shouldBe maybeHouseState
@@ -306,11 +313,11 @@ class ThermalGridWithHouseAndStorageSpec
thermalHouseInput.getTargetTemperature
.to(Units.CELSIUS)
.getValue
- .doubleValue()
+ .doubleValue
),
- testGridQDotInfeed
+ testGridQDotInfeed,
),
- Some(HouseTemperatureUpperBoundaryReached(3600L))
+ Some(HouseTemperatureUpperBoundaryReached(3600L)),
)
)
val maybeStorageState = Some(
@@ -318,9 +325,9 @@ class ThermalGridWithHouseAndStorageSpec
ThermalStorageState(
tick,
KilowattHours(50d),
- zeroInflux
+ zeroInflux,
),
- None
+ None,
)
)
@@ -330,8 +337,8 @@ class ThermalGridWithHouseAndStorageSpec
maybeStorageState,
maybeHouseState.map(_._1),
maybeStorageState.map(_._1),
- testGridambientTemperature,
- testGridQDotInfeed
+ ambientTemperature,
+ testGridQDotInfeed,
) match {
case (maybeRevisedHouseState, maybeRevisedStorageState) =>
maybeRevisedHouseState shouldBe maybeHouseState
@@ -348,11 +355,11 @@ class ThermalGridWithHouseAndStorageSpec
thermalHouseInput.getLowerTemperatureLimit
.to(Units.CELSIUS)
.getValue
- .doubleValue()
+ .doubleValue
),
- zeroInflux
+ zeroInflux,
),
- Some(HouseTemperatureLowerBoundaryReached(tick))
+ Some(HouseTemperatureLowerBoundaryReached(tick)),
)
)
val maybeStorageState = Some(
@@ -360,9 +367,9 @@ class ThermalGridWithHouseAndStorageSpec
ThermalStorageState(
tick,
KilowattHours(50d),
- testGridQDotInfeed
+ testGridQDotInfeed,
),
- Some(StorageEmpty(tick))
+ Some(StorageEmpty(tick)),
)
)
@@ -372,8 +379,8 @@ class ThermalGridWithHouseAndStorageSpec
maybeStorageState,
maybeHouseState.map(_._1),
maybeStorageState.map(_._1),
- testGridambientTemperature,
- zeroInflux
+ ambientTemperature,
+ zeroInflux,
) match {
case (maybeRevisedHouseState, maybeRevisedStorageState) =>
maybeRevisedHouseState shouldBe maybeHouseState
@@ -390,11 +397,11 @@ class ThermalGridWithHouseAndStorageSpec
thermalHouseInput.getLowerTemperatureLimit
.to(Units.CELSIUS)
.getValue
- .doubleValue()
+ .doubleValue
),
- zeroInflux
+ zeroInflux,
),
- Some(HouseTemperatureLowerBoundaryReached(tick))
+ Some(HouseTemperatureLowerBoundaryReached(tick)),
)
)
val maybeStorageState = Some(
@@ -402,9 +409,9 @@ class ThermalGridWithHouseAndStorageSpec
ThermalStorageState(
tick,
KilowattHours(250d),
- testGridQDotInfeed
+ testGridQDotInfeed,
),
- None
+ None,
)
)
val formerHouseState = Some(
@@ -414,16 +421,16 @@ class ThermalGridWithHouseAndStorageSpec
thermalHouseInput.getTargetTemperature
.to(Units.CELSIUS)
.getValue
- .doubleValue()
+ .doubleValue
),
- zeroInflux
+ zeroInflux,
)
)
val formerStorageState = Some(
ThermalStorageState(
0L,
KilowattHours(300d),
- Kilowatts(-50d)
+ Kilowatts(-50d),
)
)
@@ -433,29 +440,30 @@ class ThermalGridWithHouseAndStorageSpec
maybeStorageState,
formerHouseState,
formerStorageState,
- testGridambientTemperature,
- zeroInflux
+ ambientTemperature,
+ zeroInflux,
) match {
case (
Some(
(
ThermalHouseState(houseTick, _, revisedQDotHouse),
- Some(HouseTemperatureUpperBoundaryReached(houseColdTick))
+ Some(HouseTemperatureUpperBoundaryReached(houseColdTick)),
)
),
Some(
(
ThermalStorageState(storageTick, _, revisedQDotStorage),
- Some(StorageEmpty(storageEmptyTick))
+ Some(StorageEmpty(storageEmptyTick)),
)
- )
+ ),
) =>
houseTick shouldBe tick
storageTick shouldBe tick
- (revisedQDotHouse =~ thermalStorage.chargingPower) shouldBe true
-
- (revisedQDotStorage =~ thermalStorage.chargingPower * (-1)) shouldBe true
+ revisedQDotHouse should approximate(thermalStorage.chargingPower)
+ revisedQDotStorage should approximate(
+ thermalStorage.chargingPower * (-1)
+ )
houseColdTick shouldBe 3718L
storageEmptyTick shouldBe 3678L
@@ -480,7 +488,7 @@ class ThermalGridWithHouseAndStorageSpec
tick,
testGridambientTemperature,
initialGridState,
- externalQDot
+ externalQDot,
)
updatedGridState match {
@@ -488,18 +496,20 @@ class ThermalGridWithHouseAndStorageSpec
Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)),
Some(
ThermalStorageState(storageTick, storedEnergy, qDotStorage)
- )
+ ),
) =>
houseTick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDotHouse =~ externalQDot) shouldBe true
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDotHouse should approximate(externalQDot)
storageTick shouldBe -1L
- (storedEnergy =~ initialGridState.storageState
- .map(_.storedEnergy)
- .getOrElse(fail("No initial storage state found"))) shouldBe true
+ storedEnergy should approximate(
+ initialGridState.storageState
+ .map(_.storedEnergy)
+ .getOrElse(fail("No initial storage state found"))
+ )
- (qDotStorage =~ Kilowatts(0d)) shouldBe true
+ qDotStorage should approximate(Kilowatts(0d))
case _ => fail("Thermal grid state has been calculated wrong.")
}
@@ -523,7 +533,7 @@ class ThermalGridWithHouseAndStorageSpec
tick,
testGridambientTemperature,
gridState,
- externalQDot
+ externalQDot,
)
updatedGridState match {
@@ -531,21 +541,19 @@ class ThermalGridWithHouseAndStorageSpec
Some(ThermalHouseState(houseTick, innerTemperature, qDotHouse)),
Some(
ThermalStorageState(storageTick, storedEnergy, qDotStorage)
- )
+ ),
) =>
houseTick shouldBe 0L
- (innerTemperature =~ Celsius(20.99999167d)) shouldBe true
- (qDotHouse =~ Kilowatts(0d)) shouldBe true
+ innerTemperature should approximate(Celsius(20.99999167d))
+ qDotHouse should approximate(Kilowatts(0d))
storageTick shouldBe 0L
- (storedEnergy =~
+ storedEnergy should approximate(
gridState.storageState
.map(_.storedEnergy)
- .getOrElse(
- fail("No initial storage state found")
- )) shouldBe true
-
- (qDotStorage =~ externalQDot) shouldBe true
+ .getOrElse(fail("No initial storage state found"))
+ )
+ qDotStorage should approximate(externalQDot)
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala
index edd2392208..ac96c4c541 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithHouseOnlySpec.scala
@@ -11,7 +11,7 @@ import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseState
import edu.ie3.simona.model.thermal.ThermalHouse.ThermalHouseThreshold.{
HouseTemperatureLowerBoundaryReached,
- HouseTemperatureUpperBoundaryReached
+ HouseTemperatureUpperBoundaryReached,
}
import edu.ie3.simona.test.common.UnitSpec
import squants.energy.{Kilowatts, Megawatts, WattHours, Watts}
@@ -32,7 +32,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
new edu.ie3.datamodel.models.input.container.ThermalGrid(
thermalBusInput,
Set(thermalHouseInput).asJava,
- Set.empty[ThermalStorageInput].asJava
+ Set.empty[ThermalStorageInput].asJava,
)
ThermalGrid(thermalGridInput) match {
@@ -49,7 +49,7 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
new edu.ie3.datamodel.models.input.container.ThermalGrid(
thermalBusInput,
Set(thermalHouseInput).asJava,
- Set.empty[ThermalStorageInput].asJava
+ Set.empty[ThermalStorageInput].asJava,
)
)
@@ -58,11 +58,13 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
ThermalGrid.startingState(thermalGrid) match {
case ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, thermalInfeed)),
- None
+ None,
) =>
tick shouldBe expectedHouseStartingState.tick
- (innerTemperature =~ expectedHouseStartingState.innerTemperature) shouldBe true
- (thermalInfeed =~ expectedHouseStartingState.qDot) shouldBe true
+ innerTemperature should approximate(
+ expectedHouseStartingState.innerTemperature
+ )
+ thermalInfeed should approximate(expectedHouseStartingState.qDot)
case _ => fail("Determination of starting state failed")
}
@@ -75,17 +77,17 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
val houseDemand = thermalHouse.energyDemand(
tick,
testGridambientTemperature,
- expectedHouseStartingState
+ expectedHouseStartingState,
)
val gridDemand = thermalGrid.energyDemand(
tick,
testGridambientTemperature,
- ThermalGrid.startingState(thermalGrid)
+ ThermalGrid.startingState(thermalGrid),
)
- (gridDemand.required =~ houseDemand.required) shouldBe true
- (gridDemand.possible =~ houseDemand.possible) shouldBe true
+ gridDemand.required should approximate(houseDemand.required)
+ gridDemand.possible should approximate(houseDemand.possible)
}
}
@@ -105,21 +107,21 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
tick,
testGridambientTemperature,
gridState,
- externalQDot
+ externalQDot,
)
updatedGridState match {
case ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, qDot)),
- None
+ None,
) =>
tick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDot =~ externalQDot) shouldBe true
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDot should approximate(externalQDot)
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(
- HouseTemperatureLowerBoundaryReached(154284L)
+ HouseTemperatureLowerBoundaryReached(154285L)
)
}
@@ -132,21 +134,21 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
tick,
testGridambientTemperature,
gridState,
- testGridQDotConsumption
+ testGridQDotConsumption,
)
updatedGridState match {
case ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, qDot)),
- None
+ None,
) =>
tick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDot =~ Megawatts(0d)) shouldBe true
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDot should approximate(Megawatts(0d))
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(
- HouseTemperatureLowerBoundaryReached(154284L)
+ HouseTemperatureLowerBoundaryReached(154285L)
)
}
}
@@ -166,17 +168,17 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
tick,
testGridambientTemperature,
gridState,
- testGridQDotInfeed
+ testGridQDotInfeed,
)
updatedGridState match {
case ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, qDot)),
- None
+ None,
) =>
tick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDot =~ testGridQDotInfeed) shouldBe true
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDot should approximate(testGridQDotInfeed)
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(
@@ -191,18 +193,18 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
0L,
ThermalGrid.startingState(thermalGrid),
testGridambientTemperature,
- testGridQDotInfeed
+ testGridQDotInfeed,
) match {
case (
ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, qDot)),
- None
+ None,
),
- Some(HouseTemperatureUpperBoundaryReached(thresholdTick))
+ Some(HouseTemperatureUpperBoundaryReached(thresholdTick)),
) =>
tick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDot =~ testGridQDotInfeed) shouldBe true
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDot should approximate(testGridQDotInfeed)
thresholdTick shouldBe 7372L
case _ => fail("Thermal grid state updated failed")
}
@@ -213,19 +215,19 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
0L,
ThermalGrid.startingState(thermalGrid),
testGridambientTemperature,
- testGridQDotConsumption
+ testGridQDotConsumption,
) match {
case (
ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, qDot)),
- None
+ None,
),
- Some(HouseTemperatureLowerBoundaryReached(thresholdTick))
+ Some(HouseTemperatureLowerBoundaryReached(thresholdTick)),
) =>
tick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDot =~ Megawatts(0d)) shouldBe true
- thresholdTick shouldBe 154284L
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDot should approximate(Megawatts(0d))
+ thresholdTick shouldBe 154285L
case _ => fail("Thermal grid state updated failed")
}
}
@@ -235,19 +237,19 @@ class ThermalGridWithHouseOnlySpec extends UnitSpec with ThermalHouseTestData {
0L,
ThermalGrid.startingState(thermalGrid),
testGridambientTemperature,
- Megawatts(0d)
+ Megawatts(0d),
) match {
case (
ThermalGridState(
Some(ThermalHouseState(tick, innerTemperature, qDot)),
- None
+ None,
),
- Some(HouseTemperatureLowerBoundaryReached(thresholdTick))
+ Some(HouseTemperatureLowerBoundaryReached(thresholdTick)),
) =>
tick shouldBe 0L
- (innerTemperature =~ Celsius(18.9999d)) shouldBe true
- (qDot =~ Kilowatts(0d)) shouldBe true
- thresholdTick shouldBe 154284L
+ innerTemperature should approximate(Celsius(18.9999d))
+ qDot should approximate(Kilowatts(0d))
+ thresholdTick shouldBe 154285L
case _ => fail("Thermal grid state updated failed")
}
}
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala
index 43ccceb75a..fe4276909f 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalGridWithStorageOnlySpec.scala
@@ -8,13 +8,13 @@ package edu.ie3.simona.model.thermal
import edu.ie3.datamodel.models.input.thermal.{
ThermalHouseInput,
- ThermalStorageInput
+ ThermalStorageInput,
}
import edu.ie3.simona.model.thermal.ThermalGrid.ThermalGridState
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageState
import edu.ie3.simona.model.thermal.ThermalStorage.ThermalStorageThreshold.{
StorageEmpty,
- StorageFull
+ StorageFull,
}
import edu.ie3.simona.test.common.UnitSpec
import squants.energy._
@@ -37,7 +37,7 @@ class ThermalGridWithStorageOnlySpec
new edu.ie3.datamodel.models.input.container.ThermalGrid(
thermalBusInput,
Set.empty[ThermalHouseInput].asJava,
- Set[ThermalStorageInput](thermalStorageInput).asJava
+ Set[ThermalStorageInput](thermalStorageInput).asJava,
)
ThermalGrid(thermalGridInput) match {
@@ -54,7 +54,7 @@ class ThermalGridWithStorageOnlySpec
new edu.ie3.datamodel.models.input.container.ThermalGrid(
thermalBusInput,
Set.empty[ThermalHouseInput].asJava,
- Set[ThermalStorageInput](thermalStorageInput).asJava
+ Set[ThermalStorageInput](thermalStorageInput).asJava,
)
)
@@ -63,11 +63,13 @@ class ThermalGridWithStorageOnlySpec
ThermalGrid.startingState(thermalGrid) match {
case ThermalGridState(
None,
- Some(ThermalStorageState(tick, storedEnergy, qDot))
+ Some(ThermalStorageState(tick, storedEnergy, qDot)),
) =>
tick shouldBe expectedStorageStartingState.tick
- (storedEnergy =~ expectedStorageStartingState.storedEnergy) shouldBe true
- (qDot =~ expectedStorageStartingState.qDot) shouldBe true
+ storedEnergy should approximate(
+ expectedStorageStartingState.storedEnergy
+ )
+ qDot should approximate(expectedStorageStartingState.qDot)
case _ => fail("Determination of starting state failed")
}
@@ -81,11 +83,11 @@ class ThermalGridWithStorageOnlySpec
val gridDemand = thermalGrid.energyDemand(
tick,
testGridambientTemperature,
- ThermalGrid.startingState(thermalGrid)
+ ThermalGrid.startingState(thermalGrid),
)
- (gridDemand.required =~ MegawattHours(0d)) shouldBe true
- (gridDemand.possible =~ MegawattHours(0.92d)) shouldBe true
+ gridDemand.required should approximate(MegawattHours(0d))
+ gridDemand.possible should approximate(MegawattHours(0.92d))
}
}
@@ -104,7 +106,7 @@ class ThermalGridWithStorageOnlySpec
ThermalStorageState(
0L,
KilowattHours(430d),
- Kilowatts(0d)
+ Kilowatts(0d),
)
)
)
@@ -114,18 +116,17 @@ class ThermalGridWithStorageOnlySpec
tick,
testGridambientTemperature,
gridState,
- testGridQDotConsumptionHigh
+ testGridQDotConsumptionHigh,
)
updatedGridState match {
case ThermalGridState(
None,
- Some(ThermalStorageState(tick, storedEnergy, qDot))
+ Some(ThermalStorageState(tick, storedEnergy, qDot)),
) =>
tick shouldBe 0L
- (storedEnergy =~ KilowattHours(430d)) shouldBe true
-
- (qDot =~ testGridQDotConsumptionHigh) shouldBe true
+ storedEnergy should approximate(KilowattHours(430d))
+ qDot should approximate(testGridQDotConsumptionHigh)
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(StorageEmpty(3600L))
@@ -147,17 +148,17 @@ class ThermalGridWithStorageOnlySpec
tick,
testGridambientTemperature,
gridState,
- testGridQDotInfeed
+ testGridQDotInfeed,
)
updatedGridState match {
case ThermalGridState(
None,
- Some(ThermalStorageState(tick, storedEnergy, qDot))
+ Some(ThermalStorageState(tick, storedEnergy, qDot)),
) =>
tick shouldBe 0L
- (storedEnergy =~ KilowattHours(230d)) shouldBe true
- (qDot =~ testGridQDotInfeed) shouldBe true
+ storedEnergy should approximate(KilowattHours(230d))
+ qDot should approximate(testGridQDotInfeed)
case _ => fail("Thermal grid state has been calculated wrong.")
}
reachedThreshold shouldBe Some(StorageFull(220800L))
@@ -170,7 +171,7 @@ class ThermalGridWithStorageOnlySpec
0L,
ThermalGrid.startingState(thermalGrid),
testGridambientTemperature,
- testGridQDotInfeed
+ testGridQDotInfeed,
)
nextThreshold shouldBe Some(StorageFull(220800L))
@@ -178,11 +179,11 @@ class ThermalGridWithStorageOnlySpec
updatedState match {
case ThermalGridState(
None,
- Some(ThermalStorageState(tick, storedEnergy, qDot))
+ Some(ThermalStorageState(tick, storedEnergy, qDot)),
) =>
tick shouldBe 0L
- (storedEnergy =~ KilowattHours(230d)) shouldBe true
- (qDot =~ testGridQDotInfeed) shouldBe true
+ storedEnergy should approximate(KilowattHours(230d))
+ qDot should approximate(testGridQDotInfeed)
case _ => fail("Thermal grid state updated failed")
}
}
@@ -197,24 +198,23 @@ class ThermalGridWithStorageOnlySpec
ThermalStorageState(
0L,
KilowattHours(430d),
- Kilowatts(0d)
+ Kilowatts(0d),
)
)
),
testGridambientTemperature,
- testGridQDotConsumptionHigh
+ testGridQDotConsumptionHigh,
) match {
case (
ThermalGridState(
None,
- Some(ThermalStorageState(tick, storedEnergy, qDot))
+ Some(ThermalStorageState(tick, storedEnergy, qDot)),
),
- Some(StorageEmpty(thresholdTick))
+ Some(StorageEmpty(thresholdTick)),
) =>
tick shouldBe 0L
- (storedEnergy =~ KilowattHours(430d)) shouldBe true
-
- (qDot =~ testGridQDotConsumptionHigh) shouldBe true
+ storedEnergy should approximate(KilowattHours(430d))
+ qDot should approximate(testGridQDotConsumptionHigh)
thresholdTick shouldBe 3600L
case _ => fail("Thermal grid state updated failed")
}
@@ -225,20 +225,19 @@ class ThermalGridWithStorageOnlySpec
0L,
ThermalGrid.startingState(thermalGrid),
testGridambientTemperature,
- Kilowatts(0d)
+ Kilowatts(0d),
)
updatedState match {
case (
ThermalGridState(
None,
- Some(ThermalStorageState(tick, storedEnergy, qDot))
+ Some(ThermalStorageState(tick, storedEnergy, qDot)),
),
- None
+ None,
) =>
tick shouldBe 0L
- (storedEnergy =~ KilowattHours(230d)) shouldBe true
-
- (qDot =~ Megawatts(0d)) shouldBe true
+ storedEnergy should approximate(KilowattHours(230d))
+ qDot should approximate(Megawatts(0d))
case _ => fail("Thermal grid state updated failed")
}
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseTestData.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseTestData.scala
index 78b186296d..2b12dab681 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseTestData.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalHouseTestData.scala
@@ -25,7 +25,7 @@ trait ThermalHouseTestData extends ThermalGridTestData {
getQuantity(15.0, StandardUnits.HEAT_CAPACITY),
getQuantity(19d, Units.CELSIUS),
getQuantity(21d, Units.CELSIUS),
- getQuantity(18d, Units.CELSIUS)
+ getQuantity(18d, Units.CELSIUS),
)
protected val thermalHouse: ThermalHouse = ThermalHouse(thermalHouseInput)
@@ -34,6 +34,6 @@ trait ThermalHouseTestData extends ThermalGridTestData {
ThermalHouseState(
-1L,
Celsius(19d),
- Megawatts(0d)
+ Megawatts(0d),
)
}
diff --git a/src/test/scala/edu/ie3/simona/model/thermal/ThermalStorageTestData.scala b/src/test/scala/edu/ie3/simona/model/thermal/ThermalStorageTestData.scala
index 582ff025cb..b5434bbb17 100644
--- a/src/test/scala/edu/ie3/simona/model/thermal/ThermalStorageTestData.scala
+++ b/src/test/scala/edu/ie3/simona/model/thermal/ThermalStorageTestData.scala
@@ -9,7 +9,7 @@ package edu.ie3.simona.model.thermal
import edu.ie3.datamodel.models.StandardUnits
import edu.ie3.datamodel.models.input.thermal.{
CylindricalStorageInput,
- ThermalBusInput
+ ThermalBusInput,
}
import tech.units.indriya.quantity.Quantities.getQuantity
@@ -22,13 +22,13 @@ trait ThermalStorageTestData extends ThermalGridTestData {
"ThermalStorage",
new ThermalBusInput(
UUID.fromString("ad2db5ab-8f90-4bc1-aa2c-30b31b843ab2"),
- "TestThermalBus"
+ "TestThermalBus",
),
getQuantity(100, StandardUnits.VOLUME),
getQuantity(20, StandardUnits.VOLUME),
getQuantity(30, StandardUnits.TEMPERATURE),
getQuantity(40, StandardUnits.TEMPERATURE),
- getQuantity(1.15, StandardUnits.SPECIFIC_HEAT_CAPACITY)
+ getQuantity(1.15, StandardUnits.SPECIFIC_HEAT_CAPACITY),
)
protected val thermalStorage: CylindricalThermalStorage =
diff --git a/src/test/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessageTest.scala b/src/test/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessageTest.scala
new file mode 100644
index 0000000000..63da1cbfa2
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/ontology/messages/flex/MinMaxFlexibilityMessageTest.scala
@@ -0,0 +1,66 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.ontology.messages.flex
+
+import edu.ie3.simona.exceptions.CriticalFailureException
+import edu.ie3.simona.ontology.messages.flex.MinMaxFlexibilityMessage.ProvideMinMaxFlexOptions
+import edu.ie3.simona.test.common.UnitSpec
+import squants.energy.Watts
+
+import java.util.UUID
+
+class MinMaxFlexibilityMessageTest extends UnitSpec {
+
+ "Creating a ProvideMinMaxFlexibilityMessage" should {
+
+ "succeed if there is no flexibility" in {
+ val res = ProvideMinMaxFlexOptions.noFlexOption(
+ modelUuid = UUID.randomUUID(),
+ power = Watts(1),
+ )
+
+ res.ref shouldBe Watts(1)
+ res.min shouldBe Watts(1)
+ res.max shouldBe Watts(1)
+ }
+
+ "succeed if minimum, reference and maximum power are in order" in {
+ val res = ProvideMinMaxFlexOptions(
+ modelUuid = UUID.randomUUID(),
+ ref = Watts(1),
+ min = Watts(0),
+ max = Watts(2),
+ )
+
+ res.ref shouldBe Watts(1)
+ res.min shouldBe Watts(0)
+ res.max shouldBe Watts(2)
+ }
+
+ "throw an exception if minimum power is greater then reference power" in {
+ intercept[CriticalFailureException] {
+ ProvideMinMaxFlexOptions(
+ modelUuid = UUID.randomUUID(),
+ ref = Watts(1),
+ min = Watts(2),
+ max = Watts(2),
+ )
+ }.getMessage should include("is greater than reference power")
+ }
+
+ "throw an exception if reference power is greater then maximum power" in {
+ intercept[CriticalFailureException] {
+ ProvideMinMaxFlexOptions(
+ modelUuid = UUID.randomUUID(),
+ ref = Watts(1),
+ min = Watts(1),
+ max = Watts(0),
+ )
+ }.getMessage should include("is greater than maximum power")
+ }
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala b/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala
index b2c6dae582..447a2d2a44 100644
--- a/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/scheduler/PhaseSwitchSchedulerSpec.scala
@@ -8,7 +8,7 @@ package edu.ie3.simona.scheduler
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.core.PhaseSwitchCore
@@ -16,7 +16,7 @@ import edu.ie3.simona.util.ActorUtils.RichActivatedActor
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ScalaTestWithActorTestKit,
- TestProbe
+ TestProbe,
}
import org.scalatest.matchers.should
import org.scalatest.wordspec.AnyWordSpecLike
@@ -145,7 +145,7 @@ class PhaseSwitchSchedulerSpec
scheduler ! ScheduleActivation(
agent2.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
// waiting for next phase
@@ -221,7 +221,7 @@ class PhaseSwitchSchedulerSpec
scheduler ! ScheduleActivation(
agent1.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
val sa1 = parent.expectMessageType[ScheduleActivation]
@@ -230,7 +230,7 @@ class PhaseSwitchSchedulerSpec
scheduler ! ScheduleActivation(
agent2.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
// TICK -1
@@ -240,7 +240,7 @@ class PhaseSwitchSchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
INIT_SIM_TICK,
- Some(0)
+ Some(0),
)
parent.expectNoMessage()
@@ -248,7 +248,7 @@ class PhaseSwitchSchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
INIT_SIM_TICK,
- Some(0)
+ Some(0),
)
parent.expectMessage(Completion(schedulerActivation, Some(0)))
@@ -262,7 +262,7 @@ class PhaseSwitchSchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
0,
- Some(900)
+ Some(900),
)
parent.expectNoMessage()
@@ -270,7 +270,7 @@ class PhaseSwitchSchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
0,
- Some(300)
+ Some(300),
)
parent.expectMessage(Completion(schedulerActivation, Some(300)))
@@ -281,7 +281,7 @@ class PhaseSwitchSchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
300,
- Some(900)
+ Some(900),
)
parent.expectMessage(Completion(schedulerActivation, Some(900)))
@@ -295,7 +295,7 @@ class PhaseSwitchSchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
900,
- Some(3600)
+ Some(3600),
)
parent.expectNoMessage()
@@ -303,7 +303,7 @@ class PhaseSwitchSchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
900,
- Some(1800)
+ Some(1800),
)
parent.expectMessage(Completion(schedulerActivation, Some(1800)))
@@ -315,7 +315,33 @@ class PhaseSwitchSchedulerSpec
}
- "The Scheduler should fail and stop" when {
+ "The Scheduler with PhaseSwitchCore should fail and stop" when {
+
+ "activated if no activation is scheduled" in {
+ val parent = TestProbe[SchedulerMessage]("parent")
+ val scheduler = spawn(
+ Scheduler(parent.ref, PhaseSwitchCore)
+ )
+
+ val agent1 = TestProbe[Activation]("agent_1")
+
+ scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK)
+
+ val sa1 = parent.expectMessageType[ScheduleActivation]
+ sa1.tick shouldBe INIT_SIM_TICK
+ val schedulerActivation = sa1.actor
+
+ // TICK -1
+ schedulerActivation ! Activation(INIT_SIM_TICK)
+ agent1.expectActivationAndComplete(scheduler, INIT_SIM_TICK, None)
+ parent.expectMessage(Completion(schedulerActivation, None))
+
+ // No activation scheduled, this should fail now
+ schedulerActivation ! Activation(0)
+
+ // scheduler stopped
+ parent.expectTerminated(scheduler)
+ }
"activated with wrong tick" in {
val parent = TestProbe[SchedulerMessage]("parent")
@@ -362,7 +388,7 @@ class PhaseSwitchSchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
900,
- Some(1800)
+ Some(1800),
)
parent.expectMessage(Completion(schedulerActivation, Some(1800)))
diff --git a/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockIT.scala b/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockIT.scala
index af4586e56e..e001a45a8b 100644
--- a/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockIT.scala
+++ b/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockIT.scala
@@ -8,11 +8,11 @@ package edu.ie3.simona.scheduler
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ScalaTestWithActorTestKit,
- TestProbe
+ TestProbe,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.test.common.TestSpawnerTyped
@@ -35,17 +35,17 @@ class ScheduleLockIT
val parentScheduler = spawn(
Scheduler(timeAdvancer.ref),
- "parentScheduler"
+ "parentScheduler",
)
val childScheduler = spawn(
Scheduler(parentScheduler),
- "childScheduler"
+ "childScheduler",
)
// first, we normally schedule some activation for our agent1
childScheduler ! ScheduleActivation(
agent1.ref,
- 30
+ 30,
)
val sa1 = timeAdvancer.expectMessageType[ScheduleActivation]
sa1.tick shouldBe 30
@@ -63,7 +63,7 @@ class ScheduleLockIT
childScheduler ! ScheduleActivation(
agent2.ref,
30,
- Some(scheduleKey)
+ Some(scheduleKey),
)
// because of activated agents, child/parentScheduler should not be able to complete yet
@@ -72,11 +72,11 @@ class ScheduleLockIT
// completing agent activations
agent1.expectActivationAndComplete(
childScheduler,
- 30
+ 30,
)
agent2.expectActivationAndComplete(
childScheduler,
- 30
+ 30,
)
timeAdvancer.expectMessage(Completion(lockActivation))
@@ -96,7 +96,7 @@ class ScheduleLockIT
// first, we normally schedule some activation
childScheduler ! ScheduleActivation(
agent.ref,
- 30
+ 30,
)
val sa1 = timeAdvancer.expectMessageType[ScheduleActivation]
sa1.tick shouldBe 30
@@ -112,7 +112,7 @@ class ScheduleLockIT
// completing the agent activation
agent.expectActivationAndComplete(
childScheduler,
- 30
+ 30,
)
// because of the lock, parentScheduler should not be able to complete yet
@@ -122,7 +122,7 @@ class ScheduleLockIT
childScheduler ! ScheduleActivation(
agent.ref,
40,
- Some(scheduleKey)
+ Some(scheduleKey),
)
timeAdvancer.expectMessage(Completion(lockActivation, Some(40)))
diff --git a/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockSpec.scala b/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockSpec.scala
index e79a4c8c6e..b075c077dd 100644
--- a/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockSpec.scala
+++ b/src/test/scala/edu/ie3/simona/scheduler/ScheduleLockSpec.scala
@@ -8,12 +8,12 @@ package edu.ie3.simona.scheduler
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ScalaTestWithActorTestKit,
- TestProbe
+ TestProbe,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.test.common.TestSpawnerTyped
import org.scalatest.matchers.should
diff --git a/src/test/scala/edu/ie3/simona/scheduler/SchedulerSpec.scala b/src/test/scala/edu/ie3/simona/scheduler/SchedulerSpec.scala
index 072fc80f32..0af59c8459 100644
--- a/src/test/scala/edu/ie3/simona/scheduler/SchedulerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/scheduler/SchedulerSpec.scala
@@ -8,11 +8,11 @@ package edu.ie3.simona.scheduler
import org.apache.pekko.actor.testkit.typed.scaladsl.{
ScalaTestWithActorTestKit,
- TestProbe
+ TestProbe,
}
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.{Activation, SchedulerMessage}
import edu.ie3.simona.scheduler.ScheduleLock.{LockMsg, ScheduleKey, Unlock}
@@ -90,7 +90,7 @@ class SchedulerSpec
scheduler ! ScheduleActivation(
agent2.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
// activation is sent right away
@@ -159,7 +159,7 @@ class SchedulerSpec
scheduler ! ScheduleActivation(
agent1.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
val sa1 = parent.expectMessageType[ScheduleActivation]
@@ -168,7 +168,7 @@ class SchedulerSpec
scheduler ! ScheduleActivation(
agent2.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
// TICK -1
@@ -177,7 +177,7 @@ class SchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
INIT_SIM_TICK,
- Some(0)
+ Some(0),
)
parent.expectNoMessage()
@@ -185,7 +185,7 @@ class SchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
INIT_SIM_TICK,
- Some(0)
+ Some(0),
)
parent.expectMessage(Completion(schedulerActivation, Some(0)))
@@ -199,7 +199,7 @@ class SchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
0,
- Some(300)
+ Some(300),
)
parent.expectNoMessage()
@@ -207,7 +207,7 @@ class SchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
0,
- Some(900)
+ Some(900),
)
parent.expectMessage(Completion(schedulerActivation, Some(300)))
@@ -218,7 +218,7 @@ class SchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
300,
- Some(900)
+ Some(900),
)
parent.expectMessage(Completion(schedulerActivation, Some(900)))
@@ -232,7 +232,7 @@ class SchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
900,
- Some(3600)
+ Some(3600),
)
parent.expectNoMessage()
@@ -240,7 +240,7 @@ class SchedulerSpec
agent2.expectActivationAndComplete(
scheduler,
900,
- Some(1800)
+ Some(1800),
)
parent.expectMessage(Completion(schedulerActivation, Some(1800)))
@@ -263,7 +263,7 @@ class SchedulerSpec
// send to init activation to scheduler
scheduler ! ScheduleActivation(
actor.ref,
- INIT_SIM_TICK
+ INIT_SIM_TICK,
)
)
@@ -278,7 +278,7 @@ class SchedulerSpec
_.expectActivationAndComplete(
scheduler,
tick,
- Some(tick + 1)
+ Some(tick + 1),
)
}
@@ -310,7 +310,7 @@ class SchedulerSpec
scheduler ! ScheduleActivation(
agent2.ref,
120,
- Some(ScheduleKey(lock.ref, key))
+ Some(ScheduleKey(lock.ref, key)),
)
// no new scheduling when active
@@ -338,7 +338,7 @@ class SchedulerSpec
scheduler ! ScheduleActivation(
agent1.ref,
60,
- Some(ScheduleKey(lock.ref, key))
+ Some(ScheduleKey(lock.ref, key)),
)
// no new scheduling for same tick
@@ -367,7 +367,7 @@ class SchedulerSpec
scheduler ! ScheduleActivation(
agent1.ref,
59,
- Some(ScheduleKey(lock.ref, key))
+ Some(ScheduleKey(lock.ref, key)),
)
// lock should not receive unlock message by scheduler
@@ -378,7 +378,7 @@ class SchedulerSpec
ScheduleActivation(
schedulerActivation,
59,
- Some(ScheduleKey(lock.ref, key))
+ Some(ScheduleKey(lock.ref, key)),
)
)
}
@@ -387,6 +387,32 @@ class SchedulerSpec
"The Scheduler should fail and stop" when {
+ "activated if no activation is scheduled" in {
+ val parent = TestProbe[SchedulerMessage]("parent")
+ val scheduler = spawn(
+ Scheduler(parent.ref)
+ )
+
+ val agent1 = TestProbe[Activation]("agent_1")
+
+ scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK)
+
+ val sa1 = parent.expectMessageType[ScheduleActivation]
+ sa1.tick shouldBe INIT_SIM_TICK
+ val schedulerActivation = sa1.actor
+
+ // TICK -1
+ schedulerActivation ! Activation(INIT_SIM_TICK)
+ agent1.expectActivationAndComplete(scheduler, INIT_SIM_TICK, None)
+ parent.expectMessage(Completion(schedulerActivation, None))
+
+ // No activation scheduled, this should fail now
+ schedulerActivation ! Activation(0)
+
+ // scheduler stopped
+ parent.expectTerminated(scheduler)
+ }
+
"activated with wrong tick" in {
val parent = TestProbe[SchedulerMessage]("parent")
val scheduler = spawn(
@@ -412,7 +438,7 @@ class SchedulerSpec
parent.expectTerminated(scheduler)
}
- "asked to schedule activation for a past tick while inactive" in {
+ "asked to schedule activation for the last tick while inactive" in {
val parent = TestProbe[SchedulerMessage]("parent")
val scheduler = spawn(
Scheduler(parent.ref)
@@ -432,14 +458,15 @@ class SchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
900,
- Some(1800)
+ Some(1800),
)
parent.expectMessage(Completion(schedulerActivation, Some(1800)))
// now inactive again
- // can't schedule activation with earlier tick than last tick (900) -> error
- scheduler ! ScheduleActivation(agent1.ref, INIT_SIM_TICK)
+ // can't schedule activation with tick equal to last tick (900) -> error
+ // same result with any other past tick
+ scheduler ! ScheduleActivation(agent1.ref, 900)
// agent does not receive activation
agent1.expectNoMessage()
@@ -553,7 +580,7 @@ class SchedulerSpec
agent1.expectActivationAndComplete(
scheduler,
INIT_SIM_TICK,
- Some(0)
+ Some(0),
)
parent.expectMessage(Completion(schedulerActivation.ref, Some(0)))
diff --git a/src/test/scala/edu/ie3/simona/scheduler/TimeAdvancerSpec.scala b/src/test/scala/edu/ie3/simona/scheduler/TimeAdvancerSpec.scala
index 21edf2210f..eaa77ef996 100644
--- a/src/test/scala/edu/ie3/simona/scheduler/TimeAdvancerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/scheduler/TimeAdvancerSpec.scala
@@ -6,22 +6,20 @@
package edu.ie3.simona.scheduler
-import org.apache.pekko.actor.testkit.typed.scaladsl.{
- ScalaTestWithActorTestKit,
- TestProbe
-}
-import org.apache.pekko.actor.typed.scaladsl.adapter.TypedActorRefOps
import edu.ie3.simona.event.RuntimeEvent
import edu.ie3.simona.event.RuntimeEvent._
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
-import edu.ie3.simona.scheduler.TimeAdvancer.{StartSimMessage, Stop}
-import edu.ie3.simona.sim.SimMessage
-import edu.ie3.simona.sim.SimMessage.{SimulationFailure, SimulationSuccessful}
+import edu.ie3.simona.scheduler.TimeAdvancer.Start
+import edu.ie3.simona.sim.SimonaSim
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
import org.scalatest.matchers.should
import org.scalatest.wordspec.AnyWordSpecLike
@@ -32,19 +30,24 @@ class TimeAdvancerSpec
with AnyWordSpecLike
with should.Matchers {
+ /** A high duration in milliseconds for RuntimeEvent duration checking. The
+ * tests should under no circumstance take longer than ten minutes.
+ */
+ private val maxEventDuration: Long = 10 * 60 * 1000
+
"The TimeAdvancer should work correctly" when {
"started checkWindow but without pauseTick" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 7200
+ 7200,
)
)
@@ -54,7 +57,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick -1 is activated
scheduler.expectMessage(Activation(INIT_SIM_TICK))
@@ -62,7 +65,10 @@ class TimeAdvancerSpec
// tick -1 is completed
timeAdvancer ! Completion(scheduler.ref, Some(0))
- listener.expectMessageType[InitComplete]
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 0 is activated automatically
scheduler.expectMessage(Activation(0))
@@ -70,9 +76,21 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref, Some(3600))
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 900
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 2700
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 900
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 2700
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 3600 is activated automatically
scheduler.expectMessage(Activation(3600))
@@ -80,10 +98,26 @@ class TimeAdvancerSpec
// tick 3600 is completed
timeAdvancer ! Completion(scheduler.ref, Some(7200))
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 3600
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 4500
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 5400
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 6300
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 4500
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 5400
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 6300
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 7200 is activated automatically
scheduler.expectMessage(Activation(7200))
@@ -91,20 +125,23 @@ class TimeAdvancerSpec
// tick 7200 is completed
timeAdvancer ! Completion(scheduler.ref)
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 7200
- doneMsg.errorInSim shouldBe false
-
- simulation.expectMessage(SimulationSuccessful)
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 7200
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
+
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"started without checkWindow and pauseTick" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
- TimeAdvancer(simulation.ref.toClassic, Some(listener.ref), None, 3600)
+ TimeAdvancer(simulation.ref, Some(listener.ref), None, 3600)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, INIT_SIM_TICK)
@@ -113,7 +150,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick -1 is activated
scheduler.expectMessage(Activation(INIT_SIM_TICK))
@@ -121,7 +158,10 @@ class TimeAdvancerSpec
// tick -1 is completed
timeAdvancer ! Completion(scheduler.ref, Some(0))
- listener.expectMessageType[InitComplete]
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 0 is activated automatically
scheduler.expectMessage(Activation(0))
@@ -137,24 +177,27 @@ class TimeAdvancerSpec
// tick 3600 is completed
timeAdvancer ! Completion(scheduler.ref)
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 3600
- doneMsg.errorInSim shouldBe false
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
- simulation.expectMessage(SimulationSuccessful)
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"paused and started after initialization" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 3600
+ 3600,
)
)
@@ -164,7 +207,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage(Some(INIT_SIM_TICK))
+ timeAdvancer ! Start(Some(INIT_SIM_TICK))
// tick -1 is activated
scheduler.expectMessage(Activation(INIT_SIM_TICK))
@@ -172,7 +215,10 @@ class TimeAdvancerSpec
// tick -1 is completed
timeAdvancer ! Completion(scheduler.ref, Some(3600))
- listener.expectMessageType[InitComplete]
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
listener.expectMessageType[Ready].tick shouldBe INIT_SIM_TICK
// simulation should be paused
@@ -180,7 +226,7 @@ class TimeAdvancerSpec
listener.expectNoMessage()
// start again
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick 3600 is activated
scheduler.expectMessage(Activation(3600))
@@ -189,28 +235,43 @@ class TimeAdvancerSpec
// tick 3600 is completed
timeAdvancer ! Completion(scheduler.ref)
// check window events should only come now, since we paused at -1 before
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 900
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 2700
-
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 3600
- doneMsg.errorInSim shouldBe false
-
- simulation.expectMessage(SimulationSuccessful)
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 900
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 2700
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
+
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"paused and started and there is a gap between StartSchedule tick and next activation tick" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 5400
+ 5400,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, INIT_SIM_TICK)
@@ -219,7 +280,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage(Some(3600))
+ timeAdvancer ! Start(Some(3600))
// tick -1 is activated
scheduler.expectMessage(Activation(INIT_SIM_TICK))
@@ -227,7 +288,10 @@ class TimeAdvancerSpec
// tick -1 is completed
timeAdvancer ! Completion(scheduler.ref, Some(0))
- listener.expectMessageType[InitComplete]
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 0 is activated automatically
scheduler.expectMessage(Activation(0))
@@ -235,10 +299,26 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref, Some(5400))
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 900
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 2700
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 3600
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 900
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 2700
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ }
listener.expectMessageType[Ready].tick shouldBe 3600
// simulation should be paused
@@ -246,7 +326,7 @@ class TimeAdvancerSpec
listener.expectNoMessage()
// start again
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick 5400 is activated
scheduler.expectMessage(Activation(5400))
@@ -254,25 +334,32 @@ class TimeAdvancerSpec
// tick 5400 is completed
timeAdvancer ! Completion(scheduler.ref)
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 4500
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 5400
- doneMsg.errorInSim shouldBe false
-
- simulation.expectMessage(SimulationSuccessful)
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 4500
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 5400
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
+
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"paused and endTick - pauseScheduleAtTick == 1" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 3600
+ 3600,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, INIT_SIM_TICK)
@@ -281,7 +368,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage(Some(3599))
+ timeAdvancer ! Start(Some(3599))
// tick -1 is activated
scheduler.expectMessage(Activation(INIT_SIM_TICK))
@@ -289,7 +376,10 @@ class TimeAdvancerSpec
// tick -1 is completed
timeAdvancer ! Completion(scheduler.ref, Some(0))
- listener.expectMessageType[InitComplete]
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 0 is activated automatically
scheduler.expectMessage(Activation(0))
@@ -297,9 +387,21 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref, Some(3600))
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 900
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 2700
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 900
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 2700
+ duration should (be >= 0L and be < maxEventDuration)
+ }
listener.expectMessageType[Ready].tick shouldBe 3599
// simulation should be paused
@@ -307,7 +409,7 @@ class TimeAdvancerSpec
listener.expectNoMessage()
// start again
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick 3600 is activated
scheduler.expectMessage(Activation(3600))
@@ -315,24 +417,27 @@ class TimeAdvancerSpec
// tick 3600 is completed
timeAdvancer ! Completion(scheduler.ref)
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 3600
- doneMsg.errorInSim shouldBe false
-
- simulation.expectMessage(SimulationSuccessful)
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
+
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"activation has been scheduled after endTick" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(1800),
- 3600
+ 3600,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, 0)
@@ -341,7 +446,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick 0 is activated
scheduler.expectMessage(Activation(0))
@@ -349,30 +454,40 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref, Some(3601))
- listener.expectMessageType[InitComplete]
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// tick 3601 should not be activated!
scheduler.expectNoMessage()
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 3600
- doneMsg.errorInSim shouldBe false
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
- simulation.expectMessage(SimulationSuccessful)
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"no next trigger has been supplied" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 3600
+ 3600,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, 0)
@@ -381,7 +496,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage()
+ timeAdvancer ! Start()
// tick 0 is activated
scheduler.expectMessage(Activation(0))
@@ -389,32 +504,50 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref)
- listener.expectMessageType[InitComplete]
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 900
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 2700
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 900
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 2700
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// scheduler should not be activated!
scheduler.expectNoMessage()
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 3600
- doneMsg.errorInSim shouldBe false
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
- simulation.expectMessage(SimulationSuccessful)
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"endTick < pauseScheduleAtTick" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(1800),
- 3600
+ 3600,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, 0)
@@ -423,7 +556,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage(Some(7200))
+ timeAdvancer ! Start(Some(7200))
// tick 0 is activated
scheduler.expectMessage(Activation(0))
@@ -431,30 +564,40 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref)
- listener.expectMessageType[InitComplete]
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 1800
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// scheduler should not be activated!
scheduler.expectNoMessage()
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 3600
- doneMsg.errorInSim shouldBe false
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 3600
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
- simulation.expectMessage(SimulationSuccessful)
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
"endTick == pauseScheduleAtTick" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 1800
+ 1800,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, 0)
@@ -463,7 +606,7 @@ class TimeAdvancerSpec
scheduler.expectNoMessage()
// start simulation
- timeAdvancer ! StartSimMessage(Some(1800))
+ timeAdvancer ! Start(Some(1800))
// tick 0 is activated
scheduler.expectMessage(Activation(0))
@@ -471,39 +614,49 @@ class TimeAdvancerSpec
// tick 0 is completed
timeAdvancer ! Completion(scheduler.ref)
- listener.expectMessageType[InitComplete]
- listener.expectMessageType[CheckWindowPassed].tick shouldBe 900
+ listener.expectMessageType[InitComplete] match {
+ case InitComplete(duration) =>
+ duration should (be >= 0L and be < maxEventDuration)
+ }
+ listener.expectMessageType[CheckWindowPassed] match {
+ case CheckWindowPassed(tick, duration) =>
+ tick shouldBe 900
+ duration should (be >= 0L and be < maxEventDuration)
+ }
// scheduler should not be activated!
scheduler.expectNoMessage()
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 1800
- doneMsg.errorInSim shouldBe false
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 1800
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe false
+ }
- simulation.expectMessage(SimulationSuccessful)
+ simulation.expectMessage(SimonaSim.SimulationEnded)
}
}
"The TimeAdvancer should fail and stop" when {
"wrong next tick has been supplied" in {
- val simulation = TestProbe[SimMessage]("simulation")
+ val simulation = TestProbe[SimonaSim.Request]("simulation")
val scheduler = TestProbe[Activation]("scheduler")
val listener = TestProbe[RuntimeEvent]("listener")
val timeAdvancer = spawn(
TimeAdvancer(
- simulation.ref.toClassic,
+ simulation.ref,
Some(listener.ref),
Some(900),
- 1800
+ 1800,
)
)
timeAdvancer ! ScheduleActivation(scheduler.ref, 0)
// start simulation
- timeAdvancer ! StartSimMessage(Some(1800))
+ timeAdvancer ! Start(Some(1800))
// tick 0 is activated
scheduler.expectMessage(Activation(0))
@@ -515,92 +668,20 @@ class TimeAdvancerSpec
listener.expectMessageType[Error].errMsg should include(
"tick -1, although current active tick was 0"
)
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 0
- doneMsg.errorInSim shouldBe true
+ listener.expectMessageType[Done] match {
+ case Done(tick, duration, errorInSim) =>
+ tick shouldBe 0
+ duration should (be >= 0L and be < maxEventDuration)
+ errorInSim shouldBe true
+ }
// scheduler should not be activated!
scheduler.expectNoMessage()
scheduler.expectTerminated(timeAdvancer)
- simulation.expectMessage(SimulationFailure)
- }
-
- "receiving error message while uninitialized" in {
- val simulation = TestProbe[SimMessage]("simulation")
- val scheduler = TestProbe[Activation]("scheduler")
- val listener = TestProbe[RuntimeEvent]("listener")
-
- val timeAdvancer = spawn(
- TimeAdvancer(simulation.ref.toClassic, Some(listener.ref), None, 1800)
- )
-
- // Send stop message
- timeAdvancer ! Stop("Test message")
-
- // we cannot check the console, thus just check if time advancer died
- scheduler.expectTerminated(timeAdvancer)
-
- simulation.expectMessage(SimulationFailure)
- }
-
- "receiving error message while inactive" in {
- val simulation = TestProbe[SimMessage]("simulation")
- val scheduler = TestProbe[Activation]("scheduler")
- val listener = TestProbe[RuntimeEvent]("listener")
-
- val timeAdvancer = spawn(
- TimeAdvancer(simulation.ref.toClassic, Some(listener.ref), None, 1800)
- )
- timeAdvancer ! ScheduleActivation(scheduler.ref, 1)
-
- // Send stop message
- timeAdvancer ! Stop("Test message")
-
- listener.expectMessageType[Error].errMsg should include("Test message")
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 1
- doneMsg.errorInSim shouldBe true
-
- scheduler.expectTerminated(timeAdvancer)
-
- simulation.expectMessage(SimulationFailure)
- }
-
- "receiving error message while active" in {
- val simulation = TestProbe[SimMessage]("simulation")
- val scheduler = TestProbe[Activation]("scheduler")
- val listener = TestProbe[RuntimeEvent]("listener")
-
- val timeAdvancer = spawn(
- TimeAdvancer(
- simulation.ref.toClassic,
- Some(listener.ref),
- Some(900),
- 1800
- )
- )
- timeAdvancer ! ScheduleActivation(scheduler.ref, 0)
-
- // start simulation
- timeAdvancer ! StartSimMessage()
-
- // tick 0 is activated
- scheduler.expectMessage(Activation(0))
- listener.expectMessage(Simulating(0, 1800))
-
- // Send stop message
- timeAdvancer ! Stop("Test message")
-
- listener.expectMessageType[Error].errMsg should include("Test message")
- val doneMsg = listener.expectMessageType[Done]
- doneMsg.tick shouldBe 0
- doneMsg.errorInSim shouldBe true
-
- scheduler.expectTerminated(timeAdvancer)
-
- simulation.expectMessage(SimulationFailure)
+ // No SimulationEnded message
+ simulation.expectNoMessage()
}
}
diff --git a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala
index 3326252b6d..5eed45eb46 100644
--- a/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/ev/ExtEvDataServiceSpec.scala
@@ -15,10 +15,11 @@ import edu.ie3.simona.api.data.ev.model.EvModel
import edu.ie3.simona.api.data.ev.ontology._
import edu.ie3.simona.api.data.ontology.ScheduleDataServiceMessage
import edu.ie3.simona.exceptions.ServiceException
+import edu.ie3.simona.model.participant.evcs.EvModelWrapper
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.EvMessage._
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage
@@ -28,7 +29,7 @@ import edu.ie3.simona.service.ev.ExtEvDataService.InitExtEvData
import edu.ie3.simona.test.common.{
EvTestData,
TestKitWithShutdown,
- TestSpawnerClassic
+ TestSpawnerClassic,
}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.quantities.PowerSystemUnits
@@ -47,7 +48,7 @@ class ExtEvDataServiceSpec
.parseString("""
|pekko.loggers = ["org.apache.pekko.testkit.TestEventListener"]
|pekko.loglevel = "INFO"
- |""".stripMargin)
+ |""".stripMargin),
)
)
with AnyWordSpecLike
@@ -60,7 +61,7 @@ class ExtEvDataServiceSpec
private val extEvData = (dataService: ActorRef) =>
new ExtEvData(
dataService,
- extSimAdapter.ref
+ extSimAdapter.ref,
)
private val evcs1UUID =
@@ -70,11 +71,7 @@ class ExtEvDataServiceSpec
"An uninitialized ev movement service" must {
"send correct completion message after initialisation" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val key =
ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
@@ -82,7 +79,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extEvData(evService)), key)
+ SimonaService.Create(InitExtEvData(extEvData(evService)), key),
)
scheduler.expectMsg(
ScheduleActivation(evService.toTyped, INIT_SIM_TICK, Some(key))
@@ -93,19 +90,12 @@ class ExtEvDataServiceSpec
}
"stash registration request and handle it correctly once initialized" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val evcs1 = TestProbe("evcs1")
// this one should be stashed
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
evcs1.expectNoMessage()
scheduler.expectNoMessage()
@@ -116,7 +106,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extEvData(evService)), key)
+ SimonaService.Create(InitExtEvData(extEvData(evService)), key),
)
scheduler.expectMsg(
ScheduleActivation(evService.toTyped, INIT_SIM_TICK, Some(key))
@@ -125,18 +115,14 @@ class ExtEvDataServiceSpec
scheduler.send(evService, Activation(INIT_SIM_TICK))
scheduler.expectMsg(Completion(evService.toTyped))
- evcs1.expectMsg(RegistrationSuccessfulMessage(None))
+ evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, None))
}
}
"An idle ev movements service" must {
- // TODO enhance with tests for cases where no EVCS are applicable and answer is sent right away
+
"handle duplicate registrations correctly" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val key =
ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
@@ -144,7 +130,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extEvData(evService)), key)
+ SimonaService.Create(InitExtEvData(extEvData(evService)), key),
)
scheduler.expectMsgType[ScheduleActivation]
@@ -154,33 +140,20 @@ class ExtEvDataServiceSpec
val evcs1 = TestProbe("evcs1")
val evcs2 = TestProbe("evcs2")
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
- evcs1.expectMsg(RegistrationSuccessfulMessage(None))
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
+ evcs1.expectMsg(RegistrationSuccessfulMessage(evService.ref, None))
- evcs2.send(
- evService,
- RegisterForEvDataMessage(evcs2UUID)
- )
- evcs2.expectMsg(RegistrationSuccessfulMessage(None))
+ evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID))
+ evcs2.expectMsg(RegistrationSuccessfulMessage(evService.ref, None))
// register first one again
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
evcs1.expectNoMessage()
evcs2.expectNoMessage()
}
"fail when activated without having received ExtEvMessage" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val key =
ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
@@ -188,7 +161,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extEvData(evService)), key)
+ SimonaService.Create(InitExtEvData(extEvData(evService)), key),
)
scheduler.expectMsg(
ScheduleActivation(evService.toTyped, INIT_SIM_TICK, Some(key))
@@ -201,7 +174,7 @@ class ExtEvDataServiceSpec
assertThrows[ServiceException] {
evService.receive(
Activation(0),
- scheduler.ref
+ scheduler.ref,
)
}
@@ -209,11 +182,7 @@ class ExtEvDataServiceSpec
}
"handle free lots requests correctly and forward them to the correct evcs" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val extData = extEvData(evService)
@@ -223,7 +192,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extData), key)
+ SimonaService.Create(InitExtEvData(extData), key),
)
scheduler.expectMsgType[ScheduleActivation]
@@ -233,16 +202,10 @@ class ExtEvDataServiceSpec
val evcs1 = TestProbe("evcs1")
val evcs2 = TestProbe("evcs2")
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
evcs1.expectMsgType[RegistrationSuccessfulMessage]
- evcs2.send(
- evService,
- RegisterForEvDataMessage(evcs2UUID)
- )
+ evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID))
evcs2.expectMsgType[RegistrationSuccessfulMessage]
extData.sendExtMsg(
@@ -275,8 +238,8 @@ class ExtEvDataServiceSpec
evService,
FreeLotsResponse(
evcs1UUID,
- 2
- )
+ 2,
+ ),
)
// nothing should happen yet, waiting for second departed ev
@@ -286,16 +249,16 @@ class ExtEvDataServiceSpec
evService,
FreeLotsResponse(
evcs2UUID,
- 0
- )
+ 0,
+ ),
)
// ev service should recognize that all evcs that are expected are returned,
- // thus should send ProvidePublicEvcs
+ // thus should send ProvideEvcsFreeLots
awaitCond(
!extData.receiveTriggerQueue.isEmpty,
max = 3.seconds,
- message = "No message received"
+ message = "No message received",
)
extData.receiveTriggerQueue.size() shouldBe 1
// only evcs 1 should be included, the other one is full
@@ -305,11 +268,7 @@ class ExtEvDataServiceSpec
}
"return free lots requests right away if there are no evcs registered" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val extData = extEvData(evService)
@@ -319,7 +278,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extData), key)
+ SimonaService.Create(InitExtEvData(extData), key),
)
scheduler.expectMsgType[ScheduleActivation]
@@ -339,22 +298,18 @@ class ExtEvDataServiceSpec
scheduler.expectMsg(Completion(evService.toTyped))
- // ev service should send ProvidePublicEvcs right away
+ // ev service should send ProvideEvcsFreeLots right away
awaitCond(
!extData.receiveTriggerQueue.isEmpty,
max = 3.seconds,
- message = "No message received"
+ message = "No message received",
)
extData.receiveTriggerQueue.size() shouldBe 1
extData.receiveTriggerQueue.take() shouldBe new ProvideEvcsFreeLots()
}
"handle ev departure requests correctly and return departed evs" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val extData = extEvData(evService)
@@ -364,7 +319,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extData), key)
+ SimonaService.Create(InitExtEvData(extData), key),
)
scheduler.expectMsgType[ScheduleActivation]
@@ -374,21 +329,15 @@ class ExtEvDataServiceSpec
val evcs1 = TestProbe("evcs1")
val evcs2 = TestProbe("evcs1")
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
evcs1.expectMsgType[RegistrationSuccessfulMessage]
- evcs2.send(
- evService,
- RegisterForEvDataMessage(evcs2UUID)
- )
+ evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID))
evcs2.expectMsgType[RegistrationSuccessfulMessage]
val departures = Map(
evcs1UUID -> List(evA.getUuid).asJava,
- evcs2UUID -> List(evB.getUuid).asJava
+ evcs2UUID -> List(evB.getUuid).asJava,
).asJava
extData.sendExtMsg(
@@ -420,7 +369,7 @@ class ExtEvDataServiceSpec
evcs1.send(
evService,
- DepartingEvsResponse(evcs1UUID, Set(updatedEvA))
+ DepartingEvsResponse(evcs1UUID, Seq(EvModelWrapper(updatedEvA))),
)
// nothing should happen yet, waiting for second departed ev
@@ -432,15 +381,15 @@ class ExtEvDataServiceSpec
evcs2.send(
evService,
- DepartingEvsResponse(evcs2UUID, Set(updatedEvB))
+ DepartingEvsResponse(evcs2UUID, Seq(EvModelWrapper(updatedEvB))),
)
// ev service should recognize that all evs that are expected are returned,
- // thus should send AllDepartedEvsResponse
+ // thus should send ProvideDepartingEvs
awaitCond(
!extData.receiveTriggerQueue.isEmpty,
max = 3.seconds,
- message = "No message received"
+ message = "No message received",
)
extData.receiveTriggerQueue.size() shouldBe 1
extData.receiveTriggerQueue.take() shouldBe new ProvideDepartingEvs(
@@ -448,12 +397,53 @@ class ExtEvDataServiceSpec
)
}
- "handle ev arrivals correctly and forward them to the correct evcs" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
+ "return ev departure requests right away if request list is empty" in {
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
+
+ val extData = extEvData(evService)
+
+ val key =
+ ScheduleLock.singleKey(TSpawner, scheduler.ref.toTyped, INIT_SIM_TICK)
+ scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled
+
+ scheduler.send(
+ evService,
+ SimonaService.Create(InitExtEvData(extData), key),
+ )
+ scheduler.expectMsgType[ScheduleActivation]
+
+ scheduler.send(evService, Activation(INIT_SIM_TICK))
+ scheduler.expectMsg(Completion(evService.toTyped))
+
+ extData.sendExtMsg(
+ new RequestDepartingEvs(Map.empty[UUID, java.util.List[UUID]].asJava)
+ )
+
+ // ev service should receive departure msg at this moment
+ // scheduler should receive schedule msg
+ extSimAdapter.expectMsg(new ScheduleDataServiceMessage(evService))
+
+ val tick = 0L
+
+ // we trigger ev service
+ scheduler.send(evService, Activation(tick))
+
+ scheduler.expectMsg(Completion(evService.toTyped))
+
+ // ev service should send ProvideDepartingEvs right away
+ awaitCond(
+ !extData.receiveTriggerQueue.isEmpty,
+ max = 3.seconds,
+ message = "No message received",
)
+ extData.receiveTriggerQueue.size() shouldBe 1
+ extData.receiveTriggerQueue.take() shouldBe new ProvideDepartingEvs(
+ List.empty[EvModel].asJava
+ )
+ }
+
+ "handle ev arrivals correctly and forward them to the correct evcs" in {
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val extData = extEvData(evService)
@@ -463,7 +453,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extData), key)
+ SimonaService.Create(InitExtEvData(extData), key),
)
scheduler.expectMsgType[ScheduleActivation]
@@ -473,21 +463,15 @@ class ExtEvDataServiceSpec
val evcs1 = TestProbe("evcs1")
val evcs2 = TestProbe("evcs2")
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
evcs1.expectMsgType[RegistrationSuccessfulMessage]
- evcs2.send(
- evService,
- RegisterForEvDataMessage(evcs2UUID)
- )
+ evcs2.send(evService, RegisterForEvDataMessage(evcs2UUID))
evcs2.expectMsgType[RegistrationSuccessfulMessage]
val arrivals = Map(
evcs1UUID -> List[EvModel](evA).asJava,
- evcs2UUID -> List[EvModel](evB).asJava
+ evcs2UUID -> List[EvModel](evB).asJava,
).asJava
extData.sendExtMsg(
@@ -507,12 +491,12 @@ class ExtEvDataServiceSpec
val evsMessage1 = evcs1.expectMsgType[ProvideEvDataMessage]
evsMessage1.tick shouldBe tick
- evsMessage1.data shouldBe ArrivingEvsData(Seq(evA))
+ evsMessage1.data shouldBe ArrivingEvsData(Seq(EvModelWrapper(evA)))
evsMessage1.unlockKey should not be empty
val evsMessage2 = evcs2.expectMsgType[ProvideEvDataMessage]
evsMessage2.tick shouldBe tick
- evsMessage2.data shouldBe ArrivingEvsData(Seq(evB))
+ evsMessage2.data shouldBe ArrivingEvsData(Seq(EvModelWrapper(evB)))
evsMessage2.unlockKey should not be empty
scheduler.expectMsg(Completion(evService.toTyped))
@@ -522,11 +506,7 @@ class ExtEvDataServiceSpec
}
"skip a movements provision from an evcs that is not registered" in {
- val evService = TestActorRef(
- new ExtEvDataService(
- scheduler.ref
- )
- )
+ val evService = TestActorRef(new ExtEvDataService(scheduler.ref))
val extData = extEvData(evService)
@@ -536,7 +516,7 @@ class ExtEvDataServiceSpec
scheduler.send(
evService,
- SimonaService.Create(InitExtEvData(extData), key)
+ SimonaService.Create(InitExtEvData(extData), key),
)
scheduler.expectMsgType[ScheduleActivation]
@@ -545,15 +525,12 @@ class ExtEvDataServiceSpec
val evcs1 = TestProbe("evcs1")
- evcs1.send(
- evService,
- RegisterForEvDataMessage(evcs1UUID)
- )
+ evcs1.send(evService, RegisterForEvDataMessage(evcs1UUID))
evcs1.expectMsgType[RegistrationSuccessfulMessage]
val arrivals = Map(
evcs1UUID -> List[EvModel](evA).asJava,
- evcs2UUID -> List[EvModel](evB).asJava
+ evcs2UUID -> List[EvModel](evB).asJava,
).asJava
extData.sendExtMsg(
@@ -573,7 +550,7 @@ class ExtEvDataServiceSpec
val evsMessage1 = evcs1.expectMsgType[ProvideEvDataMessage]
evsMessage1.tick shouldBe tick
- evsMessage1.data shouldBe ArrivingEvsData(Seq(evA))
+ evsMessage1.data shouldBe ArrivingEvsData(Seq(EvModelWrapper(evA)))
evsMessage1.unlockKey should not be empty
scheduler.expectMsg(Completion(evService.toTyped))
diff --git a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySpec.scala b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySpec.scala
index 25f32e4c46..43a64da1ce 100644
--- a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySpec.scala
@@ -20,34 +20,34 @@ import edu.ie3.datamodel.models.value.{SValue, Value}
import edu.ie3.simona.config.SimonaConfig.PrimaryDataCsvParams
import edu.ie3.simona.config.SimonaConfig.Simona.Input.Primary.{
CouchbaseParams,
- InfluxDb1xParams
+ InfluxDb1xParams,
}
import edu.ie3.simona.config.SimonaConfig.Simona.Input.{
Primary => PrimaryConfig
}
import edu.ie3.simona.exceptions.{
InitializationException,
- InvalidConfigParameterException
+ InvalidConfigParameterException,
}
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationFailedMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.{
PrimaryServiceRegistrationMessage,
- WorkerRegistrationMessage
+ WorkerRegistrationMessage,
}
import edu.ie3.simona.service.SimonaService
import edu.ie3.simona.service.primary.PrimaryServiceProxy.{
InitPrimaryServiceProxyStateData,
PrimaryServiceStateData,
- SourceRef
+ SourceRef,
}
import edu.ie3.simona.service.primary.PrimaryServiceWorker.{
CsvInitPrimaryServiceStateData,
- InitPrimaryServiceStateData
+ InitPrimaryServiceStateData,
}
import edu.ie3.simona.test.common.input.TimeSeriesTestData
import edu.ie3.simona.test.common.{AgentSpec, TestSpawnerClassic}
@@ -71,7 +71,7 @@ class PrimaryServiceProxySpec
.parseString("""
|pekko.loggers = ["org.apache.pekko.testkit.TestEventListener"]
|pekko.loglevel="OFF"
- """.stripMargin)
+ """.stripMargin),
)
)
with TableDrivenPropertyChecks
@@ -97,16 +97,16 @@ class PrimaryServiceProxySpec
csvSep,
baseDirectoryPath.toString,
isHierarchic = false,
- TimeUtil.withDefaults.getDtfPattern
+ TimeUtil.withDefaults.getDtfPattern,
)
),
None,
- None
+ None,
)
val mappingSource = new CsvTimeSeriesMappingSource(
csvSep,
baseDirectoryPath,
- fileNamingStrategy
+ fileNamingStrategy,
)
val workerId: String = "PrimaryService_" + uuidPq
val modelUuid: UUID = UUID.fromString("c7ebcc6c-55fc-479b-aa6b-6fa82ccac6b8")
@@ -116,15 +116,15 @@ class PrimaryServiceProxySpec
Map(
UUID.fromString("b86e95b0-e579-4a80-a534-37c7a470a409") -> uuidP,
modelUuid -> uuidPq,
- UUID.fromString("90a96daa-012b-4fea-82dc-24ba7a7ab81c") -> uuidPq
+ UUID.fromString("90a96daa-012b-4fea-82dc-24ba7a7ab81c") -> uuidPq,
),
Map(
uuidP -> SourceRef(metaP, None),
- uuidPq -> SourceRef(metaPq, None)
+ uuidPq -> SourceRef(metaPq, None),
),
simulationStart,
validPrimaryConfig,
- mappingSource
+ mappingSource,
)
private val scheduler: TestProbe = TestProbe("scheduler")
@@ -135,7 +135,7 @@ class PrimaryServiceProxySpec
Some(CouchbaseParams("", "", "", "", "", "", "")),
Some(PrimaryDataCsvParams("", "", isHierarchic = false, "")),
None,
- None
+ None,
)
val exception = intercept[InvalidConfigParameterException](
@@ -149,7 +149,7 @@ class PrimaryServiceProxySpec
None,
None,
None,
- None
+ None,
)
val exception = intercept[InvalidConfigParameterException](
@@ -163,7 +163,7 @@ class PrimaryServiceProxySpec
Some(CouchbaseParams("", "", "", "", "", "", "")),
None,
None,
- None
+ None,
)
val exception = intercept[InvalidConfigParameterException](
@@ -177,7 +177,7 @@ class PrimaryServiceProxySpec
None,
Some(PrimaryDataCsvParams("", "", isHierarchic = false, "")),
None,
- None
+ None,
)
noException shouldBe thrownBy {
@@ -190,7 +190,7 @@ class PrimaryServiceProxySpec
None,
None,
Some(InfluxDb1xParams("", 0, "", "")),
- None
+ None,
)
val exception = intercept[InvalidConfigParameterException](
@@ -204,7 +204,7 @@ class PrimaryServiceProxySpec
None,
Some(PrimaryDataCsvParams("", "", isHierarchic = false, "xYz")),
None,
- None
+ None,
)
intercept[InvalidConfigParameterException](
@@ -222,11 +222,11 @@ class PrimaryServiceProxySpec
"",
"",
isHierarchic = false,
- "yyyy-MM-dd'T'HH:mm'Z[UTC]'"
+ "yyyy-MM-dd'T'HH:mm'Z[UTC]'",
)
),
None,
- None
+ None,
)
noException shouldBe thrownBy {
@@ -238,7 +238,7 @@ class PrimaryServiceProxySpec
val initStateData: InitPrimaryServiceProxyStateData =
InitPrimaryServiceProxyStateData(
validPrimaryConfig,
- simulationStart
+ simulationStart,
)
val proxyRef: TestActorRef[PrimaryServiceProxy] = TestActorRef(
new PrimaryServiceProxy(scheduler.ref, initStateData, simulationStart)
@@ -254,12 +254,12 @@ class PrimaryServiceProxySpec
None,
None,
None,
- None
+ None,
)
proxy invokePrivate prepareStateData(
maliciousConfig,
- simulationStart
+ simulationStart,
) match {
case Success(_) =>
fail("Building state data with missing config should fail")
@@ -274,12 +274,12 @@ class PrimaryServiceProxySpec
None,
None,
Some(InfluxDb1xParams("", -1, "", "")),
- None
+ None,
)
proxy invokePrivate prepareStateData(
maliciousConfig,
- simulationStart
+ simulationStart,
) match {
case Success(_) =>
fail("Building state data with missing config should fail")
@@ -292,7 +292,7 @@ class PrimaryServiceProxySpec
"result in correct data" in {
proxy invokePrivate prepareStateData(
validPrimaryConfig,
- simulationStart
+ simulationStart,
) match {
case Success(
PrimaryServiceStateData(
@@ -300,13 +300,13 @@ class PrimaryServiceProxySpec
timeSeriesToSourceRef,
simulationStart,
primaryConfig,
- mappingSource
+ mappingSource,
)
) =>
modelToTimeSeries shouldBe Map(
UUID.fromString("b86e95b0-e579-4a80-a534-37c7a470a409") -> uuidP,
UUID.fromString("c7ebcc6c-55fc-479b-aa6b-6fa82ccac6b8") -> uuidPq,
- UUID.fromString("90a96daa-012b-4fea-82dc-24ba7a7ab81c") -> uuidPq
+ UUID.fromString("90a96daa-012b-4fea-82dc-24ba7a7ab81c") -> uuidPq,
)
timeSeriesToSourceRef.get(uuidP) match {
case Some(SourceRef(metaInformation, worker)) =>
@@ -334,7 +334,7 @@ class PrimaryServiceProxySpec
case Failure(failure) =>
fail(
"Building state data with correct config should not fail, but failed with:",
- failure
+ failure,
)
}
}
@@ -363,7 +363,7 @@ class PrimaryServiceProxySpec
val workerRef = proxy invokePrivate classToWorkerRef(
testClass,
- workerId
+ workerId,
)
Objects.nonNull(workerRef) shouldBe true
@@ -377,13 +377,13 @@ class PrimaryServiceProxySpec
)
val metaInformation = new CsvIndividualTimeSeriesMetaInformation(
metaPq,
- Paths.get("its_pq_" + uuidPq)
+ Paths.get("its_pq_" + uuidPq),
)
proxy invokePrivate toInitData(
metaInformation,
simulationStart,
- validPrimaryConfig
+ validPrimaryConfig,
) match {
case Success(
CsvInitPrimaryServiceStateData(
@@ -393,7 +393,7 @@ class PrimaryServiceProxySpec
directoryPath,
filePath,
fileNamingStrategy,
- timePattern
+ timePattern,
)
) =>
actualTimeSeriesUuid shouldBe uuidPq
@@ -410,7 +410,7 @@ class PrimaryServiceProxySpec
case Failure(exception) =>
fail(
"Creation of init data failed, although it was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -420,12 +420,12 @@ class PrimaryServiceProxySpec
Some(CouchbaseParams("", "", "", "", "", "", "")),
None,
None,
- None
+ None,
)
proxy invokePrivate initializeWorker(
metaPq,
simulationStart,
- maliciousPrimaryConfig
+ maliciousPrimaryConfig,
) match {
case Failure(exception) =>
/* Check the exception */
@@ -459,11 +459,11 @@ class PrimaryServiceProxySpec
new PrimaryServiceProxy(
scheduler.ref,
initStateData,
- simulationStart
+ simulationStart,
) {
override protected def classToWorkerRef[V <: Value](
valueClass: Class[V],
- timeSeriesUuid: String
+ timeSeriesUuid: String,
): ActorRef = worker.ref
// needs to be overwritten as to make it available to the private method tester
@@ -471,19 +471,19 @@ class PrimaryServiceProxySpec
override protected def initializeWorker(
metaInformation: IndividualTimeSeriesMetaInformation,
simulationStart: ZonedDateTime,
- primaryConfig: PrimaryConfig
+ primaryConfig: PrimaryConfig,
): Try[ActorRef] =
super.initializeWorker(
metaInformation,
simulationStart,
- primaryConfig
+ primaryConfig,
)
}
)
val fakeProxy: PrimaryServiceProxy = fakeProxyRef.underlyingActor
val metaInformation = new CsvIndividualTimeSeriesMetaInformation(
metaPq,
- Paths.get("its_pq_" + uuidPq)
+ Paths.get("its_pq_" + uuidPq),
)
scheduler.expectNoMessage()
@@ -491,7 +491,7 @@ class PrimaryServiceProxySpec
fakeProxy invokePrivate initializeWorker(
metaInformation,
simulationStart,
- validPrimaryConfig
+ validPrimaryConfig,
) match {
case Success(workerRef) =>
/* Check, if expected init message has been sent */
@@ -506,9 +506,9 @@ class PrimaryServiceProxySpec
directoryPath,
filePath,
fileNamingStrategy,
- timePattern
+ timePattern,
),
- _
+ _,
) =>
actualTimeSeriesUuid shouldBe uuidPq
actualSimulationStart shouldBe simulationStart
@@ -530,7 +530,7 @@ class PrimaryServiceProxySpec
case Failure(exception) =>
fail(
"Spinning off a worker with correct input data should be successful, but failed with:",
- exception
+ exception,
)
}
}
@@ -544,7 +544,7 @@ class PrimaryServiceProxySpec
proxy invokePrivate updateStateData(
proxyStateData,
UUID.fromString("394fd072-832c-4c36-869b-c574ee37afe1"),
- self
+ self,
)
}
exception.getMessage shouldBe "Cannot update entry for time series '394fd072-832c-4c36-869b-c574ee37afe1', as it hasn't been part of it before."
@@ -554,19 +554,19 @@ class PrimaryServiceProxySpec
proxy invokePrivate updateStateData(
proxyStateData,
uuidPq,
- self
+ self,
) match {
case PrimaryServiceStateData(
modelToTimeSeries,
timeSeriesToSourceRef,
simulationStart,
primaryConfig,
- mappingSource
+ mappingSource,
) =>
modelToTimeSeries shouldBe proxyStateData.modelToTimeSeries
timeSeriesToSourceRef shouldBe Map(
uuidP -> SourceRef(metaP, None),
- uuidPq -> SourceRef(metaPq, Some(self))
+ uuidPq -> SourceRef(metaPq, Some(self)),
)
simulationStart shouldBe proxyStateData.simulationStart
primaryConfig shouldBe proxyStateData.primaryConfig
@@ -586,9 +586,9 @@ class PrimaryServiceProxySpec
modelUuid,
uuidPq,
maliciousStateData,
- self
+ self,
)
- expectMsg(RegistrationFailedMessage)
+ expectMsg(RegistrationFailedMessage(proxyRef))
}
"forward the registration request, if worker is already known" in {
@@ -602,7 +602,7 @@ class PrimaryServiceProxySpec
modelUuid,
uuidPq,
adaptedStateData,
- self
+ self,
)
expectMsg(WorkerRegistrationMessage(self))
}
@@ -613,7 +613,7 @@ class PrimaryServiceProxySpec
Some(CouchbaseParams("", "", "", "", "", "", "")),
None,
None,
- None
+ None,
)
)
@@ -621,9 +621,9 @@ class PrimaryServiceProxySpec
modelUuid,
uuidPq,
maliciousStateData,
- self
+ self,
)
- expectMsg(RegistrationFailedMessage)
+ expectMsg(RegistrationFailedMessage(proxyRef))
}
"spin off a worker, if needed and forward the registration request" in {
@@ -634,12 +634,12 @@ class PrimaryServiceProxySpec
new PrimaryServiceProxy(
scheduler.ref,
initStateData,
- simulationStart
+ simulationStart,
) {
override protected def initializeWorker(
metaInformation: IndividualTimeSeriesMetaInformation,
simulationStart: ZonedDateTime,
- primaryConfig: PrimaryConfig
+ primaryConfig: PrimaryConfig,
): Try[ActorRef] = Success(worker.ref)
// needs to be overwritten as to make it available to the private method tester
@@ -648,13 +648,13 @@ class PrimaryServiceProxySpec
modelUuid: UUID,
timeSeriesUuid: UUID,
stateData: PrimaryServiceStateData,
- requestingActor: ActorRef
+ requestingActor: ActorRef,
): Unit =
super.handleCoveredModel(
modelUuid,
timeSeriesUuid,
stateData,
- requestingActor
+ requestingActor,
)
}
)
@@ -664,7 +664,7 @@ class PrimaryServiceProxySpec
modelUuid,
uuidPq,
proxyStateData,
- self
+ self,
)
worker.expectMsg(WorkerRegistrationMessage(self))
}
@@ -677,7 +677,7 @@ class PrimaryServiceProxySpec
)
proxyRef ! request
- expectMsg(RegistrationFailedMessage)
+ expectMsg(RegistrationFailedMessage(proxyRef))
}
"succeed, if model is handled" in {
@@ -688,12 +688,12 @@ class PrimaryServiceProxySpec
new PrimaryServiceProxy(
scheduler.ref,
initStateData,
- simulationStart
+ simulationStart,
) {
override protected def initializeWorker(
metaInformation: IndividualTimeSeriesMetaInformation,
simulationStart: ZonedDateTime,
- primaryConfig: PrimaryConfig
+ primaryConfig: PrimaryConfig,
): Try[ActorRef] = Success(worker.ref)
}
)
@@ -701,7 +701,7 @@ class PrimaryServiceProxySpec
/* Initialize the fake proxy */
scheduler.send(
fakeProxyRef,
- Activation(INIT_SIM_TICK)
+ Activation(INIT_SIM_TICK),
)
scheduler.expectMsg(Completion(fakeProxyRef.toTyped))
diff --git a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySqlIT.scala b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySqlIT.scala
index 99418e035f..b451036391 100644
--- a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySqlIT.scala
+++ b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceProxySqlIT.scala
@@ -9,7 +9,7 @@ package edu.ie3.simona.service.primary
import org.apache.pekko.actor.ActorSystem
import org.apache.pekko.actor.typed.scaladsl.adapter.{
ClassicActorRefOps,
- TypedActorRefOps
+ TypedActorRefOps,
}
import org.apache.pekko.testkit.{TestActorRef, TestProbe}
import com.dimafeng.testcontainers.{ForAllTestContainer, PostgreSQLContainer}
@@ -19,12 +19,12 @@ import edu.ie3.simona.config.SimonaConfig.Simona.Input.Primary.SqlParams
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.PrimaryServiceRegistrationMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.service.primary.PrimaryServiceProxy.InitPrimaryServiceProxyStateData
import edu.ie3.simona.test.common.{AgentSpec, TestSpawnerClassic}
@@ -32,6 +32,7 @@ import edu.ie3.simona.test.helper.TestContainerHelper
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.TimeUtil
import org.scalatest.BeforeAndAfterAll
+import org.testcontainers.utility.DockerImageName
import java.util.UUID
@@ -42,7 +43,7 @@ class PrimaryServiceProxySqlIT
ConfigFactory
.parseString("""
|pekko.loglevel="OFF"
- """.stripMargin)
+ """.stripMargin),
)
)
with ForAllTestContainer
@@ -51,7 +52,7 @@ class PrimaryServiceProxySqlIT
with TestSpawnerClassic {
override val container: PostgreSQLContainer = PostgreSQLContainer(
- "postgres:14.2"
+ DockerImageName.parse("postgres:14.2")
)
private val simulationStart =
@@ -67,7 +68,7 @@ class PrimaryServiceProxySqlIT
Iterable(
"time_series_p.sql",
"time_series_pqh.sql",
- "time_series_mapping.sql"
+ "time_series_mapping.sql",
).foreach { file =>
val res = container.execInContainer("psql", "-Utest", "-f/home/" + file)
res.getStderr shouldBe empty
@@ -87,7 +88,7 @@ class PrimaryServiceProxySqlIT
userName = container.username,
password = container.password,
schemaName = schemaName,
- timePattern = "yyyy-MM-dd HH:mm:ss"
+ timePattern = "yyyy-MM-dd HH:mm:ss",
)
private def createProxy(): TestActorRef[PrimaryServiceProxy] = {
@@ -96,16 +97,16 @@ class PrimaryServiceProxySqlIT
None,
None,
None,
- sqlParams = Some(sqlParams)
+ sqlParams = Some(sqlParams),
),
- simulationStart
+ simulationStart,
)
TestActorRef(
PrimaryServiceProxy.props(
scheduler.ref,
initData,
- simulationStart
+ simulationStart,
)
)
}
@@ -131,7 +132,7 @@ class PrimaryServiceProxySqlIT
proxyRef,
PrimaryServiceRegistrationMessage(
UUID.fromString("b86e95b0-e579-4a80-a534-37c7a470a409")
- )
+ ),
)
scheduler.expectMsgType[ScheduleActivation] // lock activation scheduled
@@ -143,12 +144,14 @@ class PrimaryServiceProxySqlIT
val workerRef = initActivation.actor
scheduler.send(
workerRef.toClassic,
- Activation(INIT_SIM_TICK)
+ Activation(INIT_SIM_TICK),
)
scheduler.expectMsg(Completion(workerRef, Some(0)))
- systemParticipantProbe.expectMsg(RegistrationSuccessfulMessage(Some(0L)))
+ systemParticipantProbe.expectMsg(
+ RegistrationSuccessfulMessage(workerRef.toClassic, Some(0L))
+ )
}
"handle participant request correctly if participant does not have primary data" in {
@@ -163,12 +166,12 @@ class PrimaryServiceProxySqlIT
proxyRef,
PrimaryServiceRegistrationMessage(
UUID.fromString("db958617-e49d-44d3-b546-5f7b62776afd")
- )
+ ),
)
scheduler.expectNoMessage()
- systemParticipantProbe.expectMsg(RegistrationFailedMessage)
+ systemParticipantProbe.expectMsg(RegistrationFailedMessage(proxyRef))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala
index 6f1aa171a6..b54fa8a41f 100644
--- a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSpec.scala
@@ -19,7 +19,7 @@ import edu.ie3.simona.agent.participant.data.Data.PrimaryData.ActivePower
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.WorkerRegistrationMessage
@@ -30,7 +30,7 @@ import edu.ie3.simona.service.primary.PrimaryServiceWorker.{
CsvInitPrimaryServiceStateData,
InitPrimaryServiceStateData,
PrimaryServiceInitializedStateData,
- ProvidePrimaryDataMessage
+ ProvidePrimaryDataMessage,
}
import edu.ie3.simona.service.primary.PrimaryServiceWorkerSpec.WrongInitPrimaryServiceStateData
import edu.ie3.simona.test.common.{AgentSpec, TestSpawnerClassic}
@@ -54,7 +54,7 @@ class PrimaryServiceWorkerSpec
ConfigFactory
.parseString("""
|pekko.loglevel="OFF"
- """.stripMargin)
+ """.stripMargin),
)
)
with TimeSeriesTestData
@@ -78,7 +78,7 @@ class PrimaryServiceWorkerSpec
fileNamingStrategy = new FileNamingStrategy(),
simulationStart =
TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00"),
- timePattern = "yyyy-MM-dd'T'HH:mm:ss'Z'"
+ timePattern = "yyyy-MM-dd'T'HH:mm:ss'Z'",
)
private implicit val powerTolerance: squants.Power = Watts(0.1)
@@ -90,7 +90,7 @@ class PrimaryServiceWorkerSpec
TestActorRef(
new PrimaryServiceWorker[PValue](
scheduler.ref,
- classOf[PValue]
+ classOf[PValue],
)
)
val service = serviceRef.underlyingActor
@@ -114,7 +114,7 @@ class PrimaryServiceWorkerSpec
directoryPath = baseDirectoryPath,
filePath = Paths.get("its_pq_" + uuidPq),
fileNamingStrategy = new FileNamingStrategy(),
- timePattern = TimeUtil.withDefaults.getDtfPattern
+ timePattern = TimeUtil.withDefaults.getDtfPattern,
)
service.init(maliciousInitData) match {
case Failure(exception) =>
@@ -135,12 +135,12 @@ class PrimaryServiceWorkerSpec
activationTicks,
simulationStart,
source,
- subscribers
+ subscribers,
) =>
nextActivationTick shouldBe Some(0L)
activationTicks.toVector shouldBe Vector(
900L,
- 1800L
+ 1800L,
) // The first tick should already been popped
simulationStart shouldBe validInitData.simulationStart
source.getClass shouldBe classOf[CsvTimeSeriesSource[PValue]]
@@ -160,7 +160,7 @@ class PrimaryServiceWorkerSpec
scheduler.send(
serviceRef,
- SimonaService.Create(validInitData, key)
+ SimonaService.Create(validInitData, key),
)
scheduler.expectMsg(
ScheduleActivation(serviceRef.toTyped, INIT_SIM_TICK, Some(key))
@@ -180,7 +180,9 @@ class PrimaryServiceWorkerSpec
serviceRef ! WorkerRegistrationMessage(systemParticipant.ref)
/* Wait for request approval */
- systemParticipant.expectMsg(RegistrationSuccessfulMessage(Some(0L)))
+ systemParticipant.expectMsg(
+ RegistrationSuccessfulMessage(serviceRef, Some(0L))
+ )
/* We cannot directly check, if the requesting actor is among the subscribers, therefore we ask the actor to
* provide data to all subscribed actors and check, if the subscribed probe gets one */
@@ -202,16 +204,16 @@ class PrimaryServiceWorkerSpec
uuidP,
Paths.get("its_p_" + uuidP),
classOf[PValue],
- new TimeBasedSimpleValueFactory[PValue](classOf[PValue])
+ new TimeBasedSimpleValueFactory[PValue](classOf[PValue]),
),
- Vector(self)
+ Vector(self),
)
"correctly distribute proper primary data" in {
val announcePrimaryData = PrivateMethod[
(
PrimaryServiceInitializedStateData[PValue],
- Option[Long]
+ Option[Long],
)
](Symbol("announcePrimaryData"))
val tick = 0L
@@ -221,7 +223,7 @@ class PrimaryServiceWorkerSpec
service invokePrivate announcePrimaryData(
tick,
primaryData,
- serviceStateData
+ serviceStateData,
) match {
case (updatedStateData, maybeNextTick) =>
/* Check updated state data */
@@ -231,7 +233,7 @@ class PrimaryServiceWorkerSpec
activationTicks,
_,
_,
- _
+ _,
) =>
nextActivationTick shouldBe Some(900L)
activationTicks.size shouldBe 0
@@ -243,11 +245,13 @@ class PrimaryServiceWorkerSpec
expectMsgClass(classOf[ProvidePrimaryDataMessage]) match {
case ProvidePrimaryDataMessage(
actualTick,
+ actualServiceRef,
actualData,
actualNextDataTick,
- unlockKey
+ unlockKey,
) =>
actualTick shouldBe 0L
+ actualServiceRef shouldBe serviceRef
actualData shouldBe primaryData
actualNextDataTick shouldBe Some(900L)
unlockKey shouldBe None
@@ -257,7 +261,7 @@ class PrimaryServiceWorkerSpec
val processDataAndAnnounce = PrivateMethod[
(
PrimaryServiceInitializedStateData[PValue],
- Option[Long]
+ Option[Long],
)
](Symbol("processDataAndAnnounce"))
@@ -273,7 +277,7 @@ class PrimaryServiceWorkerSpec
service invokePrivate processDataAndAnnounce(
tick,
maliciousValue,
- stateData
+ stateData,
) match {
case (
PrimaryServiceInitializedStateData(
@@ -281,9 +285,9 @@ class PrimaryServiceWorkerSpec
_,
_,
_,
- _
+ _,
),
- maybeNextTick
+ maybeNextTick,
) =>
nextActivationTick shouldBe Some(900L)
maybeNextTick shouldBe Some(900L)
@@ -302,7 +306,7 @@ class PrimaryServiceWorkerSpec
service invokePrivate processDataAndAnnounce(
tick,
value,
- serviceStateData
+ serviceStateData,
) match {
case (updatedStateData, _) =>
inside(updatedStateData) {
@@ -311,7 +315,7 @@ class PrimaryServiceWorkerSpec
activationTicks,
_,
_,
- _
+ _,
) =>
nextActivationTick shouldBe Some(900L)
activationTicks.size shouldBe 0
@@ -322,8 +326,9 @@ class PrimaryServiceWorkerSpec
expectMsg(
ProvidePrimaryDataMessage(
tick,
+ serviceRef,
ActivePower(Kilowatts(50.0)),
- Some(900L)
+ Some(900L),
)
)
}
@@ -342,15 +347,23 @@ class PrimaryServiceWorkerSpec
inside(
systemParticipant.expectMsgClass(classOf[ProvidePrimaryDataMessage])
- ) { case ProvidePrimaryDataMessage(tick, data, nextDataTick, unlockKey) =>
- tick shouldBe 900L
- inside(data) {
- case ActivePower(p) =>
- (p ~= Kilowatts(1250.0)) shouldBe true
- case _ => fail("Expected to get active power only.")
- }
- nextDataTick shouldBe None
- unlockKey shouldBe None
+ ) {
+ case ProvidePrimaryDataMessage(
+ tick,
+ actualServiceRef,
+ data,
+ nextDataTick,
+ unlockKey,
+ ) =>
+ tick shouldBe 900L
+ actualServiceRef shouldBe serviceRef
+ inside(data) {
+ case ActivePower(p) =>
+ p should approximate(Kilowatts(1250.0))
+ case _ => fail("Expected to get active power only.")
+ }
+ nextDataTick shouldBe None
+ unlockKey shouldBe None
}
}
}
@@ -359,14 +372,14 @@ class PrimaryServiceWorkerSpec
object PrimaryServiceWorkerSpec {
final case class WrongInitPrimaryServiceStateData(
override val simulationStart: ZonedDateTime,
- override val timeSeriesUuid: UUID
+ override val timeSeriesUuid: UUID,
) extends InitPrimaryServiceStateData
object WrongInitPrimaryServiceStateData {
def apply(): WrongInitPrimaryServiceStateData =
new WrongInitPrimaryServiceStateData(
ZonedDateTime.now(),
- UUID.randomUUID()
+ UUID.randomUUID(),
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSqlIT.scala b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSqlIT.scala
index 8d0c4a9299..f85def148c 100644
--- a/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSqlIT.scala
+++ b/src/test/scala/edu/ie3/simona/service/primary/PrimaryServiceWorkerSqlIT.scala
@@ -15,13 +15,13 @@ import edu.ie3.datamodel.io.naming.DatabaseNamingStrategy
import edu.ie3.datamodel.models.value.{HeatAndSValue, PValue}
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ActivePower,
- ApparentPowerAndHeat
+ ApparentPowerAndHeat,
}
import edu.ie3.simona.config.SimonaConfig.Simona.Input.Primary.SqlParams
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.WorkerRegistrationMessage
@@ -29,7 +29,7 @@ import edu.ie3.simona.scheduler.ScheduleLock.ScheduleKey
import edu.ie3.simona.service.SimonaService
import edu.ie3.simona.service.primary.PrimaryServiceWorker.{
ProvidePrimaryDataMessage,
- SqlInitPrimaryServiceStateData
+ SqlInitPrimaryServiceStateData,
}
import edu.ie3.simona.test.common.input.TimeSeriesTestData
import edu.ie3.simona.test.common.{AgentSpec, TestSpawnerClassic}
@@ -39,6 +39,7 @@ import edu.ie3.util.TimeUtil
import edu.ie3.util.scala.quantities.Kilovars
import org.scalatest.BeforeAndAfterAll
import org.scalatest.prop.TableDrivenPropertyChecks
+import org.testcontainers.utility.DockerImageName
import squants.energy.Kilowatts
import java.util.UUID
@@ -50,7 +51,7 @@ class PrimaryServiceWorkerSqlIT
ConfigFactory
.parseString("""
|pekko.loglevel="OFF"
- """.stripMargin)
+ """.stripMargin),
)
)
with ForAllTestContainer
@@ -61,7 +62,7 @@ class PrimaryServiceWorkerSqlIT
with TestSpawnerClassic {
override val container: PostgreSQLContainer = PostgreSQLContainer(
- "postgres:14.2"
+ DockerImageName.parse("postgres:14.2")
)
private val simulationStart =
@@ -97,34 +98,34 @@ class PrimaryServiceWorkerSqlIT
"uuid",
"firstTick",
"firstData",
- "maybeNextTick"
+ "maybeNextTick",
),
(
PrimaryServiceWorker.props(
scheduler.ref,
- classOf[HeatAndSValue]
+ classOf[HeatAndSValue],
),
uuidPqh,
0L,
ApparentPowerAndHeat(
Kilowatts(1000.0),
Kilovars(329.0),
- Kilowatts(8000.0)
+ Kilowatts(8000.0),
),
- Some(900L)
+ Some(900L),
),
(
PrimaryServiceWorker.props(
scheduler.ref,
- classOf[PValue]
+ classOf[PValue],
),
uuidP,
0L,
ActivePower(
Kilowatts(1000.0)
),
- Some(900L)
- )
+ Some(900L),
+ ),
)
forAll(cases) {
@@ -133,7 +134,7 @@ class PrimaryServiceWorkerSqlIT
uuid,
firstTick,
firstData,
- maybeNextTick
+ maybeNextTick,
) =>
val serviceRef = TestActorRef(service)
@@ -145,15 +146,15 @@ class PrimaryServiceWorkerSqlIT
userName = container.username,
password = container.password,
schemaName = schemaName,
- timePattern = "yyyy-MM-dd HH:mm:ss"
+ timePattern = "yyyy-MM-dd HH:mm:ss",
),
- new DatabaseNamingStrategy()
+ new DatabaseNamingStrategy(),
)
val key1 = ScheduleKey(lock.ref.toTyped, UUID.randomUUID())
scheduler.send(
serviceRef,
- SimonaService.Create(initData, key1)
+ SimonaService.Create(initData, key1),
)
scheduler.expectMsg(
ScheduleActivation(serviceRef.toTyped, INIT_SIM_TICK, Some(key1))
@@ -166,9 +167,11 @@ class PrimaryServiceWorkerSqlIT
participant.send(
serviceRef,
- WorkerRegistrationMessage(participant.ref)
+ WorkerRegistrationMessage(participant.ref),
+ )
+ participant.expectMsg(
+ RegistrationSuccessfulMessage(serviceRef, Some(firstTick))
)
- participant.expectMsg(RegistrationSuccessfulMessage(Some(firstTick)))
scheduler.send(serviceRef, Activation(firstTick))
scheduler.expectMsg(Completion(serviceRef.toTyped, maybeNextTick))
diff --git a/src/test/scala/edu/ie3/simona/service/weather/SampleWeatherSourceSpec.scala b/src/test/scala/edu/ie3/simona/service/weather/SampleWeatherSourceSpec.scala
index e94aaf6a1c..0d61932603 100644
--- a/src/test/scala/edu/ie3/simona/service/weather/SampleWeatherSourceSpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/weather/SampleWeatherSourceSpec.scala
@@ -10,7 +10,7 @@ import edu.ie3.datamodel.models.input.NodeInput
import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
import edu.ie3.simona.service.weather.WeatherSource.{
AgentCoordinates,
- WeightedCoordinates
+ WeightedCoordinates,
}
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.util.TickUtil._
@@ -40,12 +40,12 @@ class SampleWeatherSourceSpec
"always return the queried coordinate itself as nearest coordinate" in {
val queryCoordinate = AgentCoordinates(
NodeInput.DEFAULT_GEO_POSITION.getY,
- NodeInput.DEFAULT_GEO_POSITION.getX
+ NodeInput.DEFAULT_GEO_POSITION.getX,
)
source.getWeightedCoordinates(
queryCoordinate,
- 4
+ 4,
) match {
case Success(WeightedCoordinates(weighting)) =>
weighting.corresponds(
@@ -58,7 +58,7 @@ class SampleWeatherSourceSpec
case Failure(exception) =>
fail(
"Querying the nearest coordinates was supposed to pass.",
- exception
+ exception,
)
}
}
@@ -69,7 +69,7 @@ class SampleWeatherSourceSpec
(0L, 86400L, (0L to 86400L by 3600L).toArray),
(1L, 86400L, (3600L to 86400L by 3600L).toArray),
(0L, 86399L, (0L to 82800L by 3600L).toArray),
- (1L, 86399L, (3600L to 82800L by 3600L).toArray)
+ (1L, 86399L, (3600L to 82800L by 3600L).toArray),
)
testData.forEvery {
@@ -92,13 +92,10 @@ class SampleWeatherSourceSpec
actual.windVel.unit shouldBe MetersPerSecond
/* Values meet expectations */
- (actual.diffIrr ~= WattsPerSquareMeter(72.7656)) shouldBe true
-
- (actual.dirIrr ~= WattsPerSquareMeter(80.1172)) shouldBe true
-
- (actual.windVel ~= MetersPerSecond(11.11602)) shouldBe true
-
- (actual.temp ~= Celsius(6.459)) shouldBe true
+ actual.diffIrr should approximate(WattsPerSquareMeter(72.7656))
+ actual.dirIrr should approximate(WattsPerSquareMeter(80.1172))
+ actual.windVel should approximate(MetersPerSecond(11.11602))
+ actual.temp should approximate(Celsius(6.459))
}
@@ -109,17 +106,16 @@ class SampleWeatherSourceSpec
source.getWeather(tick, weightedCoordinates) match {
case WeatherData(diffIrr, dirIrr, temp, windVel) =>
diffIrr.unit shouldBe WattsPerSquareMeter
- (diffIrr ~= WattsPerSquareMeter(72.7656)) shouldBe true
+ diffIrr should approximate(WattsPerSquareMeter(72.7656))
dirIrr.unit shouldBe WattsPerSquareMeter
- (dirIrr ~= WattsPerSquareMeter(80.1172)) shouldBe true
+ dirIrr should approximate(WattsPerSquareMeter(80.1172))
temp.unit shouldBe Celsius
- (temp ~= Celsius(6.459d)) shouldBe true
+ temp should approximate(Celsius(6.459d))
windVel.unit shouldBe MetersPerSecond
- (windVel ~= MetersPerSecond(11.11602d)) shouldBe true
-
+ windVel should approximate(MetersPerSecond(11.11602d))
}
}
}
diff --git a/src/test/scala/edu/ie3/simona/service/weather/WeatherServiceSpec.scala b/src/test/scala/edu/ie3/simona/service/weather/WeatherServiceSpec.scala
index 8830f6c2ec..ea38f702cf 100644
--- a/src/test/scala/edu/ie3/simona/service/weather/WeatherServiceSpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/weather/WeatherServiceSpec.scala
@@ -6,25 +6,17 @@
package edu.ie3.simona.service.weather
-import org.apache.pekko.actor.ActorSystem
-import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
-import org.apache.pekko.testkit.{
- EventFilter,
- ImplicitSender,
- TestActorRef,
- TestProbe
-}
import com.typesafe.config.ConfigFactory
import com.typesafe.scalalogging.LazyLogging
import edu.ie3.simona.config.SimonaConfig
import edu.ie3.simona.ontology.messages.Activation
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
Completion,
- ScheduleActivation
+ ScheduleActivation,
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.{
RegistrationFailedMessage,
- RegistrationSuccessfulMessage
+ RegistrationSuccessfulMessage,
}
import edu.ie3.simona.ontology.messages.services.WeatherMessage._
import edu.ie3.simona.scheduler.ScheduleLock
@@ -34,11 +26,19 @@ import edu.ie3.simona.service.weather.WeatherSource.AgentCoordinates
import edu.ie3.simona.test.common.{
ConfigTestData,
TestKitWithShutdown,
- TestSpawnerClassic
+ TestSpawnerClassic,
}
import edu.ie3.simona.util.SimonaConstants.INIT_SIM_TICK
import edu.ie3.util.TimeUtil
import edu.ie3.util.scala.quantities.WattsPerSquareMeter
+import org.apache.pekko.actor.ActorSystem
+import org.apache.pekko.actor.typed.scaladsl.adapter.ClassicActorRefOps
+import org.apache.pekko.testkit.{
+ EventFilter,
+ ImplicitSender,
+ TestActorRef,
+ TestProbe,
+}
import org.scalatest.PrivateMethodTester
import org.scalatest.wordspec.AnyWordSpecLike
import squants.motion.MetersPerSecond
@@ -52,7 +52,7 @@ class WeatherServiceSpec
.parseString("""
|pekko.loggers = ["org.apache.pekko.testkit.TestEventListener"]
|pekko.loglevel = "INFO"
- """.stripMargin)
+ """.stripMargin),
)
)
with ImplicitSender
@@ -105,7 +105,7 @@ class WeatherServiceSpec
TimeUtil.withDefaults.toZonedDateTime(
simonaConfig.simona.time.endDateTime
),
- 4
+ 4,
)
)
@@ -121,8 +121,8 @@ class WeatherServiceSpec
InitWeatherServiceStateData(
simonaConfig.simona.input.weather.datasource
),
- key
- )
+ key,
+ ),
)
scheduler.expectMsg(
ScheduleActivation(weatherActor.toTyped, INIT_SIM_TICK, Some(key))
@@ -137,26 +137,26 @@ class WeatherServiceSpec
.error(
pattern =
"\\[.*] Unable to obtain necessary information to register for coordinate AgentCoordinates\\(180\\.5,90\\.5\\)\\.",
- occurrences = 1
+ occurrences = 1,
)
.intercept {
weatherActor ! RegisterForWeatherMessage(
invalidCoordinate.latitude,
- invalidCoordinate.longitude
+ invalidCoordinate.longitude,
)
}
- expectMsg(RegistrationFailedMessage)
+ expectMsg(RegistrationFailedMessage(weatherActor))
}
"announce, that a valid coordinate is registered" in {
/* The successful registration stems from the test above */
weatherActor ! RegisterForWeatherMessage(
validCoordinate.latitude,
- validCoordinate.longitude
+ validCoordinate.longitude,
)
- expectMsg(RegistrationSuccessfulMessage(Some(0L)))
+ expectMsg(RegistrationSuccessfulMessage(weatherActor.ref, Some(0L)))
}
"recognize, that a valid coordinate yet is registered" in {
@@ -164,12 +164,12 @@ class WeatherServiceSpec
EventFilter
.warning(
pattern = "Sending actor Actor\\[.*] is already registered",
- occurrences = 1
+ occurrences = 1,
)
.intercept {
weatherActor ! RegisterForWeatherMessage(
validCoordinate.latitude,
- validCoordinate.longitude
+ validCoordinate.longitude,
)
}
expectNoMessage()
@@ -184,13 +184,14 @@ class WeatherServiceSpec
expectMsg(
ProvideWeatherMessage(
0,
+ weatherActor,
WeatherData(
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(-2.3719999999999573),
- MetersPerSecond(4.16474)
+ MetersPerSecond(4.16474),
),
- Some(3600L)
+ Some(3600L),
)
)
@@ -205,13 +206,14 @@ class WeatherServiceSpec
expectMsg(
ProvideWeatherMessage(
3600,
+ weatherActor,
WeatherData(
WattsPerSquareMeter(0d),
WattsPerSquareMeter(0d),
Celsius(-2.5259999999999536),
- MetersPerSecond(4.918092)
+ MetersPerSecond(4.918092),
),
- None
+ None,
)
)
}
diff --git a/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceSpec.scala b/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceSpec.scala
index 063e46d1f7..5490e26ea1 100644
--- a/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceSpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceSpec.scala
@@ -9,17 +9,17 @@ package edu.ie3.simona.service.weather
import edu.ie3.datamodel.io.factory.timeseries.{
CosmoIdCoordinateFactory,
IconIdCoordinateFactory,
- IdCoordinateFactory
+ IdCoordinateFactory,
}
import edu.ie3.datamodel.io.source.IdCoordinateSource
import edu.ie3.simona.exceptions.{
InvalidConfigParameterException,
- ServiceException
+ ServiceException,
}
import edu.ie3.simona.ontology.messages.services.WeatherMessage
import edu.ie3.simona.service.weather.WeatherSource.{
AgentCoordinates,
- WeightedCoordinates
+ WeightedCoordinates,
}
import edu.ie3.simona.service.weather.WeatherSourceSpec._
import edu.ie3.simona.test.common.UnitSpec
@@ -44,7 +44,7 @@ class WeatherSourceSpec extends UnitSpec {
"issue a ServiceException, if there are not enough coordinates available" in {
DummyWeatherSource.getNearestCoordinatesWithDistances(
AgentCoordinates(coordinate0.getY, coordinate0.getX),
- 9
+ 9,
) match {
case Failure(exception: ServiceException) =>
exception.getMessage shouldBe "There are not enough coordinates for averaging. Found 8 within the given distance of 400000 m but need 9. Please make sure that there are enough coordinates within the given distance."
@@ -54,7 +54,7 @@ class WeatherSourceSpec extends UnitSpec {
"issue a ServiceException, if there are not enough coordinates in max distance available" in {
DummyWeatherSource.getNearestCoordinatesWithDistances(
AgentCoordinates(coordinate0.getY, coordinate0.getX),
- 9
+ 9,
) match {
case Failure(exception: ServiceException) =>
exception.getMessage shouldBe "There are not enough coordinates for averaging. Found 8 within the given distance of 400000 m but need 9. Please make sure that there are enough coordinates within the given distance."
@@ -66,7 +66,7 @@ class WeatherSourceSpec extends UnitSpec {
val agentCoordinates = AgentCoordinates(51.3, 7.3)
DummyWeatherSource.getNearestCoordinatesWithDistances(
agentCoordinates,
- 4
+ 4,
) match {
case Failure(exception: ServiceException) =>
exception.getMessage shouldBe "The queried point shall be surrounded by 4 weather coordinates, which are in each quadrant. This is not the case."
@@ -80,12 +80,12 @@ class WeatherSourceSpec extends UnitSpec {
agentCoordinates.latitude,
agentCoordinates.longitude,
coordinate551525.getY,
- coordinate551525.getX
+ coordinate551525.getX,
)
DummyWeatherSource.getNearestCoordinatesWithDistances(
agentCoordinates,
- 4
+ 4,
) match {
case Success(coordinateDistances) =>
coordinateDistances.size shouldBe 1
@@ -95,14 +95,14 @@ class WeatherSourceSpec extends UnitSpec {
coordinateDistance.getCoordinateB shouldBe coordinate551525
QuantityUtil.isEquivalentAbs(
coordinateDistance.getDistance,
- distance
+ distance,
) shouldBe true
case None => fail("Somebody stole the first result >:-(")
}
case Failure(exception) =>
fail(
"Determining the nearest coordinates was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -113,25 +113,25 @@ class WeatherSourceSpec extends UnitSpec {
val expectedCoordinateDistances = Vector(
new CoordinateDistance(
coordinate0,
- coordinate67775
+ coordinate67775,
),
new CoordinateDistance(
coordinate0,
- coordinate551525
+ coordinate551525,
),
new CoordinateDistance(
coordinate0,
- coordinate531137
+ coordinate531137,
),
new CoordinateDistance(
coordinate0,
- coordinate278150
- )
+ coordinate278150,
+ ),
)
DummyWeatherSource.getNearestCoordinatesWithDistances(
agentCoordinates,
- 4
+ 4,
) match {
case Success(coordinateDistances) =>
coordinateDistances.size shouldBe 4
@@ -144,7 +144,7 @@ class WeatherSourceSpec extends UnitSpec {
case Failure(exception) =>
fail(
"Determining the nearest coordinates was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -153,7 +153,7 @@ class WeatherSourceSpec extends UnitSpec {
val coordinates = Vector(
new CoordinateDistance(
coordinate0,
- coordinate67775
+ coordinate67775,
)
)
@@ -165,7 +165,7 @@ class WeatherSourceSpec extends UnitSpec {
case Failure(exception) =>
fail(
"Determining the weight of coordinates was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -174,12 +174,12 @@ class WeatherSourceSpec extends UnitSpec {
val coordinates = Vector(
new CoordinateDistance(
coordinate0,
- coordinate0
+ coordinate0,
),
new CoordinateDistance(
coordinate0,
- coordinate0
- )
+ coordinate0,
+ ),
)
DummyWeatherSource.determineWeights(coordinates) match {
@@ -198,26 +198,26 @@ class WeatherSourceSpec extends UnitSpec {
val coordinates = Vector(
new CoordinateDistance(
coordinate0,
- coordinate67775
+ coordinate67775,
),
new CoordinateDistance(
coordinate0,
- coordinate531137
+ coordinate531137,
),
new CoordinateDistance(
coordinate0,
- coordinate551525
+ coordinate551525,
),
new CoordinateDistance(
coordinate0,
- coordinate278150
- )
+ coordinate278150,
+ ),
)
val expectedWeights = Map(
coordinate67775 -> 0.254626046882988,
coordinate531137 -> 0.249222038996929,
coordinate551525 -> 0.250659514620527,
- coordinate278150 -> 0.245492399499556
+ coordinate278150 -> 0.245492399499556,
)
DummyWeatherSource.determineWeights(coordinates) match {
@@ -230,7 +230,7 @@ class WeatherSourceSpec extends UnitSpec {
case Failure(exception) =>
fail(
"Determining the weight of coordinates was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -239,7 +239,7 @@ class WeatherSourceSpec extends UnitSpec {
/* Query more coordinates, than are apparent */
DummyWeatherSource.getWeightedCoordinates(
AgentCoordinates(coordinate0.getY, coordinate0.getX),
- 9
+ 9,
) match {
case Failure(exception: ServiceException) =>
exception.getMessage shouldBe "Determination of coordinate weights failed."
@@ -255,18 +255,18 @@ class WeatherSourceSpec extends UnitSpec {
DummyWeatherSource.getWeightedCoordinates(
agentCoordinates,
- 4
+ 4,
) match {
case Success(WeightedCoordinates(weighting)) =>
weighting.size shouldBe 1
weighting.getOrElse(
coordinate551525,
- fail("Expected coordinate wasn't found")
+ fail("Expected coordinate wasn't found"),
) shouldBe 1d
case Failure(exception) =>
fail(
"Determining the nearest weighted coordinates was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -278,12 +278,12 @@ class WeatherSourceSpec extends UnitSpec {
coordinate67775 -> 0.254626046882988,
coordinate531137 -> 0.249222038996929,
coordinate551525 -> 0.250659514620527,
- coordinate278150 -> 0.245492399499556
+ coordinate278150 -> 0.245492399499556,
)
DummyWeatherSource.getWeightedCoordinates(
agentCoordinates,
- 4
+ 4,
) match {
case Success(WeightedCoordinates(weighting)) =>
weighting.corresponds(expectedWeighting) {
@@ -295,7 +295,7 @@ class WeatherSourceSpec extends UnitSpec {
case Failure(exception) =>
fail(
"Determining the nearest weighted coordinates was meant to succeed.",
- exception
+ exception,
)
}
}
@@ -309,15 +309,15 @@ class WeatherSourceSpec extends UnitSpec {
(
"",
classOf[InvalidConfigParameterException],
- "No grid model defined!"
+ "No grid model defined!",
),
("icon", classOf[IconIdCoordinateFactory], ""),
("cosmo", classOf[CosmoIdCoordinateFactory], ""),
(
"else",
classOf[InvalidConfigParameterException],
- "Grid model 'else' is not supported!"
- )
+ "Grid model 'else' is not supported!",
+ ),
)
forAll(cases) { (gridModel, expectedClass, failureMessage) =>
@@ -365,7 +365,7 @@ case object WeatherSourceSpec {
*/
override def getWeather(
tick: Long,
- weightedCoordinates: WeightedCoordinates
+ weightedCoordinates: WeightedCoordinates,
): WeatherMessage.WeatherData =
throw new UnsupportedOperationException(
"This is not supported by the dummy source."
@@ -384,7 +384,7 @@ case object WeatherSourceSpec {
*/
override def getDataTicks(
requestFrameStart: Long,
- requestFrameEnd: Long
+ requestFrameEnd: Long,
): Array[Long] =
throw new UnsupportedOperationException(
"This is not supported by the dummy source."
@@ -400,7 +400,7 @@ case object WeatherSourceSpec {
477295 -> coordinate477295,
537947 -> coordinate537947,
144112 -> coordinate144112,
- 165125 -> coordinate165125
+ 165125 -> coordinate165125,
)
private val coordinateToId = idToCoordinate.map { case (key, value) =>
@@ -422,7 +422,7 @@ case object WeatherSourceSpec {
def getClosestCoordinates(
coordinate: Point,
n: Int,
- distance: ComparableQuantity[Length]
+ distance: ComparableQuantity[Length],
): util.List[CoordinateDistance] = {
val points: Set[Point] = coordinateToId.keySet
@@ -442,7 +442,7 @@ case object WeatherSourceSpec {
override def getNearestCoordinates(
coordinate: Point,
- n: Int
+ n: Int,
): util.List[CoordinateDistance] = {
calculateCoordinateDistances(coordinate, n, coordinateToId.keySet.asJava)
}
diff --git a/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala b/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala
index 5d76f0b3e3..96eb6df6ee 100644
--- a/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala
+++ b/src/test/scala/edu/ie3/simona/service/weather/WeatherSourceWrapperSpec.scala
@@ -9,18 +9,18 @@ package edu.ie3.simona.service.weather
import edu.ie3.datamodel.io.factory.timeseries.IconTimeBasedWeatherValueFactory
import edu.ie3.datamodel.io.source.{
IdCoordinateSource,
- WeatherSource => PsdmWeatherSource
+ WeatherSource => PsdmWeatherSource,
}
import edu.ie3.datamodel.models.StandardUnits
import edu.ie3.datamodel.models.timeseries.individual.{
IndividualTimeSeries,
- TimeBasedValue
+ TimeBasedValue,
}
import edu.ie3.datamodel.models.value.WeatherValue
import edu.ie3.simona.ontology.messages.services.WeatherMessage.WeatherData
import edu.ie3.simona.service.weather.WeatherSource.{
EMPTY_WEATHER_DATA,
- WeightedCoordinates
+ WeightedCoordinates,
}
import edu.ie3.simona.service.weather.WeatherSourceSpec.DummyIdCoordinateSource
import edu.ie3.simona.service.weather.WeatherSourceWrapper.WeightSum
@@ -55,7 +55,7 @@ class WeatherSourceWrapperSpec extends UnitSpec {
classOf[IdCoordinateSource],
classOf[Long],
classOf[ComparableQuantity[Length]],
- classOf[ZonedDateTime]
+ classOf[ZonedDateTime],
)
actor.setAccessible(true)
val source = actor.newInstance(
@@ -63,7 +63,7 @@ class WeatherSourceWrapperSpec extends UnitSpec {
DummyIdCoordinateSource,
360L,
Quantities.getQuantity(10000, Units.METRE),
- ZonedDateTime.now()
+ ZonedDateTime.now(),
)
val date = ZonedDateTime.of(2021, 1, 15, 18, 0, 0, 0, ZoneId.of("UTC"))
@@ -73,15 +73,15 @@ class WeatherSourceWrapperSpec extends UnitSpec {
coordinate1a -> 0.25,
coordinate1b -> 0.25,
coordinate1c -> 0.25,
- coordinate13 -> 0.25
+ coordinate13 -> 0.25,
)
)
val result = source.getWeather(date.toEpochSecond, weightedCoordinates)
val sumOfAll = 1 + 1 + 1 + 13
- (result.dirIrr ~= WattsPerSquareMeter(sumOfAll / 4)) shouldBe true
- (result.diffIrr ~= WattsPerSquareMeter(sumOfAll / 4)) shouldBe true
- (result.temp ~= Celsius(sumOfAll / 4)) shouldBe true
- (result.windVel ~= MetersPerSecond(sumOfAll / 4)) shouldBe true
+ result.dirIrr should approximate(WattsPerSquareMeter(sumOfAll / 4))
+ result.diffIrr should approximate(WattsPerSquareMeter(sumOfAll / 4))
+ result.temp should approximate(Celsius(sumOfAll / 4))
+ result.windVel should approximate(MetersPerSecond(sumOfAll / 4))
}
@@ -91,15 +91,15 @@ class WeatherSourceWrapperSpec extends UnitSpec {
coordinate1a -> 0.25,
coordinate1b -> 0.25,
coordinate1c -> 0.25,
- coordinate13NoTemp -> 0.25
+ coordinate13NoTemp -> 0.25,
)
)
val result = source.getWeather(date.toEpochSecond, weightedCoordinates)
val sumOfAll = 1 + 1 + 1 + 13
- (result.dirIrr ~= WattsPerSquareMeter(sumOfAll / 4)) shouldBe true
- (result.diffIrr ~= WattsPerSquareMeter(sumOfAll / 4)) shouldBe true
- (result.temp ~= Celsius((1 + 1 + 1) / 3)) shouldBe true
- (result.windVel ~= MetersPerSecond(sumOfAll / 4)) shouldBe true
+ result.dirIrr should approximate(WattsPerSquareMeter(sumOfAll / 4))
+ result.diffIrr should approximate(WattsPerSquareMeter(sumOfAll / 4))
+ result.temp should approximate(Celsius((1 + 1 + 1) / 3))
+ result.windVel should approximate(MetersPerSecond(sumOfAll / 4))
}
"Calculate the correct weighted value for 4 coordinates with 0.25 weight each, where one is empty" in {
@@ -108,25 +108,25 @@ class WeatherSourceWrapperSpec extends UnitSpec {
coordinate1a -> 0.25,
coordinate1b -> 0.25,
coordinate1c -> 0.25,
- coordinateEmpty -> 0.25
+ coordinateEmpty -> 0.25,
)
)
val result = source.getWeather(date.toEpochSecond, weightedCoordinates)
val sumOfAll = 1 + 1 + 1
- (result.dirIrr ~= WattsPerSquareMeter(sumOfAll / 3)) shouldBe true
- (result.diffIrr ~= WattsPerSquareMeter(sumOfAll / 3)) shouldBe true
- (result.temp ~= Celsius(sumOfAll / 3)) shouldBe true
- (result.windVel ~= MetersPerSecond(sumOfAll / 3)) shouldBe true
+ result.dirIrr should approximate(WattsPerSquareMeter(sumOfAll / 3))
+ result.diffIrr should approximate(WattsPerSquareMeter(sumOfAll / 3))
+ result.temp should approximate(Celsius(sumOfAll / 3))
+ result.windVel should approximate(MetersPerSecond(sumOfAll / 3))
}
"calculate the correct weighted value for 1 coordinate with a weight of 1" in {
val weightedCoordinates = WeightedCoordinates(Map(coordinate13 -> 1d))
val result = source.getWeather(date.toEpochSecond, weightedCoordinates)
- (result.dirIrr ~= WattsPerSquareMeter(13d)) shouldBe true
- (result.diffIrr ~= WattsPerSquareMeter(13d)) shouldBe true
- (result.temp ~= Celsius(13d)) shouldBe true
- (result.windVel ~= MetersPerSecond(13d)) shouldBe true
+ result.dirIrr should approximate(WattsPerSquareMeter(13d))
+ result.diffIrr should approximate(WattsPerSquareMeter(13d))
+ result.temp should approximate(Celsius(13d))
+ result.windVel should approximate(MetersPerSecond(13d))
}
"return temperature quantity on absolute scale" in {
@@ -155,13 +155,13 @@ class WeatherSourceWrapperSpec extends UnitSpec {
(0.5, 0.75, 291d, 10d),
(12.3, 1.2, 293d, 12d),
(25.0, 5.7, 290d, 9d),
- (26.3, 1.7, 289d, 11d)
+ (26.3, 1.7, 289d, 11d),
)
val weights = Seq(
(0.1, 0.2, 0.3, 0.4),
(0.25, 0.2, 0.25, 0.1),
(0.3, 0.4, 0.15, 0.05),
- (0.35, 0.2, 0.3, 0.45)
+ (0.35, 0.2, 0.3, 0.45),
)
val (weightedWeather, weightSum) =
@@ -169,10 +169,10 @@ class WeatherSourceWrapperSpec extends UnitSpec {
weightSum.scale(weightedWeather) match {
case WeatherData(diffIrr, dirIrr, temp, windVel) =>
- (diffIrr ~= WattsPerSquareMeter(19.83)) shouldBe true
- (dirIrr ~= WattsPerSquareMeter(3.01)) shouldBe true
- (temp ~= Kelvin(290.75)) shouldBe true
- (windVel ~= MetersPerSecond(10.6)) shouldBe true
+ diffIrr should approximate(WattsPerSquareMeter(19.83))
+ dirIrr should approximate(WattsPerSquareMeter(3.01))
+ temp should approximate(Kelvin(290.75))
+ windVel should approximate(MetersPerSecond(10.6))
}
}
}
@@ -182,13 +182,13 @@ class WeatherSourceWrapperSpec extends UnitSpec {
(0.5, 0.75, 291d, 10d),
(12.3, 1.2, 293d, 12d),
(25.0, 5.7, 290d, 9d),
- (26.3, 1.7, 289d, 11d)
+ (26.3, 1.7, 289d, 11d),
)
val weights = Seq(
(0.1, 0.2, 0d, 0.4),
(0.25, 0.2, 0d, 0.1),
(0.3, 0.4, 0d, 0.05),
- (0.35, 0.2, 0d, 0.45)
+ (0.35, 0.2, 0d, 0.45),
)
val (weightedWeather, weightSum) =
@@ -205,13 +205,13 @@ class WeatherSourceWrapperSpec extends UnitSpec {
(0.5, 0.75, 291d, 10d),
(12.3, 1.2, 0d, 12d),
(25.0, 5.7, 290d, 9d),
- (26.3, 1.7, 289d, 11d)
+ (26.3, 1.7, 289d, 11d),
)
val weights = Seq(
(0.1, 0.2, 0.3, 0.4),
(0.25, 0.2, 0d, 0.1),
(0.3, 0.4, 0.15, 0.05),
- (0.35, 0.2, 0.3, 0.45)
+ (0.35, 0.2, 0.3, 0.45),
)
val (weightedWeather, weightSum) =
@@ -219,7 +219,7 @@ class WeatherSourceWrapperSpec extends UnitSpec {
weightSum.scale(weightedWeather) match {
case WeatherData(_, _, temp, _) =>
- (temp ~= Kelvin(290d)) shouldBe true
+ temp should approximate(Kelvin(290d))
}
}
@@ -228,17 +228,16 @@ class WeatherSourceWrapperSpec extends UnitSpec {
WattsPerSquareMeter(1.0),
WattsPerSquareMeter(1.0),
Kelvin(1.0d),
- MetersPerSecond(1.0d)
+ MetersPerSecond(1.0d),
)
val weightSum = WeightSum(0.25, 0.5, 0.8, 1.0)
weightSum.scale(weatherData) match {
case WeatherData(diffIrr, dirIrr, temp, windVel) =>
- (diffIrr ~= WattsPerSquareMeter(4.0)) shouldBe true
- (dirIrr ~= WattsPerSquareMeter(2.0)) shouldBe true
- (temp ~= Kelvin(1.25d)) shouldBe true
- (windVel ~= MetersPerSecond(1.0d)) shouldBe true
-
+ diffIrr should approximate(WattsPerSquareMeter(4.0))
+ dirIrr should approximate(WattsPerSquareMeter(2.0))
+ temp should approximate(Kelvin(1.25d))
+ windVel should approximate(MetersPerSecond(1.0d))
}
}
}
@@ -257,7 +256,7 @@ object WeatherSourceWrapperSpec {
case object DummyPsdmWeatherSource
extends PsdmWeatherSource(
DummyIdCoordinateSource,
- new IconTimeBasedWeatherValueFactory()
+ new IconTimeBasedWeatherValueFactory(),
) {
private val dummyValues = Map(
@@ -267,7 +266,7 @@ object WeatherSourceWrapperSpec {
Quantities.getQuantity(1d, StandardUnits.SOLAR_IRRADIANCE),
Quantities.getQuantity(1d, StandardUnits.TEMPERATURE),
Quantities.getQuantity(1d, StandardUnits.WIND_DIRECTION),
- Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY)
+ Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY),
),
coordinate1b -> new WeatherValue(
coordinate1b,
@@ -275,7 +274,7 @@ object WeatherSourceWrapperSpec {
Quantities.getQuantity(1d, StandardUnits.SOLAR_IRRADIANCE),
Quantities.getQuantity(1d, StandardUnits.TEMPERATURE),
Quantities.getQuantity(1d, StandardUnits.WIND_DIRECTION),
- Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY)
+ Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY),
),
coordinate1c -> new WeatherValue(
coordinate1c,
@@ -283,7 +282,7 @@ object WeatherSourceWrapperSpec {
Quantities.getQuantity(1d, StandardUnits.SOLAR_IRRADIANCE),
Quantities.getQuantity(1d, StandardUnits.TEMPERATURE),
Quantities.getQuantity(1d, StandardUnits.WIND_DIRECTION),
- Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY)
+ Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY),
),
coordinate1d -> new WeatherValue(
coordinate1d,
@@ -291,7 +290,7 @@ object WeatherSourceWrapperSpec {
Quantities.getQuantity(1d, StandardUnits.SOLAR_IRRADIANCE),
Quantities.getQuantity(1d, StandardUnits.TEMPERATURE),
Quantities.getQuantity(1d, StandardUnits.WIND_DIRECTION),
- Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY)
+ Quantities.getQuantity(1d, StandardUnits.WIND_VELOCITY),
),
coordinate13 -> new WeatherValue(
coordinate13,
@@ -299,7 +298,7 @@ object WeatherSourceWrapperSpec {
Quantities.getQuantity(13d, StandardUnits.SOLAR_IRRADIANCE),
Quantities.getQuantity(13d, StandardUnits.TEMPERATURE),
Quantities.getQuantity(13d, StandardUnits.WIND_DIRECTION),
- Quantities.getQuantity(13d, StandardUnits.WIND_VELOCITY)
+ Quantities.getQuantity(13d, StandardUnits.WIND_VELOCITY),
),
coordinate13NoTemp -> new WeatherValue(
coordinate13NoTemp,
@@ -307,7 +306,7 @@ object WeatherSourceWrapperSpec {
Quantities.getQuantity(13d, StandardUnits.SOLAR_IRRADIANCE),
null,
Quantities.getQuantity(13d, StandardUnits.WIND_DIRECTION),
- Quantities.getQuantity(13d, StandardUnits.WIND_VELOCITY)
+ Quantities.getQuantity(13d, StandardUnits.WIND_VELOCITY),
),
coordinateEmpty -> new WeatherValue(
coordinateEmpty,
@@ -315,8 +314,8 @@ object WeatherSourceWrapperSpec {
null,
null,
null,
- null
- )
+ null,
+ ),
)
override def getWeather(
@@ -331,15 +330,15 @@ object WeatherSourceWrapperSpec {
point,
new IndividualTimeSeries[WeatherValue](
UUID.randomUUID(),
- ticks.map(tick => new TimeBasedValue(tick, data)).toSet.asJava
- )
+ ticks.map(tick => new TimeBasedValue(tick, data)).toSet.asJava,
+ ),
)
}.asJava
}
override def getWeather(
timeInterval: ClosedInterval[ZonedDateTime],
- coordinates: util.Collection[Point]
+ coordinates: util.Collection[Point],
): util.Map[Point, IndividualTimeSeries[WeatherValue]] = {
val ticks = LazyList
.iterate(timeInterval.getLower)(_.plusHours(1))
@@ -352,8 +351,8 @@ object WeatherSourceWrapperSpec {
point,
new IndividualTimeSeries[WeatherValue](
UUID.randomUUID(),
- ticks.map(tick => new TimeBasedValue(tick, data)).toSet.asJava
- )
+ ticks.map(tick => new TimeBasedValue(tick, data)).toSet.asJava,
+ ),
)
}
.asJava
@@ -361,7 +360,7 @@ object WeatherSourceWrapperSpec {
override def getWeather(
date: ZonedDateTime,
- coordinate: Point
+ coordinate: Point,
): Optional[TimeBasedValue[WeatherValue]] = {
dummyValues.get(coordinate) match {
case Some(value) => Optional.of(new TimeBasedValue(date, value))
@@ -382,14 +381,14 @@ object WeatherSourceWrapperSpec {
*/
private def prepareWeightTestData(
weatherSeq: Seq[(Double, Double, Double, Double)],
- weights: Seq[(Double, Double, Double, Double)]
+ weights: Seq[(Double, Double, Double, Double)],
): (WeatherData, WeightSum) = {
val weatherData = weatherSeq.map { case (diff, dir, temp, wVel) =>
WeatherData(
WattsPerSquareMeter(diff),
WattsPerSquareMeter(dir),
Kelvin(temp),
- MetersPerSecond(wVel)
+ MetersPerSecond(wVel),
)
}
@@ -399,14 +398,14 @@ object WeatherSourceWrapperSpec {
currentSum,
(
WeatherData(diffIrr, dirIrr, temp, windVel),
- (diffWeight, dirWeight, tempWeight, wVelWeight)
- )
+ (diffWeight, dirWeight, tempWeight, wVelWeight),
+ ),
) =>
currentSum.copy(
diffIrr = currentSum.diffIrr + (diffIrr * diffWeight),
dirIrr = currentSum.dirIrr + (dirIrr * dirWeight),
temp = currentSum.temp + temp * tempWeight,
- windVel = currentSum.windVel + windVel * wVelWeight
+ windVel = currentSum.windVel + windVel * wVelWeight,
)
}
val weightSum = weights.foldLeft(WeightSum.EMPTY_WEIGHT_SUM) {
@@ -415,7 +414,7 @@ object WeatherSourceWrapperSpec {
currentWeight._1,
currentWeight._2,
currentWeight._3,
- currentWeight._4
+ currentWeight._4,
)
}
diff --git a/src/test/scala/edu/ie3/simona/sim/SimonaSimFailSpec.scala b/src/test/scala/edu/ie3/simona/sim/SimonaSimFailSpec.scala
deleted file mode 100644
index 27c6c80fae..0000000000
--- a/src/test/scala/edu/ie3/simona/sim/SimonaSimFailSpec.scala
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * © 2021. TU Dortmund University,
- * Institute of Energy Systems, Energy Efficiency and Energy Economics,
- * Research group Distribution grid planning and operation
- */
-
-package edu.ie3.simona.sim
-
-import org.apache.pekko.actor.typed.scaladsl.adapter.{
- ClassicActorRefOps,
- ClassicActorSystemOps
-}
-import org.apache.pekko.actor.{
- Actor,
- ActorContext,
- ActorRef,
- ActorSystem,
- Props
-}
-import org.apache.pekko.testkit.{TestActorRef, TestProbe}
-import com.typesafe.config.ConfigFactory
-import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.event.RuntimeEvent
-import edu.ie3.simona.scheduler.TimeAdvancer
-import edu.ie3.simona.scheduler.TimeAdvancer.StartSimMessage
-import edu.ie3.simona.sim.SimMessage.{InitSim, SimulationFailure}
-import edu.ie3.simona.sim.SimonaSimFailSpec.FailSim
-import edu.ie3.simona.sim.setup.{ExtSimSetupData, SimonaSetup}
-import edu.ie3.simona.test.common.AgentSpec
-
-class SimonaSimFailSpec
- extends AgentSpec(
- ActorSystem(
- "SimonaSimFailSpec",
- ConfigFactory
- .parseString("""
- |pekko.loggers = ["org.apache.pekko.testkit.TestEventListener"]
- |pekko.loglevel="OFF"
- """.stripMargin)
- )
- ) {
- "A SimonaSim" should {
- "properly fail on uncaught exception" in {
- val timeAdvancer = TestProbe("timeAdvancer")
-
- val failSim = TestActorRef.create[FailSim](
- system,
- Props(
- new FailSim(
- system,
- timeAdvancer.ref.toTyped
- )
- )
- )
-
- /* Init the simulation */
- failSim ! InitSim
-
- /* The sim asks the scheduler to start it's schedule */
- timeAdvancer.expectMsg(StartSimMessage())
-
- /* Trigger the child to fail */
- failSim.underlyingActor.getChild ! "fail"
-
- expectMsg(SimulationFailure)
- }
- }
-}
-
-object SimonaSimFailSpec {
- class FailSim(
- actorSystem: ActorSystem,
- timeAdvancer: org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming]
- ) extends SimonaSim(
- new DummySetup(
- actorSystem,
- timeAdvancer
- )
- ) {
- val child: ActorRef = context.actorOf(Props(new Loser))
- context.watch(child)
-
- def getChild: ActorRef = child
- }
-
- class Loser extends Actor {
- override def receive: Receive = { case _ =>
- throw new RuntimeException("Nah, I'm not gonna do that!")
- }
- }
-
- class DummySetup(
- actorSystem: ActorSystem,
- timeAdvancer: org.apache.pekko.actor.typed.ActorRef[
- TimeAdvancer.Incoming
- ],
- override val args: Array[String] = Array.empty[String]
- ) extends SimonaSetup {
-
- override val buildActorSystem: () => ActorSystem = () => actorSystem
-
- override def runtimeEventListener(
- context: ActorContext
- ): org.apache.pekko.actor.typed.ActorRef[RuntimeEvent] =
- org.apache.pekko.actor.testkit.typed.scaladsl
- .TestProbe[RuntimeEvent]()(actorSystem.toTyped)
- .ref
-
- override def systemParticipantsListener(
- context: ActorContext
- ): Seq[ActorRef] = Seq.empty[ActorRef]
-
- override def primaryServiceProxy(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef =
- TestProbe("primaryService")(actorSystem).ref
-
- override def weatherService(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef =
- TestProbe("weatherService")(actorSystem).ref
-
- override def timeAdvancer(
- context: ActorContext,
- simulation: ActorRef,
- runtimeEventListener: org.apache.pekko.actor.typed.ActorRef[
- RuntimeEvent
- ]
- ): org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming] =
- timeAdvancer
-
- override def scheduler(
- context: ActorContext,
- timeAdvancer: org.apache.pekko.actor.typed.ActorRef[
- TimeAdvancer.Incoming
- ]
- ): ActorRef = TestProbe("scheduler")(actorSystem).ref
-
- override def gridAgents(
- context: ActorContext,
- environmentRefs: EnvironmentRefs,
- systemParticipantListener: Seq[ActorRef]
- ): Iterable[ActorRef] = Iterable.empty
-
- override def extSimulations(
- context: ActorContext,
- scheduler: ActorRef
- ): ExtSimSetupData =
- ExtSimSetupData(Iterable.empty, Map.empty)
- }
-}
diff --git a/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala
new file mode 100644
index 0000000000..07522bfe44
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/sim/SimonaSimSpec.scala
@@ -0,0 +1,453 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.sim
+
+import edu.ie3.simona.agent.EnvironmentRefs
+import edu.ie3.simona.agent.grid.GridAgentMessage
+import edu.ie3.simona.api.ExtSimAdapter
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
+import edu.ie3.simona.event.listener.{
+ DelayedStopHelper,
+ ResultEventListener,
+ RuntimeEventListener,
+}
+import edu.ie3.simona.main.RunSimona.SimonaEnded
+import edu.ie3.simona.ontology.messages.SchedulerMessage
+import edu.ie3.simona.ontology.messages.SchedulerMessage.Completion
+import edu.ie3.simona.scheduler.TimeAdvancer
+import edu.ie3.simona.sim.SimonaSim.SimulationEnded
+import edu.ie3.simona.sim.SimonaSimSpec._
+import edu.ie3.simona.sim.setup.{ExtSimSetupData, SimonaSetup}
+import edu.ie3.simona.test.common.UnitSpec
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
+import org.apache.pekko.actor.typed.scaladsl.adapter._
+import org.apache.pekko.actor.typed.scaladsl.{ActorContext, Behaviors}
+import org.apache.pekko.actor.typed.{ActorRef, Behavior}
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
+
+import java.util.UUID
+
+class SimonaSimSpec extends ScalaTestWithActorTestKit with UnitSpec {
+
+ "SimonaSim" should {
+
+ "indicate success to the starter and stop" when {
+
+ "receiving SimulationEnded from TimeAdvancer" in {
+ val starter = TestProbe[SimonaEnded]("starter")
+ val runtimeListener =
+ TestProbe[RuntimeEventListener.Request]("runtimeEventListener")
+ val resultListener =
+ TestProbe[ResultEventListener.Request]("resultEventListener")
+ val timeAdvancer = TestProbe[TimeAdvancer.Request]("timeAdvancer")
+ val extSimAdapter = TestProbe[ExtSimAdapter.Stop]("extSimAdapter")
+
+ val simonaSim = spawn(
+ SimonaSim(
+ new MockSetup(
+ Some(runtimeListener.ref),
+ Some(resultListener.ref),
+ Some(timeAdvancer.ref),
+ ) {
+ override def extSimulations(
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ExtSimSetupData = {
+ // We cannot return a TestProbe ref here,
+ // needs to be a proper actor created by context
+ val extSim = context.spawn(
+ forwardMessage(Some(extSimAdapter.ref)),
+ uniqueName("extSimAdapterForwarder"),
+ )
+ ExtSimSetupData(Iterable(extSim.toClassic), Map.empty)
+ }
+ }
+ ),
+ uniqueName("simonaSim"),
+ )
+
+ simonaSim ! SimonaSim.Start(starter.ref)
+
+ // Initialization has started, mock actors are being created
+ timeAdvancer.expectMessage(TimeAdvancer.Start())
+
+ // Simulation should still "run" at this point
+ starter.expectNoMessage()
+
+ // TimeAdvancer reached its last tick, ending
+ simonaSim ! SimulationEnded
+
+ // Actors receive stop message
+ runtimeListener.expectMessage(DelayedStopHelper.FlushAndStop)
+ resultListener.expectMessage(DelayedStopHelper.FlushAndStop)
+ extSimAdapter.expectMessage(
+ ExtSimAdapter.Stop(simulationSuccessful = true)
+ )
+
+ // Both runtime and result listener actors have stopped, thus we can end
+ starter.expectMessage(SimonaEnded(successful = true))
+ starter.expectTerminated(simonaSim)
+ }
+
+ }
+
+ "indicate failure to starter and stop" when {
+
+ "child stops due to thrown exception" in {
+ val starter = TestProbe[SimonaEnded]("starter")
+ val runtimeListener =
+ TestProbe[RuntimeEventListener.Request]("runtimeEventListener")
+ val resultListener =
+ TestProbe[ResultEventListener.Request]("resultEventListener")
+ val timeAdvancer = TestProbe[TimeAdvancer.Request]("timeAdvancer")
+
+ val receiveThrowingActor =
+ TestProbe[ActorRef[SchedulerMessage]]("receiveThrowingActor")
+
+ val simonaSim = spawn(
+ SimonaSim(
+ new MockSetup(
+ Some(runtimeListener.ref),
+ Some(resultListener.ref),
+ Some(timeAdvancer.ref),
+ ) {
+
+ override def scheduler(
+ context: ActorContext[_],
+ timeAdvancer: ActorRef[TimeAdvancer.Request],
+ ): ActorRef[SchedulerMessage] = {
+ val throwingActor = context
+ .spawn[SchedulerMessage](
+ throwOnMessage,
+ uniqueName("throwingActor"),
+ )
+ // Send ref to the outside to make it accessible
+ receiveThrowingActor.ref ! throwingActor
+ throwingActor
+ }
+
+ }
+ ),
+ uniqueName("simonaSim"),
+ )
+
+ simonaSim ! SimonaSim.Start(starter.ref)
+
+ // Initialization has started, mock actors are being created
+ val throwingActor =
+ receiveThrowingActor.expectMessageType[ActorRef[SchedulerMessage]]
+ timeAdvancer.expectMessage(TimeAdvancer.Start())
+
+ // Simulation should still "run" at this point
+ starter.expectNoMessage()
+
+ // We cause an actor to fail.
+ // (The mock actor reacts to any message with an
+ // exception, we just pick the first best fit)
+ throwingActor ! Completion(TestProbe().ref)
+
+ // Runtime/result event listeners receive stop message
+ runtimeListener.expectMessage(
+ RuntimeEvent.Error("Simulation stopped with error.")
+ )
+ runtimeListener.expectMessage(DelayedStopHelper.FlushAndStop)
+ resultListener.expectMessage(DelayedStopHelper.FlushAndStop)
+
+ // Both runtime and result listener actors have stopped, thus we can end
+ starter.expectMessage(SimonaEnded(successful = false))
+ starter.expectTerminated(simonaSim)
+ }
+
+ "child stops by changing behavior" in {
+ val starter = TestProbe[SimonaEnded]("starter")
+ val runtimeListener =
+ TestProbe[RuntimeEventListener.Request]("runtimeEventListener")
+ val resultListener =
+ TestProbe[ResultEventListener.Request]("resultEventListener")
+ val timeAdvancer = TestProbe[TimeAdvancer.Request]("timeAdvancer")
+
+ val receiveStoppingActor =
+ TestProbe[ActorRef[SchedulerMessage]]("receiveStoppingActor")
+
+ val simonaSim = spawn(
+ SimonaSim(
+ new MockSetup(
+ Some(runtimeListener.ref),
+ Some(resultListener.ref),
+ Some(timeAdvancer.ref),
+ ) {
+
+ override def scheduler(
+ context: ActorContext[_],
+ timeAdvancer: ActorRef[TimeAdvancer.Request],
+ ): ActorRef[SchedulerMessage] = {
+ val stoppingActor =
+ context.spawn[SchedulerMessage](
+ stopOnMessage,
+ uniqueName("stoppingActor"),
+ )
+ // Send ref to the outside to make it accessible
+ receiveStoppingActor.ref ! stoppingActor
+ stoppingActor
+ }
+ }
+ ),
+ uniqueName("simonaSim"),
+ )
+
+ simonaSim ! SimonaSim.Start(starter.ref)
+
+ // Initialization has started, mock actors are being created
+ val stoppingActor =
+ receiveStoppingActor.expectMessageType[ActorRef[SchedulerMessage]]
+ timeAdvancer.expectMessage(TimeAdvancer.Start())
+
+ // Simulation should still "run" at this point
+ starter.expectNoMessage()
+
+ // We cause an actor to fail.
+ // (The mock actor reacts to any message by stopping
+ // itself, we just pick the first best fit)
+ stoppingActor ! Completion(TestProbe().ref)
+
+ // Runtime/result event listeners receive stop message
+ runtimeListener.expectMessage(
+ RuntimeEvent.Error("Simulation stopped with error.")
+ )
+ runtimeListener.expectMessage(DelayedStopHelper.FlushAndStop)
+ resultListener.expectMessage(DelayedStopHelper.FlushAndStop)
+
+ // Both runtime and result listener actors have stopped, thus we can end
+ starter.expectMessage(SimonaEnded(successful = false))
+ starter.expectTerminated(simonaSim)
+ }
+
+ "RuntimeEventListener stops unexpectedly" in {
+ val starter = TestProbe[SimonaEnded]("starter")
+ val resultListener =
+ TestProbe[ResultEventListener.Request]("resultEventListener")
+ val timeAdvancer = TestProbe[TimeAdvancer.Request]("timeAdvancer")
+
+ val receiveThrowingActor =
+ TestProbe[ActorRef[RuntimeEventListener.Request]](
+ "receiveThrowingActor"
+ )
+
+ val simonaSim = spawn(
+ SimonaSim(
+ new MockSetup(
+ None,
+ Some(resultListener.ref),
+ Some(timeAdvancer.ref),
+ ) {
+
+ override def runtimeEventListener(
+ context: ActorContext[_]
+ ): ActorRef[RuntimeEventListener.Request] = {
+ val throwingActor = context
+ .spawn[RuntimeEventListener.Request](
+ throwOnMessage,
+ uniqueName("throwingActor"),
+ )
+ // Send ref to the outside
+ receiveThrowingActor.ref ! throwingActor
+ throwingActor
+ }
+
+ }
+ ),
+ uniqueName("simonaSim"),
+ )
+
+ simonaSim ! SimonaSim.Start(starter.ref)
+
+ // Initialization has started, mock actors are being created
+ val throwingActor =
+ receiveThrowingActor
+ .expectMessageType[ActorRef[RuntimeEventListener.Request]]
+ timeAdvancer.expectMessage(TimeAdvancer.Start())
+
+ // Simulation should still "run" at this point
+ starter.expectNoMessage()
+
+ // We cause the RuntimeEventListener to fail.
+ // (The mock actor reacts to any message with an
+ // exception, we just pick the first best fit)
+ throwingActor ! RuntimeEvent.Initializing
+
+ // Result event listener receives stop message
+ resultListener.expectMessage(DelayedStopHelper.FlushAndStop)
+
+ // Both runtime and result listener actors have stopped, thus we can end
+ starter.expectMessage(SimonaEnded(successful = false))
+ starter.expectTerminated(simonaSim)
+ }
+
+ "exception is thrown while initializing" in {
+ val starter = TestProbe[SimonaEnded]("starter")
+
+ val simonaSim = spawn(
+ SimonaSim(
+ new MockSetup() {
+
+ override def resultEventListener(
+ context: ActorContext[_]
+ ): Seq[ActorRef[ResultEventListener.Request]] =
+ throwTestException()
+ }
+ ),
+ uniqueName("simonaSim"),
+ )
+
+ // Initialization should not have started yet
+ starter.expectNoMessage()
+
+ simonaSim ! SimonaSim.Start(starter.ref)
+
+ // SimonaSim should run its failure routine and tell us that a failure happened
+ starter.expectMessage(SimonaEnded(successful = false))
+ starter.expectTerminated(simonaSim)
+ }
+ }
+
+ }
+
+}
+
+object SimonaSimSpec {
+
+ /** Behavior that does nothing on receiving message */
+ def empty[T]: Behavior[T] = Behaviors.receiveMessage { _ =>
+ Behaviors.same
+ }
+
+ /** Behavior that forwards all messages to given actor. This is required
+ * because unless actors are created by the ActorContext inside SimonaSim,
+ * they cannot be stopped by SimonaSim.
+ */
+ def forwardMessage[T](recipient: Option[ActorRef[T]]): Behavior[T] =
+ Behaviors.receiveMessage { msg =>
+ recipient.foreach(_ ! msg)
+ Behaviors.same
+ }
+
+ /** Similarly to [[forwardMessage]], this behavior stops on receiving
+ * [[DelayedStopHelper.FlushAndStop]] and forwards all other messages to
+ * given actor.
+ */
+ def stoppableForwardMessage[T >: DelayedStopHelper.StoppingMsg](
+ recipient: Option[ActorRef[T]]
+ ): Behavior[T] =
+ Behaviors.receiveMessage {
+ case msg @ DelayedStopHelper.FlushAndStop =>
+ recipient.foreach(_ ! msg)
+ Behaviors.stopped
+ case msg =>
+ recipient.foreach(_ ! msg)
+ Behaviors.same
+ }
+
+ /** Behavior that throws a RuntimeException on receiving any message, which
+ * should cause the actor to stop
+ */
+ def throwOnMessage[T]: Behavior[T] = Behaviors.receiveMessage { _ =>
+ throwTestException()
+ }
+
+ /** Behavior that stops itself on the first received message */
+ def stopOnMessage[T]: Behavior[T] = Behaviors.receiveMessage { _ =>
+ Behaviors.stopped
+ }
+
+ def throwTestException[T](): T = throw new RuntimeException(
+ "This is an exception for test purposes. It is expected to be thrown."
+ )
+
+ /** Makes the given actor name unique by appending a random UUID */
+ def uniqueName(name: String): String =
+ s"${name}_${UUID.randomUUID()}"
+
+ /** Mock implementation of [[SimonaSetup]]
+ *
+ * @param runtimeEventProbe
+ * Optional ActorRef that messages received by RuntimeEventListener are
+ * forwarded to
+ * @param resultEventProbe
+ * Optional ActorRef that messages received by ResultEventListener are
+ * forwarded to
+ * @param timeAdvancerProbe
+ * Optional ActorRef that messages received by TimeAdvancer are forwarded
+ * to
+ */
+ class MockSetup(
+ runtimeEventProbe: Option[ActorRef[RuntimeEventListener.Request]] = None,
+ resultEventProbe: Option[ActorRef[ResultEventListener.Request]] = None,
+ timeAdvancerProbe: Option[ActorRef[TimeAdvancer.Request]] = None,
+ ) extends SimonaSetup {
+
+ override val args: Array[String] = Array.empty[String]
+
+ override def runtimeEventListener(
+ context: ActorContext[_]
+ ): ActorRef[RuntimeEventListener.Request] = context.spawn(
+ stoppableForwardMessage(runtimeEventProbe),
+ uniqueName("runtimeEventForwarder"),
+ )
+
+ override def resultEventListener(
+ context: ActorContext[_]
+ ): Seq[ActorRef[ResultEventListener.Request]] = Seq(
+ context.spawn(
+ stoppableForwardMessage(resultEventProbe),
+ uniqueName("resultEventForwarder"),
+ )
+ )
+
+ override def primaryServiceProxy(
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef =
+ context.spawn(empty, uniqueName("primaryService")).toClassic
+
+ override def weatherService(
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef =
+ context.spawn(empty, uniqueName("weatherService")).toClassic
+
+ override def timeAdvancer(
+ context: ActorContext[_],
+ simulation: ActorRef[SimonaSim.SimulationEnded.type],
+ runtimeEventListener: ActorRef[RuntimeEvent],
+ ): ActorRef[TimeAdvancer.Request] =
+ context.spawn(
+ forwardMessage(timeAdvancerProbe),
+ uniqueName("timeAdvancerForwarder"),
+ )
+
+ override def scheduler(
+ context: ActorContext[_],
+ timeAdvancer: ActorRef[TimeAdvancer.Request],
+ ): ActorRef[SchedulerMessage] =
+ context.spawn(empty, uniqueName("scheduler"))
+
+ override def gridAgents(
+ context: ActorContext[_],
+ environmentRefs: EnvironmentRefs,
+ resultEventListeners: Seq[ActorRef[ResultEvent]],
+ ): Iterable[ActorRef[GridAgentMessage]] = Iterable.empty
+
+ override def extSimulations(
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ExtSimSetupData =
+ ExtSimSetupData(Iterable.empty, Map.empty)
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/sim/setup/SetupHelperSpec.scala b/src/test/scala/edu/ie3/simona/sim/setup/SetupHelperSpec.scala
index 93ae23d06b..1a933a3bd9 100644
--- a/src/test/scala/edu/ie3/simona/sim/setup/SetupHelperSpec.scala
+++ b/src/test/scala/edu/ie3/simona/sim/setup/SetupHelperSpec.scala
@@ -7,28 +7,38 @@
package edu.ie3.simona.sim.setup
import java.util.UUID
-
-import org.apache.pekko.actor.ActorRef
+import org.apache.pekko.actor.typed.ActorRef
import org.apache.pekko.testkit.TestException
import edu.ie3.datamodel.models.input.MeasurementUnitInput
import edu.ie3.datamodel.models.input.connector.{
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
JointGridContainer,
- RawGridElements
+ RawGridElements,
}
+import edu.ie3.simona.agent.grid.GridAgentMessage
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.test.common.input.GridInputTestData
+import org.apache.pekko.actor.testkit.typed.scaladsl.{
+ ScalaTestWithActorTestKit,
+ TestProbe,
+}
import scala.jdk.CollectionConverters._
-class SetupHelperSpec extends UnitSpec with GridInputTestData {
+class SetupHelperSpec
+ extends ScalaTestWithActorTestKit
+ with UnitSpec
+ with GridInputTestData {
private final object SetupHelperInstance extends SetupHelper
"A setup helper" should {
+ val actorRef: ActorRef[GridAgentMessage] =
+ TestProbe[GridAgentMessage]("mock_grid_agent").ref
+
"reduce multiple SubGridGates between the same superior and inferior nodes to one unique SubGridGate" in {
// build dummy grid with two transformers between the same nodes based on the basic grid input test data
@@ -42,7 +52,7 @@ class SetupHelperSpec extends UnitSpec with GridInputTestData {
adaptedTransformerInputModel.getParallelDevices,
adaptedTransformerInputModel.getType,
adaptedTransformerInputModel.getTapPos,
- adaptedTransformerInputModel.isAutoTap
+ adaptedTransformerInputModel.isAutoTap,
)
val adaptedTransformers = transformers + secondTransformer
@@ -52,14 +62,14 @@ class SetupHelperSpec extends UnitSpec with GridInputTestData {
adaptedTransformers.asJava,
Set.empty[Transformer3WInput].asJava,
switches.asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
val gridModel = new JointGridContainer(
"TestGrid",
rawGridElements,
validTestGridInputModel.getSystemParticipants,
- validTestGridInputModel.getGraphics
+ validTestGridInputModel.getGraphics,
)
val subGrids = gridModel.getSubGridTopologyGraph
@@ -73,17 +83,17 @@ class SetupHelperSpec extends UnitSpec with GridInputTestData {
val superiorGrid = subGrids
.getOrElse(
1,
- throw TestException("Cannot get subGrid with id 1 from test data!")
+ throw TestException("Cannot get subGrid with id 1 from test data!"),
)
val inferiorGrid = subGrids
.getOrElse(
100,
- throw TestException("Cannot get subGrid with id 100 from test data!")
+ throw TestException("Cannot get subGrid with id 100 from test data!"),
)
val subGridToActorRefMap =
- Map(1 -> ActorRef.noSender, 100 -> ActorRef.noSender)
+ Map(1 -> actorRef, 100 -> actorRef)
// subGrid gates should be the same for this case
gridModel.getSubGridTopologyGraph.edgesOf(
@@ -102,14 +112,14 @@ class SetupHelperSpec extends UnitSpec with GridInputTestData {
.buildGateToActorRef(
subGridToActorRefMap,
subGridGates,
- superiorGrid.getSubnet
+ superiorGrid.getSubnet,
)
.size shouldBe 1
SetupHelperInstance
.buildGateToActorRef(
subGridToActorRefMap,
subGridGates,
- inferiorGrid.getSubnet
+ inferiorGrid.getSubnet,
)
.size shouldBe 1
diff --git a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala
index 26b6246e42..de7cc0e38d 100644
--- a/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala
+++ b/src/test/scala/edu/ie3/simona/sim/setup/SimonaSetupSpec.scala
@@ -6,17 +6,23 @@
package edu.ie3.simona.sim.setup
-import org.apache.pekko.actor.{ActorContext, ActorRef, ActorSystem}
import edu.ie3.datamodel.exceptions.NotImplementedException
import edu.ie3.datamodel.models.input.connector.{
ConnectorPort,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.simona.agent.EnvironmentRefs
-import edu.ie3.simona.event.RuntimeEvent
+import edu.ie3.simona.agent.grid.GridAgentMessage
+import edu.ie3.simona.event.listener.{ResultEventListener, RuntimeEventListener}
+import edu.ie3.simona.event.{ResultEvent, RuntimeEvent}
+import edu.ie3.simona.ontology.messages.SchedulerMessage
import edu.ie3.simona.scheduler.TimeAdvancer
+import edu.ie3.simona.sim.SimonaSim
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.simona.test.common.model.grid.SubGridGateMokka
+import org.apache.pekko.actor.typed.ActorRef
+import org.apache.pekko.actor.typed.scaladsl.ActorContext
+import org.apache.pekko.actor.{ActorRef => ClassicRef}
import java.util.UUID
@@ -24,53 +30,55 @@ class SimonaSetupSpec extends UnitSpec with SimonaSetup with SubGridGateMokka {
override val args: Array[String] = Array.empty[String]
- override val buildActorSystem: () => ActorSystem = () =>
- throw new NotImplementedException("This is a dummy setup")
-
override def runtimeEventListener(
- context: ActorContext
- ): org.apache.pekko.actor.typed.ActorRef[RuntimeEvent] =
+ context: ActorContext[_]
+ ): ActorRef[RuntimeEventListener.Request] =
+ throw new NotImplementedException(
+ "This is a dummy setup"
+ )
+
+ override def resultEventListener(
+ context: ActorContext[_]
+ ): Seq[ActorRef[ResultEventListener.Request]] =
throw new NotImplementedException("This is a dummy setup")
- override def systemParticipantsListener(
- context: ActorContext
- ): Seq[ActorRef] = throw new NotImplementedException("This is a dummy setup")
-
override def primaryServiceProxy(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef =
- throw new NotImplementedException("This is a dummy setup")
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef = throw new NotImplementedException("This is a dummy setup")
override def weatherService(
- context: ActorContext,
- scheduler: ActorRef
- ): ActorRef =
- throw new NotImplementedException("This is a dummy setup")
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ClassicRef = throw new NotImplementedException("This is a dummy setup")
override def extSimulations(
- context: ActorContext,
- scheduler: ActorRef
- ): ExtSimSetupData =
- throw new NotImplementedException("This is a dummy setup")
+ context: ActorContext[_],
+ scheduler: ActorRef[SchedulerMessage],
+ ): ExtSimSetupData = throw new NotImplementedException(
+ "This is a dummy setup"
+ )
override def timeAdvancer(
- context: ActorContext,
- simulation: ActorRef,
- runtimeEventListener: org.apache.pekko.actor.typed.ActorRef[RuntimeEvent]
- ): org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming] =
- throw new NotImplementedException("This is a dummy setup")
+ context: ActorContext[_],
+ simulation: ActorRef[SimonaSim.SimulationEnded.type],
+ runtimeEventListener: ActorRef[RuntimeEvent],
+ ): ActorRef[TimeAdvancer.Request] = throw new NotImplementedException(
+ "This is a dummy setup"
+ )
override def scheduler(
- context: ActorContext,
- timeAdvancer: org.apache.pekko.actor.typed.ActorRef[TimeAdvancer.Incoming]
- ): ActorRef = throw new NotImplementedException("This is a dummy setup")
+ context: ActorContext[_],
+ timeAdvancer: ActorRef[TimeAdvancer.Request],
+ ): ActorRef[SchedulerMessage] = throw new NotImplementedException(
+ "This is a dummy setup"
+ )
override def gridAgents(
- context: ActorContext,
+ context: ActorContext[_],
environmentRefs: EnvironmentRefs,
- systemParticipantListener: Seq[ActorRef]
- ): Iterable[ActorRef] =
+ resultEventListeners: Seq[ActorRef[ResultEvent]],
+ ): Iterable[ActorRef[GridAgentMessage]] =
throw new NotImplementedException("This is a dummy setup")
"Attempting to modify a sub grid gate" should {
@@ -94,7 +102,7 @@ class SimonaSetupSpec extends UnitSpec with SimonaSetup with SubGridGateMokka {
2,
nodeCUuid,
3,
- ConnectorPort.C
+ ConnectorPort.C,
)
val internalNode = subGridGate.link match {
case input: Transformer3WInput => input.getNodeInternal
diff --git a/src/test/scala/edu/ie3/simona/test/KafkaSpecLike.scala b/src/test/scala/edu/ie3/simona/test/KafkaSpecLike.scala
index d228185517..15962057cf 100644
--- a/src/test/scala/edu/ie3/simona/test/KafkaSpecLike.scala
+++ b/src/test/scala/edu/ie3/simona/test/KafkaSpecLike.scala
@@ -38,7 +38,7 @@ trait KafkaSpecLike extends BeforeAndAfterAll {
new NewTopic(
topic.name,
topic.partitions,
- topic.replicationFactor
+ topic.replicationFactor,
)
}.asJava
)
@@ -58,6 +58,6 @@ object KafkaSpecLike {
final case class Topic(
name: String,
partitions: Int,
- replicationFactor: Short
+ replicationFactor: Short,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/ParticipantAgentSpec.scala b/src/test/scala/edu/ie3/simona/test/ParticipantAgentSpec.scala
index b7de3f6435..e6a3d9e6e0 100644
--- a/src/test/scala/edu/ie3/simona/test/ParticipantAgentSpec.scala
+++ b/src/test/scala/edu/ie3/simona/test/ParticipantAgentSpec.scala
@@ -6,11 +6,12 @@
package edu.ie3.simona.test
+import edu.ie3.simona.test.common.AgentSpec
import org.apache.pekko.actor.ActorSystem
import org.apache.pekko.testkit.TestProbe
-import edu.ie3.simona.test.common.AgentSpec
-/** Class to help building tests for [[ParticipantAgent]] s
+/** Class to help building tests for
+ * [[edu.ie3.simona.agent.participant.ParticipantAgent]]s
*
* @param actorSystem
* The actor system to use for building actors
diff --git a/src/test/scala/edu/ie3/simona/test/common/DefaultTestData.scala b/src/test/scala/edu/ie3/simona/test/common/DefaultTestData.scala
index 579589316e..f989f9082b 100644
--- a/src/test/scala/edu/ie3/simona/test/common/DefaultTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/DefaultTestData.scala
@@ -46,7 +46,7 @@ trait DefaultTestData {
SystemComponent.determineOperationInterval(
defaultSimulationStart,
defaultSimulationEnd,
- defaultOperationTime
+ defaultOperationTime,
)
// default Lat/Long
@@ -60,7 +60,7 @@ trait DefaultTestData {
protected val default400Kva10KvRefSystem: RefSystem = RefSystem(
Kilowatts(400d),
- Kilovolts(10d)
+ Kilovolts(10d),
)
/** Creates a [[SimonaConfig]], that provides the desired participant model
@@ -75,7 +75,7 @@ trait DefaultTestData {
*/
def createSimonaConfig(
modelBehaviour: LoadModelBehaviour.Value,
- reference: LoadReference
+ reference: LoadReference,
): SimonaConfig = {
val typesafeConfig: Config = ConfigFactory.parseString(
s"""
@@ -98,6 +98,7 @@ trait DefaultTestData {
| notifier = "default"
| powerRequestReply = false
| simulationResult = false
+ | flexResult = false
|}
|simona.output.participant.individualConfigs = []
|
diff --git a/src/test/scala/edu/ie3/simona/test/common/EvTestData.scala b/src/test/scala/edu/ie3/simona/test/common/EvTestData.scala
index 93f2820010..ca77f35553 100644
--- a/src/test/scala/edu/ie3/simona/test/common/EvTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/EvTestData.scala
@@ -17,12 +17,16 @@ trait EvTestData {
UUID.fromString("73c041c7-68e9-470e-8ca2-21fd7dbd1797"),
"evA",
Quantities.getQuantity(11d, PowerSystemUnits.KILOWATT),
- Quantities.getQuantity(58d, PowerSystemUnits.KILOWATTHOUR)
+ Quantities.getQuantity(11d, PowerSystemUnits.KILOWATT),
+ Quantities.getQuantity(58d, PowerSystemUnits.KILOWATTHOUR),
+ 200,
)
protected val evB: MockEvModel = new MockEvModel(
UUID.fromString("6d7d27a1-5cbb-4b73-aecb-dfcc5a6fb22e"),
"evB",
Quantities.getQuantity(11d, PowerSystemUnits.KILOWATT),
- Quantities.getQuantity(80d, PowerSystemUnits.KILOWATTHOUR)
+ Quantities.getQuantity(11d, PowerSystemUnits.KILOWATT),
+ Quantities.getQuantity(80d, PowerSystemUnits.KILOWATTHOUR),
+ 200,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/SilentTestEventListener.scala b/src/test/scala/edu/ie3/simona/test/common/SilentTestEventListener.scala
index f3acaeeddd..0c960b230c 100644
--- a/src/test/scala/edu/ie3/simona/test/common/SilentTestEventListener.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/SilentTestEventListener.scala
@@ -28,7 +28,7 @@ class SilentTestEventListener extends TestEventListener with LazyLogging {
Warning(
simpleName(this),
this.getClass,
- "received unexpected event of class " + e.getClass + ": " + e
+ "received unexpected event of class " + e.getClass + ": " + e,
)
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/ThreeWindingTestData.scala b/src/test/scala/edu/ie3/simona/test/common/ThreeWindingTestData.scala
index 075ad05670..ad497696ba 100644
--- a/src/test/scala/edu/ie3/simona/test/common/ThreeWindingTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/ThreeWindingTestData.scala
@@ -12,16 +12,16 @@ import edu.ie3.datamodel.models.input.connector.{
LineInput,
SwitchInput,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
JointGridContainer,
- RawGridElements
+ RawGridElements,
}
import edu.ie3.datamodel.models.input.{
MeasurementUnitInput,
NodeInput,
- OperatorInput
+ OperatorInput,
}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.util.TestGridFactory
@@ -44,7 +44,7 @@ trait ThreeWindingTestData extends DefaultTestData {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.EHV_380KV,
- 1
+ 1,
)
private val nodeB = new NodeInput(
UUID.fromString("3d4c66a3-dc11-4ec8-857a-53d77beb15ee"),
@@ -55,7 +55,7 @@ trait ThreeWindingTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 2
+ 2,
)
private val nodeC = new NodeInput(
UUID.fromString("a865a429-615e-44be-9d00-d384298986f6"),
@@ -66,7 +66,7 @@ trait ThreeWindingTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.MV_10KV,
- 3
+ 3,
)
private val transformerType = new Transformer3WTypeInput(
@@ -90,7 +90,7 @@ trait ThreeWindingTestData extends DefaultTestData {
Quantities.getQuantity(0d, DEGREE_GEOM),
0,
-10,
- 10
+ 10,
)
private val transformer = new Transformer3WInput(
@@ -104,7 +104,7 @@ trait ThreeWindingTestData extends DefaultTestData {
1,
transformerType,
0,
- true
+ true,
)
protected val threeWindingTestGrid: JointGridContainer = {
@@ -114,11 +114,11 @@ trait ThreeWindingTestData extends DefaultTestData {
Set.empty[Transformer2WInput].asJava,
Set(transformer).asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
TestGridFactory.createJointGrid(
gridName = "threeWindingTestGrid",
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/UnitSpec.scala b/src/test/scala/edu/ie3/simona/test/common/UnitSpec.scala
index ab6b1752bf..5cbf725051 100644
--- a/src/test/scala/edu/ie3/simona/test/common/UnitSpec.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/UnitSpec.scala
@@ -6,15 +6,17 @@
package edu.ie3.simona.test.common
-import java.util.Locale
import com.typesafe.scalalogging.LazyLogging
-import edu.ie3.simona.test.matchers.QuantityMatchers
+import edu.ie3.simona.test.matchers.{QuantityMatchers, SquantsMatchers}
import edu.ie3.util.scala.quantities.{QuantityUtil => PSQuantityUtil}
+import org.apache.pekko.actor.testkit.typed.scaladsl.LogCapturing
import org.scalatest._
import org.scalatest.matchers.should
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpecLike
+import java.util.Locale
+
/** Base class to be used with all scala unit tests. All data that should be
* commonly available to all unit tests should be placed here instead of mixing
* a lot of traits together. See
@@ -26,7 +28,9 @@ import org.scalatest.wordspec.AnyWordSpecLike
trait UnitSpec
extends should.Matchers
with QuantityMatchers
+ with SquantsMatchers
with AnyWordSpecLike
+ with LogCapturing
with OptionValues
with Inside
with Inspectors
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala
index a1483310a5..b7c0f678ce 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/CylindricalStorageInputTestData.scala
@@ -11,7 +11,7 @@ import java.util.UUID
import edu.ie3.datamodel.models.StandardUnits
import edu.ie3.datamodel.models.input.thermal.{
CylindricalStorageInput,
- ThermalBusInput
+ ThermalBusInput,
}
import tech.units.indriya.quantity.Quantities.getQuantity
@@ -25,6 +25,6 @@ trait CylindricalStorageInputTestData {
getQuantity(20, StandardUnits.VOLUME),
getQuantity(30, StandardUnits.TEMPERATURE),
getQuantity(40, StandardUnits.TEMPERATURE),
- getQuantity(1.15, StandardUnits.SPECIFIC_HEAT_CAPACITY)
+ getQuantity(1.15, StandardUnits.SPECIFIC_HEAT_CAPACITY),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala
index 3fd0600aa4..1ed1312d92 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/EvcsInputTestData.scala
@@ -12,15 +12,9 @@ import edu.ie3.datamodel.models.input.system.EvcsInput
import edu.ie3.datamodel.models.input.system.`type`.chargingpoint.ChargingPointTypeUtils
import edu.ie3.datamodel.models.input.system.`type`.evcslocation.EvcsLocationType
import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
-import edu.ie3.simona.config.SimonaConfig
-import edu.ie3.simona.event.notifier.NotifierConfig
-import edu.ie3.simona.model.participant.load.{LoadModelBehaviour, LoadReference}
+import edu.ie3.simona.model.participant.evcs.EvcsModel
import edu.ie3.simona.test.common.DefaultTestData
-import edu.ie3.simona.util.ConfigUtil
-import edu.ie3.util.TimeUtil
-import squants.energy.Kilowatts
-import java.time.ZonedDateTime
import java.util.UUID
trait EvcsInputTestData extends DefaultTestData with NodeInputTestData {
@@ -28,7 +22,7 @@ trait EvcsInputTestData extends DefaultTestData with NodeInputTestData {
protected val evcsInputModel = new EvcsInput(
UUID.randomUUID(),
"Dummy_EvcsModel",
- new OperatorInput(UUID.randomUUID(), "NO_OPERATOR"),
+ OperatorInput.NO_OPERATOR_ASSIGNED,
OperationTime.notLimited(),
nodeInputNoSlackNs04KvA,
CosPhiFixed.CONSTANT_CHARACTERISTIC,
@@ -36,32 +30,16 @@ trait EvcsInputTestData extends DefaultTestData with NodeInputTestData {
2,
0.95,
EvcsLocationType.HOME,
- true
+ true,
)
- protected val simonaConfig: SimonaConfig =
- createSimonaConfig(
- LoadModelBehaviour.FIX,
- LoadReference.ActivePower(Kilowatts(0.0))
- )
-
- private val configUtil = ConfigUtil.ParticipantConfigUtil(
- simonaConfig.simona.runtime.participant
+ protected val evcsStandardModel: EvcsModel = EvcsModel(
+ evcsInputModel,
+ 1.0,
+ defaultSimulationStart,
+ defaultSimulationEnd,
+ "maxPower",
+ lowestEvSoc = 0.2,
)
- protected val defaultOutputConfig: NotifierConfig =
- NotifierConfig(
- simonaConfig.simona.output.participant.defaultConfig.simulationResult,
- simonaConfig.simona.output.participant.defaultConfig.powerRequestReply
- )
-
- protected val modelConfig: SimonaConfig.EvcsRuntimeConfig =
- configUtil.getOrDefault[SimonaConfig.EvcsRuntimeConfig](
- evcsInputModel.getUuid
- )
-
- protected implicit val simulationStartDate: ZonedDateTime =
- TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00")
- protected val simulationEndDate: ZonedDateTime =
- TimeUtil.withDefaults.toZonedDateTime("2020-01-01 02:00:00")
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/FixedFeedInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/FixedFeedInputTestData.scala
index 7fc9e2d6f3..6c86b34869 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/FixedFeedInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/FixedFeedInputTestData.scala
@@ -30,6 +30,6 @@ trait FixedFeedInputTestData extends NodeInputTestData {
nodeInputNoSlackNs04KvA,
new CosPhiFixed("cosPhiFixed:{(0.0,0.95)}"),
Quantities.getQuantity(282.74d, VOLTAMPERE),
- 0.95
+ 0.95,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/GridInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/GridInputTestData.scala
index 5095730cba..d9cdbb2681 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/GridInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/GridInputTestData.scala
@@ -10,16 +10,16 @@ import edu.ie3.datamodel.models.input.connector.{
LineInput,
SwitchInput,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
RawGridElements,
- SubGridContainer
+ SubGridContainer,
}
import edu.ie3.datamodel.models.input.{MeasurementUnitInput, NodeInput}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils.{
HV,
- MV_10KV
+ MV_10KV,
}
import edu.ie3.simona.model.grid.RefSystem
import edu.ie3.simona.test.common.DefaultTestData
@@ -90,7 +90,7 @@ trait GridInputTestData
inputNode15,
inputNode16,
inputNode17,
- inputNode18
+ inputNode18,
)
// create the lines
@@ -120,7 +120,7 @@ trait GridInputTestData
line1_13,
line14_2,
line0_15,
- line16_3
+ line16_3,
)
// create the switches
@@ -144,7 +144,7 @@ trait GridInputTestData
transformerInput.getParallelDevices,
transformerInput.getType,
transformerInput.getTapPos,
- transformerInput.isAutoTap
+ transformerInput.isAutoTap,
)
val transformers: Set[Transformer2WInput] = Set(adaptedTransformerInputModel)
@@ -163,7 +163,7 @@ trait GridInputTestData
transformers.asJava,
Set.empty[Transformer3WInput].asJava,
switches.asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
TestGridFactory.createSubGrid(
rawGridElements = rawGridElements
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/LineInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/LineInputTestData.scala
index 621516d2a7..cd011673f3 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/LineInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/LineInputTestData.scala
@@ -18,7 +18,7 @@ import edu.ie3.util.quantities.PowerSystemUnits.{
KILOMETRE,
KILOVOLT,
OHM_PER_KILOMETRE,
- SIEMENS_PER_KILOMETRE
+ SIEMENS_PER_KILOMETRE,
}
import tech.units.indriya.quantity.Quantities
import tech.units.indriya.unit.Units.AMPERE
@@ -40,7 +40,7 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(0.437, OHM_PER_KILOMETRE),
Quantities.getQuantity(0.356, OHM_PER_KILOMETRE),
Quantities.getQuantity(300d, AMPERE),
- Quantities.getQuantity(10, KILOVOLT)
+ Quantities.getQuantity(10, KILOVOLT),
)
// / 10 kV line models
@@ -56,9 +56,9 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(0.75, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(
nodeInputNoSlackMs10Kv,
- nodeInputNoSlackMs10Kv
+ nodeInputNoSlackMs10Kv,
),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
// 20 kV line input models
@@ -71,7 +71,7 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(0.207000002264977, OHM_PER_KILOMETRE),
Quantities.getQuantity(0.0691149979829788, OHM_PER_KILOMETRE),
Quantities.getQuantity(300, AMPERE),
- Quantities.getQuantity(20, KILOVOLT)
+ Quantities.getQuantity(20, KILOVOLT),
)
// / 20 kV line models
@@ -87,9 +87,9 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(20, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(
nodeInputNoSlackMs20Kv,
- nodeInputNoSlackMs20Kv
+ nodeInputNoSlackMs20Kv,
),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val lineInputWithTooHighVoltLvlA = new LineInput(
UUID.fromString("da55ed58-e3b1-4cc3-b9e7-997082a2a624"),
@@ -103,9 +103,9 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(20, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(
nodeInputNoSlackNs04KvA,
- nodeInputNoSlackMs20Kv
+ nodeInputNoSlackMs20Kv,
),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val lineInputWithTooLowVoltLvlA = new LineInput(
UUID.fromString("8c712c17-2f6f-4ae3-beb9-eb66563ffd32"),
@@ -119,9 +119,9 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(20, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(
nodeInputNoSlackNs04KvA,
- nodeInputNoSlackMs20Kv
+ nodeInputNoSlackMs20Kv,
),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val lineInputWithTooHighVoltLvlB = new LineInput(
UUID.fromString("cc9d9547-42ab-4613-93c0-17e6ac11cd9c"),
@@ -135,9 +135,9 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(20, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(
nodeInputNoSlackMs20Kv,
- nodeInputNoSlackNs04KvA
+ nodeInputNoSlackNs04KvA,
),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val lineInputWithTooLowVoltLvlB = new LineInput(
UUID.fromString("cb910fee-b7cb-4b14-8609-f85e83c6973b"),
@@ -151,8 +151,8 @@ trait LineInputTestData extends DefaultTestData with NodeInputTestData {
Quantities.getQuantity(20, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(
nodeInputNoSlackMs20Kv,
- nodeInputNoSlackNs04KvA
+ nodeInputNoSlackNs04KvA,
),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/LoadInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/LoadInputTestData.scala
index b99c09fc8f..d77a6762e7 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/LoadInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/LoadInputTestData.scala
@@ -33,6 +33,6 @@ trait LoadInputTestData extends NodeInputTestData {
false,
Quantities.getQuantity(3000d, KILOWATTHOUR),
Quantities.getQuantity(282.74d, VOLTAMPERE),
- 0.95
+ 0.95,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/NodeInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/NodeInputTestData.scala
index 4d63805474..27ace499de 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/NodeInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/NodeInputTestData.scala
@@ -31,7 +31,7 @@ trait NodeInputTestData extends DefaultTestData {
false,
defaultLatLong,
GermanVoltageLevelUtils.LV,
- -1
+ -1,
)
protected val nodeInputNoSlackNs04KvB = new NodeInput(
UUID.fromString("ad39d0b9-5ad6-4588-8d92-74c7d7de9ace"),
@@ -42,7 +42,7 @@ trait NodeInputTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.LV,
- -1
+ -1,
)
protected val nodeInputNoSlackNs04KvWrongVTarget = new NodeInput(
UUID.fromString("7be605eb-fc14-4fdc-a580-a2c2a9abd5f7"),
@@ -53,7 +53,7 @@ trait NodeInputTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.LV,
- -1
+ -1,
)
// 10 kV node input models
@@ -66,7 +66,7 @@ trait NodeInputTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.MV_10KV,
- -1
+ -1,
)
// 20 kV node input models
@@ -80,7 +80,7 @@ trait NodeInputTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.MV_20KV,
- 1
+ 1,
)
// 110 kV node input models
@@ -94,6 +94,6 @@ trait NodeInputTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/PvInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/PvInputTestData.scala
index f92c0cff86..28769ee202 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/PvInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/PvInputTestData.scala
@@ -6,19 +6,17 @@
package edu.ie3.simona.test.common.input
-import java.time.ZonedDateTime
-import java.util.UUID
-
import edu.ie3.datamodel.models.input.OperatorInput
import edu.ie3.datamodel.models.input.system.PvInput
import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
import edu.ie3.datamodel.models.{OperationTime, StandardUnits}
import edu.ie3.simona.test.common.DefaultTestData
-import edu.ie3.util.TimeUtil
import org.mockito.Mockito.when
import org.scalatestplus.mockito.MockitoSugar
import tech.units.indriya.quantity.Quantities
+import java.util.UUID
+
/** Simple test data to be used in tests for PvModel. Should be extended as
* needed.
*/
@@ -26,10 +24,6 @@ trait PvInputTestData
extends DefaultTestData
with NodeInputTestData
with MockitoSugar {
- protected implicit val simulationStartDate: ZonedDateTime =
- TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00")
- protected val simulationEndDate: ZonedDateTime =
- TimeUtil.withDefaults.toZonedDateTime("2020-01-01 01:00:00")
protected val pvInputMock: PvInput = mock[PvInput]
when(pvInputMock.getUuid)
@@ -42,7 +36,7 @@ trait PvInputTestData
when(pvInputModel04Kv.getId).thenReturn("TestPvInputModel_0.4_kV")
when(pvInputModel04Kv.getNode).thenReturn(nodeInputNoSlackNs04KvA)
- protected val pvInputModel = new PvInput(
+ protected val pvInput = new PvInput(
UUID.randomUUID(),
"Dummy_PvModel",
new OperatorInput(UUID.randomUUID(), "NO_OPERATOR"),
@@ -57,6 +51,6 @@ trait PvInputTestData
11,
false,
Quantities.getQuantity(10, StandardUnits.S_RATED),
- 0.95
+ 0.95,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/SwitchInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/SwitchInputTestData.scala
index 3bd7d0990a..138edf8219 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/SwitchInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/SwitchInputTestData.scala
@@ -26,7 +26,7 @@ trait SwitchInputTestData extends NodeInputTestData with DefaultTestData {
defaultOperationTime,
nodeInputNoSlackNs04KvA,
nodeInputNoSlackNs04KvB,
- true
+ true,
)
protected val loopSwitchInput: SwitchInput = new SwitchInput(
@@ -36,7 +36,7 @@ trait SwitchInputTestData extends NodeInputTestData with DefaultTestData {
defaultOperationTime,
nodeInputNoSlackNs04KvA,
nodeInputNoSlackNs04KvA,
- true
+ true,
)
protected val invalidSwitchInput: SwitchInput = new SwitchInput(
@@ -46,6 +46,6 @@ trait SwitchInputTestData extends NodeInputTestData with DefaultTestData {
defaultOperationTime,
nodeInputNoSlackNs04KvA,
nodeInputNoSlackMs10Kv,
- true
+ true,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/TimeSeriesTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/TimeSeriesTestData.scala
index ecbced6214..026d92c4be 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/TimeSeriesTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/TimeSeriesTestData.scala
@@ -24,18 +24,18 @@ trait TimeSeriesTestData {
new CsvIndividualTimeSeriesMetaInformation(
uuidP,
ColumnScheme.ACTIVE_POWER,
- Paths.get("its_p_" + uuidP)
+ Paths.get("its_p_" + uuidP),
)
protected val metaPq: CsvIndividualTimeSeriesMetaInformation =
new CsvIndividualTimeSeriesMetaInformation(
uuidPq,
ColumnScheme.APPARENT_POWER,
- Paths.get("its_pq_" + uuidPq)
+ Paths.get("its_pq_" + uuidPq),
)
protected val metaPqh: CsvIndividualTimeSeriesMetaInformation =
new CsvIndividualTimeSeriesMetaInformation(
uuidPqh,
ColumnScheme.APPARENT_POWER_AND_HEAT_DEMAND,
- Paths.get("its_pqh_" + uuidPqh)
+ Paths.get("its_pqh_" + uuidPqh),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/Transformer3wTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/Transformer3wTestData.scala
index 91da9daba4..9c14017bc7 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/Transformer3wTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/Transformer3wTestData.scala
@@ -12,16 +12,16 @@ import edu.ie3.datamodel.models.input.connector.{
LineInput,
SwitchInput,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
JointGridContainer,
- RawGridElements
+ RawGridElements,
}
import edu.ie3.datamodel.models.input.{
MeasurementUnitInput,
NodeInput,
- OperatorInput
+ OperatorInput,
}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.model.grid.{RefSystem, Transformer3wModel}
@@ -67,7 +67,7 @@ trait Transformer3wTestData extends DefaultTestData {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.EHV_380KV,
- 1
+ 1,
)
private val nodeB = new NodeInput(
UUID.fromString("dd037896-d2e0-4cdd-a4a1-c9e2b5e8366a"),
@@ -78,7 +78,7 @@ trait Transformer3wTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 2
+ 2,
)
private val nodeC = new NodeInput(
UUID.fromString("c838f8a5-03d4-40d3-94fa-815e1bcd0aa0"),
@@ -89,7 +89,7 @@ trait Transformer3wTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.MV_20KV,
- 3
+ 3,
)
protected val transformer3wType = new Transformer3WTypeInput(
@@ -113,7 +113,7 @@ trait Transformer3wTestData extends DefaultTestData {
Quantities.getQuantity(0d, DEGREE_GEOM),
0,
-10,
- 10
+ 10,
)
protected val transformer3wInput: Transformer3WInput = new Transformer3WInput(
@@ -127,7 +127,7 @@ trait Transformer3wTestData extends DefaultTestData {
1,
transformer3wType,
0,
- false
+ false,
)
protected val transformer3wInputPostponed: Transformer3WInput =
@@ -142,7 +142,7 @@ trait Transformer3wTestData extends DefaultTestData {
1,
transformer3wType,
0,
- false
+ false,
)
protected val transformer3wInputTapped: Transformer3WInput =
@@ -157,7 +157,7 @@ trait Transformer3wTestData extends DefaultTestData {
1,
transformer3wType,
10,
- false
+ false,
)
protected def transformerModelEhv: Transformer3wModel =
@@ -166,7 +166,7 @@ trait Transformer3wTestData extends DefaultTestData {
mainRefSystemEhv,
1,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
protected def transformerModelHv: Transformer3wModel =
@@ -175,7 +175,7 @@ trait Transformer3wTestData extends DefaultTestData {
mainRefSystemHv,
2,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
protected def transformerModelLv: Transformer3wModel =
@@ -184,7 +184,7 @@ trait Transformer3wTestData extends DefaultTestData {
mainRefSystemHv,
3,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
protected val transformer3wTestGrid: JointGridContainer = {
@@ -194,11 +194,11 @@ trait Transformer3wTestData extends DefaultTestData {
Set.empty[Transformer2WInput].asJava,
Set(transformer3wInput).asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
TestGridFactory.createJointGrid(
gridName = "transformer3WTestGrid",
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
)
}
@@ -209,128 +209,128 @@ trait Transformer3wTestData extends DefaultTestData {
-10,
Complex(0.0441648321658824, -7.07226179085529),
Complex.zero,
- Complex(-0.00662285051288235, 1.06076425571629)
+ Complex(-0.00662285051288235, 1.06076425571629),
),
(
-9,
Complex(0.0433989680242775, -6.94962141297919),
Complex.zero,
- Complex(-0.00585698637127746, 0.938123877840191)
+ Complex(-0.00585698637127746, 0.938123877840191),
),
(
-8,
Complex(0.0426592128875, -6.83116195707614),
Complex.zero,
- Complex(-0.0051172312345, 0.819664421937137)
+ Complex(-0.0051172312345, 0.819664421937137),
),
(
-7,
Complex(0.0419442540122905, -6.71667320919218),
Complex.zero,
- Complex(-0.0044022723592905, 0.705175674053178)
+ Complex(-0.0044022723592905, 0.705175674053178),
),
(
-6,
Complex(0.0412528652098901, -6.60595881563407),
Complex.zero,
- Complex(-0.00371088355689011, 0.594461280495065)
+ Complex(-0.00371088355689011, 0.594461280495065),
),
(
-5,
Complex(0.0405838998281081, -6.49883515916432),
Complex.zero,
- Complex(-0.0030419181751081, 0.487337624025323)
+ Complex(-0.0030419181751081, 0.487337624025323),
),
(
-4,
Complex(0.0399362844053192, -6.39513034279468),
Complex.zero,
- Complex(-0.00239430275231915, 0.383632807655681)
+ Complex(-0.00239430275231915, 0.383632807655681),
),
(
-3,
Complex(0.0393090129225131, -6.29468326934764),
Complex.zero,
- Complex(-0.00176703126951309, 0.283185734208644)
+ Complex(-0.00176703126951309, 0.283185734208644),
),
(
-2,
Complex(0.0387011415886598, -6.19734280641959),
Complex.zero,
- Complex(-0.0011591599356598, 0.185845271280588)
+ Complex(-0.0011591599356598, 0.185845271280588),
),
(
-1,
Complex(0.0381117841025381, -6.10296702764162),
Complex.zero,
- Complex(-0.000569802449538069, 0.091469492502624)
+ Complex(-0.000569802449538069, 0.091469492502624),
),
(
0,
Complex(0.037540107341, -6.011422522227),
Complex.zero,
- Complex(0.000001874312, -0.000075012912)
+ Complex(0.000001874312, -0.000075012912),
),
(
1,
Complex(0.0369853274295567, -5.92258376574089),
Complex.zero,
- Complex(0.000556654223443345, -0.0889137693981125)
+ Complex(0.000556654223443345, -0.0889137693981125),
),
(
2,
Complex(0.0364467061563107, -5.83633254585146),
Complex.zero,
- Complex(0.00109527549668932, -0.175164989287544)
+ Complex(0.00109527549668932, -0.175164989287544),
),
(
3,
Complex(0.0359235476947368, -5.7525574375378),
Complex.zero,
- Complex(0.00161843395826315, -0.2589400976012)
+ Complex(0.00161843395826315, -0.2589400976012),
),
(
4,
Complex(0.035415195604717, -5.67115332285566),
Complex.zero,
- Complex(0.00212678604828302, -0.34034421228334)
+ Complex(0.00212678604828302, -0.34034421228334),
),
(
5,
Complex(0.0349210300846512, -5.59202095090884),
Complex.zero,
- Complex(0.00262095156834884, -0.419476584230163)
+ Complex(0.00262095156834884, -0.419476584230163),
),
(
6,
Complex(0.0344404654504587, -5.51506653415321),
Complex.zero,
- Complex(0.00310151620254129, -0.496431000985789)
+ Complex(0.00310151620254129, -0.496431000985789),
),
(
7,
Complex(0.0339729478199095, -5.440201377581),
Complex.zero,
- Complex(0.0035690338330905, -0.571296157558004)
+ Complex(0.0035690338330905, -0.571296157558004),
),
(
8,
Complex(0.0335179529830357, -5.36734153770268),
Complex.zero,
- Complex(0.00402402866996429, -0.644155997436322)
+ Complex(0.00402402866996429, -0.644155997436322),
),
(
9,
Complex(0.0330749844414097, -5.29640750857004),
Complex.zero,
- Complex(0.00446699721159031, -0.715090026568956)
+ Complex(0.00446699721159031, -0.715090026568956),
),
(
10,
Complex(0.0326435716008696, -5.22732393237131),
Complex.zero,
- Complex(0.00489841005213043, -0.784173602767695)
- )
+ Complex(0.00489841005213043, -0.784173602767695),
+ ),
)
val tapDependentVoltRatioEhv: TableFor2[Int, String] = Table(
@@ -355,6 +355,6 @@ trait Transformer3wTestData extends DefaultTestData {
(7, "1.105"),
(8, "1.12"),
(9, "1.135"),
- (10, "1.15")
+ (10, "1.15"),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/TransformerInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/TransformerInputTestData.scala
index a99035aaca..0ddd591c9e 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/TransformerInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/TransformerInputTestData.scala
@@ -12,16 +12,16 @@ import edu.ie3.datamodel.models.input.connector.{
LineInput,
SwitchInput,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
JointGridContainer,
- RawGridElements
+ RawGridElements,
}
import edu.ie3.datamodel.models.input.{
MeasurementUnitInput,
NodeInput,
- OperatorInput
+ OperatorInput,
}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.test.common.DefaultTestData
@@ -55,7 +55,7 @@ trait TransformerInputTestData extends DefaultTestData {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
private val nodeB = new NodeInput(
UUID.fromString("d46ac046-70c0-478f-8ab1-92d70f0ba172"),
@@ -66,7 +66,7 @@ trait TransformerInputTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.MV_10KV,
- 2
+ 2,
)
protected val transformerType = new Transformer2WTypeInput(
@@ -84,7 +84,7 @@ trait TransformerInputTestData extends DefaultTestData {
false,
0,
-13,
- 13
+ 13,
)
val transformerInput = new Transformer2WInput(
@@ -97,7 +97,7 @@ trait TransformerInputTestData extends DefaultTestData {
1,
transformerType,
10,
- false
+ false,
)
protected val gridContainer: JointGridContainer = {
@@ -107,11 +107,11 @@ trait TransformerInputTestData extends DefaultTestData {
Set(transformerInput).asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
TestGridFactory.createJointGrid(
gridName = "twoWindingTestGrid",
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/WecInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/WecInputTestData.scala
index f3923c370d..f5b41a9a5e 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/WecInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/WecInputTestData.scala
@@ -26,7 +26,7 @@ trait WecInputTestData extends WecTypeInputTestData {
nodeInputNoSlackNs04KvB,
CosPhiFixed.CONSTANT_CHARACTERISTIC,
wecTypeInputEnerconE82,
- false
+ false,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/input/WecTypeInputTestData.scala b/src/test/scala/edu/ie3/simona/test/common/input/WecTypeInputTestData.scala
index 1f2a1a54bf..7e272db12d 100644
--- a/src/test/scala/edu/ie3/simona/test/common/input/WecTypeInputTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/input/WecTypeInputTestData.scala
@@ -40,7 +40,7 @@ trait WecTypeInputTestData extends DefaultTestData with NodeInputTestData {
),
Quantities.getQuantity(15, StandardUnits.EFFICIENCY),
Quantities.getQuantity(5281, StandardUnits.ROTOR_AREA),
- Quantities.getQuantity(98, StandardUnits.HUB_HEIGHT)
+ Quantities.getQuantity(98, StandardUnits.HUB_HEIGHT),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGrid.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGrid.scala
index dc05ed3cda..959a3074f7 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGrid.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGrid.scala
@@ -9,7 +9,7 @@ package edu.ie3.simona.test.common.model.grid
import edu.ie3.simona.model.grid.{
NodeModel,
TransformerModel,
- TransformerTappingModel
+ TransformerTappingModel,
}
import edu.ie3.simona.test.common.DefaultTestData
import edu.ie3.util.quantities.PowerSystemUnits._
@@ -67,7 +67,7 @@ trait BasicGrid extends FiveLinesWithNodes with DefaultTestData {
"node0",
"5f2b9b3e-faa6-493b-a6ee-22a4a516ad0e",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
// / create transformer HV node @ 110kV
def node6: NodeModel =
@@ -75,7 +75,7 @@ trait BasicGrid extends FiveLinesWithNodes with DefaultTestData {
"node6",
"3d2d3626-5043-4ec7-892d-cead983c046e",
true,
- transformerHvVoltLvl
+ transformerHvVoltLvl,
)
override protected def nodes: Seq[NodeModel] =
@@ -95,7 +95,7 @@ trait BasicGrid extends FiveLinesWithNodes with DefaultTestData {
13,
-13,
0,
- autoTap = true
+ autoTap = true,
)
// / electric params in pu
@@ -129,7 +129,7 @@ trait BasicGrid extends FiveLinesWithNodes with DefaultTestData {
transformerRInPu,
transformerXInPu,
transformerGInPu,
- transformerBInPu
+ transformerBInPu,
)
// init transformer tapping
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGridWithSwitches.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGridWithSwitches.scala
index c9270e0888..bd709daa01 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGridWithSwitches.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/BasicGridWithSwitches.scala
@@ -6,13 +6,14 @@
package edu.ie3.simona.test.common.model.grid
+import edu.ie3.simona.model.control.GridControls
import edu.ie3.simona.model.grid.GridModel.GridComponents
import edu.ie3.simona.model.grid.{
GridModel,
LineModel,
NodeModel,
SwitchModel,
- Transformer3wModel
+ Transformer3wModel,
}
import edu.ie3.util.quantities.PowerSystemUnits._
import tech.units.indriya.quantity.Quantities
@@ -47,42 +48,42 @@ trait BasicGridWithSwitches extends BasicGrid {
"node13",
"69f08e1d-725d-4bae-80c3-5b5a472493c9",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node14: NodeModel =
_nodeCreator(
"node14",
"c09cb11f-4e2c-4871-84c6-a22dc6702679",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node15: NodeModel =
_nodeCreator(
"node15",
"2b45e1e2-591e-49c1-bcbe-0e4ceed79c9b",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node16: NodeModel =
_nodeCreator(
"node16",
"8aec5998-9c8a-453d-8556-e8630f4c053a",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node17: NodeModel =
_nodeCreator(
"node17",
"e1002827-0430-4ba0-950f-8107fefc09fa",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node18: NodeModel =
_nodeCreator(
"node18",
"4ab4904e-dde3-4591-8eb5-3e0ca4fd8e3d",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
// add nodes to nodes list
@@ -97,7 +98,7 @@ trait BasicGridWithSwitches extends BasicGrid {
node15.uuid -> 9,
node16.uuid -> 10,
node17.uuid -> 11,
- node18.uuid -> 12
+ node18.uuid -> 12,
)
// rebuild lines
@@ -109,7 +110,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.0013109999999999999, PU),
Quantities.getQuantity(0.0010680000000000002, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.0000048375, PU)
+ Quantities.getQuantity(0.0000048375, PU),
)
def line18To1: LineModel = _lineCreator(
"line18To1",
@@ -119,7 +120,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.0013109999999999999, PU),
Quantities.getQuantity(0.0010680000000000002, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.0000048375, PU)
+ Quantities.getQuantity(0.0000048375, PU),
)
def line1To13: LineModel = _lineCreator(
"line1To13",
@@ -129,7 +130,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.001748, PU),
Quantities.getQuantity(0.001424, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.00000645, PU)
+ Quantities.getQuantity(0.00000645, PU),
)
def line14To2: LineModel = _lineCreator(
"line14To2",
@@ -139,7 +140,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.001748, PU),
Quantities.getQuantity(0.001424, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.00000645, PU)
+ Quantities.getQuantity(0.00000645, PU),
)
def line0To15: LineModel = _lineCreator(
"line0To15",
@@ -149,7 +150,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.000874, PU),
Quantities.getQuantity(0.000712, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.000003225, PU)
+ Quantities.getQuantity(0.000003225, PU),
)
def line16To3: LineModel = _lineCreator(
"line16To3",
@@ -159,7 +160,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.000874, PU),
Quantities.getQuantity(0.000712, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.000003225, PU)
+ Quantities.getQuantity(0.000003225, PU),
)
def line2To3: LineModel = _lineCreator(
@@ -170,7 +171,7 @@ trait BasicGridWithSwitches extends BasicGrid {
Quantities.getQuantity(0.0013109999999999999, PU),
Quantities.getQuantity(0.0010680000000000002, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.0000048375, PU)
+ Quantities.getQuantity(0.0000048375, PU),
)
override protected val lines: Set[LineModel] = Set(
@@ -182,7 +183,7 @@ trait BasicGridWithSwitches extends BasicGrid {
line16To3,
line3To4,
line3To5,
- line2To3
+ line2To3,
)
// switches
@@ -191,21 +192,21 @@ trait BasicGridWithSwitches extends BasicGrid {
"TestSwitch1",
defaultOperationInterval,
node13.uuid,
- node14.uuid
+ node14.uuid,
)
val switch2 = new SwitchModel(
UUID.fromString("e9eb5598-1611-46ad-a44f-fde689c3f558"),
"TestSwitch2",
defaultOperationInterval,
node15.uuid,
- node16.uuid
+ node16.uuid,
)
val switch3 = new SwitchModel(
UUID.fromString("01731a4a-7801-4656-96c9-26d002ff52da"),
"TestSwitch3",
defaultOperationInterval,
node17.uuid,
- node18.uuid
+ node18.uuid,
)
def switches: Set[SwitchModel] = Set(switch1, switch2, switch3)
@@ -228,8 +229,9 @@ trait BasicGridWithSwitches extends BasicGrid {
gridLines,
Set(transformer2wModel),
Set.empty[Transformer3wModel],
- gridSwitches
- )
+ gridSwitches,
+ ),
+ GridControls.empty,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGrid.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGrid.scala
index 6a91877773..07a2ef7f8f 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGrid.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGrid.scala
@@ -11,14 +11,14 @@ import edu.ie3.datamodel.models.OperationTime
import edu.ie3.datamodel.models.input.connector._
import edu.ie3.datamodel.models.input.connector.`type`.{
LineTypeInput,
- Transformer2WTypeInput
+ Transformer2WTypeInput,
}
import edu.ie3.datamodel.models.input.container.RawGridElements
import edu.ie3.datamodel.models.input.system.characteristic.OlmCharacteristicInput
import edu.ie3.datamodel.models.input.{
MeasurementUnitInput,
NodeInput,
- OperatorInput
+ OperatorInput,
}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.datamodel.utils.GridAndGeoUtils
@@ -58,7 +58,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
protected val node2 = new NodeInput(
UUID.fromString("e364ef00-e6ca-46b1-ba2b-bb73c0c6fee0"),
@@ -69,7 +69,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
protected val node3 = new NodeInput(
UUID.fromString("47ef9983-8fcf-4713-be90-093fc27864ae"),
@@ -80,7 +80,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
protected val node4 = new NodeInput(
UUID.fromString("d44ba8ed-81db-4a22-a40d-f7c0d0808a75"),
@@ -91,7 +91,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
protected val supNodeA = new NodeInput(
UUID.fromString("9fe5fa33-6d3b-4153-a829-a16f4347bc4e"),
@@ -102,7 +102,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.EHV_380KV,
- 1000
+ 1000,
)
protected val supNodeB = new NodeInput(
UUID.fromString("fb4272fa-5a31-4218-9a46-0a37ac5b34a4"),
@@ -113,7 +113,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.EHV_380KV,
- 1000
+ 1000,
)
/* Mocking table of nodes of underlying grids
@@ -133,7 +133,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
Quantities.getQuantity(0.1094, OHM_PER_KILOMETRE),
Quantities.getQuantity(0.4, OHM_PER_KILOMETRE),
Quantities.getQuantity(680.0, AMPERE),
- Quantities.getQuantity(110.0, KILOVOLT)
+ Quantities.getQuantity(110.0, KILOVOLT),
)
protected val lineType2 = new LineTypeInput(
@@ -144,7 +144,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
Quantities.getQuantity(0.0283, OHM_PER_KILOMETRE),
Quantities.getQuantity(0.11, OHM_PER_KILOMETRE),
Quantities.getQuantity(800.0, AMPERE),
- Quantities.getQuantity(110.0, KILOVOLT)
+ Quantities.getQuantity(110.0, KILOVOLT),
)
protected val lineType3 = new LineTypeInput(
@@ -155,7 +155,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
Quantities.getQuantity(0.0547, OHM_PER_KILOMETRE),
Quantities.getQuantity(0.4, OHM_PER_KILOMETRE),
Quantities.getQuantity(1360.0, AMPERE),
- Quantities.getQuantity(110.0, KILOVOLT)
+ Quantities.getQuantity(110.0, KILOVOLT),
)
protected val lineType4 = new LineTypeInput(
@@ -166,7 +166,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
Quantities.getQuantity(0.109999999403954, OHM_PER_KILOMETRE),
Quantities.getQuantity(0.379999995231628, OHM_PER_KILOMETRE),
Quantities.getQuantity(550.0, AMPERE),
- Quantities.getQuantity(110.0, KILOVOLT)
+ Quantities.getQuantity(110.0, KILOVOLT),
)
protected val line3To4 = new LineInput(
@@ -180,7 +180,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
lineType1,
Quantities.getQuantity(20, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(node3, node4),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val line2To3 = new LineInput(
@@ -194,7 +194,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
lineType2,
Quantities.getQuantity(20.0, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(node3, node2),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val line1To2 = new LineInput(
@@ -208,7 +208,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
lineType3,
Quantities.getQuantity(24.0, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(node1, node2),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val line1To3 = new LineInput(
@@ -222,7 +222,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
lineType4,
Quantities.getQuantity(40, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(node1, node3),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
protected val line1To4 = new LineInput(
@@ -236,7 +236,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
lineType2,
Quantities.getQuantity(30, KILOMETRE),
GridAndGeoUtils.buildSafeLineStringBetweenNodes(node4, node1),
- OlmCharacteristicInput.CONSTANT_CHARACTERISTIC
+ OlmCharacteristicInput.CONSTANT_CHARACTERISTIC,
)
// 1 transformer from HS to HöS
@@ -255,7 +255,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
false,
0,
-5,
- 5
+ 5,
)
protected val transformer1 = new Transformer2WInput(
@@ -268,7 +268,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
1,
trafoType,
0,
- false
+ false,
)
protected val transformer2 = new Transformer2WInput(
UUID.fromString("ceccd8cb-29dc-45d6-8a13-4b0033c5f1ef"),
@@ -280,7 +280,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
1,
trafoType,
0,
- false
+ false,
)
protected val (hvGridContainer, hvSubGridGates) = {
@@ -296,7 +296,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
transformers.asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
/* Sub grid gates are the apparent gates to superior grids + artificial one to underlying grids */
@@ -306,42 +306,42 @@ trait DbfsTestGrid extends SubGridGateMokka {
) ++ rawGridElements.getTransformer3Ws.asScala.flatMap(transformer =>
Seq(
SubGridGate.fromTransformer3W(transformer, ConnectorPort.B),
- SubGridGate.fromTransformer3W(transformer, ConnectorPort.C)
+ SubGridGate.fromTransformer3W(transformer, ConnectorPort.C),
)
) ++ Seq(
build2wSubGridGate(
node4.getUuid,
1,
UUID.fromString("1129b00d-3d89-4a4a-8ae1-2a56041b95aa"),
- 13
+ 13,
),
build2wSubGridGate(
node2.getUuid,
1,
UUID.fromString("139c435d-e550-48d8-b590-ee897621f42a"),
- 12
+ 12,
),
build2wSubGridGate(
node1.getUuid,
1,
UUID.fromString("1676e48c-5353-4f06-b671-c579cf6a7072"),
- 11
+ 11,
),
build2wSubGridGate(
node3.getUuid,
1,
UUID.fromString("9237e237-01e9-446f-899f-c3b5cf69d288"),
- 13
- )
+ 13,
+ ),
)
(
TestGridFactory.createSubGrid(
gridName = "centerGrid",
subgrid = 1,
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
),
- subGridGates
+ subGridGates,
)
}
@@ -357,7 +357,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
transformers.asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
/* Sub grid gates are the apparent gates to superior grids + artificial one to underlying grids */
@@ -367,14 +367,14 @@ trait DbfsTestGrid extends SubGridGateMokka {
) ++ rawGridElements.getTransformer3Ws.asScala.flatMap(transformer =>
Seq(
SubGridGate.fromTransformer3W(transformer, ConnectorPort.B),
- SubGridGate.fromTransformer3W(transformer, ConnectorPort.C)
+ SubGridGate.fromTransformer3W(transformer, ConnectorPort.C),
)
) ++ Seq(
build2wSubGridGate(
node1.getUuid,
1,
UUID.fromString("1676e48c-5353-4f06-b671-c579cf6a7072"),
- 11
+ 11,
)
)
@@ -382,9 +382,9 @@ trait DbfsTestGrid extends SubGridGateMokka {
TestGridFactory.createSubGrid(
gridName = "centerGrid",
subgrid = 1,
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
),
- subGridGates
+ subGridGates,
)
}
@@ -396,7 +396,7 @@ trait DbfsTestGrid extends SubGridGateMokka {
Set.empty[Transformer2WInput].asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
val subGridGates: Seq[SubGridGate] =
@@ -408,9 +408,9 @@ trait DbfsTestGrid extends SubGridGateMokka {
TestGridFactory.createSubGrid(
gridName = "superiorGrid",
subgrid = 1000,
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
),
- subGridGates
+ subGridGates,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGridWithParticipants.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGridWithParticipants.scala
index eb0208adbd..3e2445ecc1 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGridWithParticipants.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/DbfsTestGridWithParticipants.scala
@@ -12,14 +12,14 @@ import edu.ie3.datamodel.models.input.connector._
import edu.ie3.datamodel.models.input.connector.`type`.Transformer2WTypeInput
import edu.ie3.datamodel.models.input.container.{
RawGridElements,
- SystemParticipants
+ SystemParticipants,
}
import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed
import edu.ie3.datamodel.models.input.system._
import edu.ie3.datamodel.models.input.{
MeasurementUnitInput,
NodeInput,
- OperatorInput
+ OperatorInput,
}
import edu.ie3.datamodel.models.profile.LoadProfile
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
@@ -48,7 +48,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.HV,
- 1
+ 1,
)
protected val supNodeA = new NodeInput(
@@ -60,7 +60,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.EHV_380KV,
- 1000
+ 1000,
)
// 1 transformer from hv to ehv
@@ -79,7 +79,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
false,
0,
-5,
- 5
+ 5,
)
private val transformer1 = new Transformer2WInput(
@@ -92,7 +92,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
1,
trafoType,
0,
- false
+ false,
)
protected val load1 = new LoadInput(
@@ -106,7 +106,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
false,
Quantities.getQuantity(300000, KILOWATTHOUR): ComparableQuantity[Energy],
Quantities.getQuantity(150, MEGAVOLTAMPERE): ComparableQuantity[Power],
- 0.9
+ 0.9,
)
protected val (hvGridContainer, hvSubGridGates) = {
@@ -121,7 +121,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
transformers.asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
val systemParticipants = new SystemParticipants(
@@ -135,7 +135,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
Set.empty[PvInput].asJava,
Set.empty[StorageInput].asJava,
Set.empty[WecInput].asJava,
- Set.empty[EmInput].asJava
+ Set.empty[EmInput].asJava,
)
/* Sub grid gates are the apparent gates to superior grids + artificial one to underlying grids */
@@ -145,7 +145,7 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
) ++ rawGridElements.getTransformer3Ws.asScala.flatMap(transformer =>
Seq(
SubGridGate.fromTransformer3W(transformer, ConnectorPort.B),
- SubGridGate.fromTransformer3W(transformer, ConnectorPort.C)
+ SubGridGate.fromTransformer3W(transformer, ConnectorPort.C),
)
)
@@ -154,9 +154,9 @@ trait DbfsTestGridWithParticipants extends SubGridGateMokka {
gridName = "gridAgentWithParticipants",
subgrid = 1,
rawGridElements = rawGridElements,
- systemParticipants = systemParticipants
+ systemParticipants = systemParticipants,
),
- subGridGates
+ subGridGates,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/FiveLinesWithNodes.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/FiveLinesWithNodes.scala
index d76bd254fe..830b77f7f7 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/FiveLinesWithNodes.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/FiveLinesWithNodes.scala
@@ -51,7 +51,7 @@ trait FiveLinesWithNodes {
String,
String,
Boolean,
- ComparableQuantity[ElectricPotential]
+ ComparableQuantity[ElectricPotential],
) => NodeModel = { (nodeId, uuid, isSlack, vNominal) =>
new NodeModel(
UUID.fromString(uuid),
@@ -59,7 +59,7 @@ trait FiveLinesWithNodes {
OperationInterval(0L, 7200L),
isSlack,
Each(1.0d),
- GermanVoltageLevelUtils.parse(vNominal)
+ GermanVoltageLevelUtils.parse(vNominal),
)
}
@@ -71,7 +71,7 @@ trait FiveLinesWithNodes {
Quantity[Dimensionless],
Quantity[Dimensionless],
Quantity[Dimensionless],
- Quantity[Dimensionless]
+ Quantity[Dimensionless],
) => LineModel = { (lineId, uuid, nodeA, nodeB, r, x, g, b) =>
new LineModel(
UUID.fromString(uuid),
@@ -84,7 +84,7 @@ trait FiveLinesWithNodes {
Each(r.getValue.doubleValue()),
Each(x.getValue.doubleValue()),
Each(g.getValue.doubleValue()),
- Each(b.getValue.doubleValue())
+ Each(b.getValue.doubleValue()),
)
}
@@ -93,42 +93,42 @@ trait FiveLinesWithNodes {
"node0",
"51c03963-f28b-4892-9053-c6bb58d20a45",
true,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node1: NodeModel =
_nodeCreator(
"node1",
"890fb76c-2c6c-4eea-a47d-cf0244750718",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node2: NodeModel =
_nodeCreator(
"node2",
"be77fa50-613e-4fc9-854a-cfb694443e2f",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node3: NodeModel =
_nodeCreator(
"node3",
"9a41fd03-fb9a-4966-925e-d847a28ca97d",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node4: NodeModel =
_nodeCreator(
"node4",
"7f058275-476a-4d84-b1fa-12381204ac4f",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
def node5: NodeModel =
_nodeCreator(
"node5",
"ea93feca-0947-4869-a961-9cf942143feb",
false,
- linesRatedVoltage
+ linesRatedVoltage,
)
protected def nodes: Seq[NodeModel] =
@@ -142,7 +142,7 @@ trait FiveLinesWithNodes {
Quantities.getQuantity(0.0013109999999999999, PU),
Quantities.getQuantity(0.0010680000000000002, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.0000048375, PU)
+ Quantities.getQuantity(0.0000048375, PU),
)
val line1To2: LineModel = _lineCreator(
@@ -153,7 +153,7 @@ trait FiveLinesWithNodes {
Quantities.getQuantity(0.001748, PU),
Quantities.getQuantity(0.001424, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.00000645, PU)
+ Quantities.getQuantity(0.00000645, PU),
)
val line0To3: LineModel = _lineCreator(
@@ -164,7 +164,7 @@ trait FiveLinesWithNodes {
Quantities.getQuantity(0.000874, PU),
Quantities.getQuantity(0.000712, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.000003225, PU)
+ Quantities.getQuantity(0.000003225, PU),
)
val line3To4: LineModel = _lineCreator(
@@ -175,7 +175,7 @@ trait FiveLinesWithNodes {
Quantities.getQuantity(0.000437, PU),
Quantities.getQuantity(0.000356, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.0000016125, PU)
+ Quantities.getQuantity(0.0000016125, PU),
)
val line3To5: LineModel = _lineCreator(
@@ -186,7 +186,7 @@ trait FiveLinesWithNodes {
Quantities.getQuantity(0.002185, PU),
Quantities.getQuantity(0.00178, PU),
Quantities.getQuantity(0, PU),
- Quantities.getQuantity(0.0000080625, PU)
+ Quantities.getQuantity(0.0000080625, PU),
)
protected val lines: Set[LineModel] =
@@ -200,7 +200,7 @@ trait FiveLinesWithNodes {
node2.uuid -> 2,
node3.uuid -> 3,
node4.uuid -> 4,
- node5.uuid -> 5
+ node5.uuid -> 5,
)
// corresponding admittance matrix
@@ -211,7 +211,7 @@ trait FiveLinesWithNodes {
C.zero,
C(-687.7449206024457, 560.2681733054249),
C.zero,
- C.zero
+ C.zero,
),
(
C(-458.4966137349637, 373.5121155369499),
@@ -219,7 +219,7 @@ trait FiveLinesWithNodes {
C(-343.8724603012229, 280.1340866527124),
C.zero,
C.zero,
- C.zero
+ C.zero,
),
(
C.zero,
@@ -227,7 +227,7 @@ trait FiveLinesWithNodes {
C(343.8724603012229, -280.1340834277124),
C.zero,
C.zero,
- C.zero
+ C.zero,
),
(
C(-687.7449206024457, 560.2681733054249),
@@ -235,7 +235,7 @@ trait FiveLinesWithNodes {
C.zero,
C(2338.3327300483156, -1904.9117827884445),
C(-1375.4898412048915, 1120.5363466108497),
- C(-275.0979682409783, 224.10726932216994)
+ C(-275.0979682409783, 224.10726932216994),
),
(
C.zero,
@@ -243,7 +243,7 @@ trait FiveLinesWithNodes {
C.zero,
C(-1375.4898412048915, 1120.5363466108497),
C(1375.4898412048915, -1120.5363458045997),
- C.zero
+ C.zero,
),
(
C.zero,
@@ -251,8 +251,8 @@ trait FiveLinesWithNodes {
C.zero,
C(-275.0979682409783, 224.10726932216994),
C.zero,
- C(275.0979682409783, -224.10726529091994)
- )
+ C(275.0979682409783, -224.10726529091994),
+ ),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/SubGridGateMokka.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/SubGridGateMokka.scala
index 19f3966a23..6cf1b682d4 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/SubGridGateMokka.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/SubGridGateMokka.scala
@@ -12,7 +12,7 @@ import edu.ie3.datamodel.models.input.NodeInput
import edu.ie3.datamodel.models.input.connector.{
ConnectorPort,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import org.mockito.Mockito._
import org.scalatestplus.mockito.MockitoSugar
@@ -49,7 +49,7 @@ trait SubGridGateMokka extends MockitoSugar {
*/
protected def mockTransformer2w(
nodeA: NodeInput,
- nodeB: NodeInput
+ nodeB: NodeInput,
): Transformer2WInput = {
val transformer = mock[Transformer2WInput]
when(transformer.getNodeA).thenReturn(nodeA)
@@ -75,7 +75,7 @@ trait SubGridGateMokka extends MockitoSugar {
nodeA: NodeInput,
nodeASubnet: Int,
nodeB: NodeInput,
- nodeC: NodeInput
+ nodeC: NodeInput,
): Transformer3WInput = {
val internalNode = mock[NodeInput]
when(internalNode.getUuid).thenReturn(UUID.randomUUID())
@@ -106,7 +106,7 @@ trait SubGridGateMokka extends MockitoSugar {
nodeAUuid: UUID,
subGridA: Int,
nodeBUuud: UUID,
- subGridB: Int
+ subGridB: Int,
): SubGridGate = {
val nodeA = mockNode(nodeAUuid, subGridA)
val nodeB = mockNode(nodeBUuud, subGridB)
@@ -140,7 +140,7 @@ trait SubGridGateMokka extends MockitoSugar {
subGridB: Int,
nodeCUuid: UUID,
subGridC: Int,
- inferiorPort: ConnectorPort
+ inferiorPort: ConnectorPort,
): SubGridGate = {
val nodeA = mockNode(nodeAUuid, subGridA)
val nodeB = mockNode(nodeBUuid, subGridB)
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/TapTestData.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/TapTestData.scala
index b8949dda1a..b2ede4ea43 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/TapTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/TapTestData.scala
@@ -20,295 +20,295 @@ trait TapTestData extends TransformerTestGrid {
-10,
Complex(15.208258743, -50.248897222),
Complex(5.069419581, -16.752965741),
- Complex(-3.802064686, 12.560349306)
+ Complex(-3.802064686, 12.560349306),
),
(
ConnectorPort.A,
-9,
Complex(14.717669751, -48.627965054),
Complex(4.272871863, -14.120918054),
- Complex(-3.311475694, 10.939417137)
+ Complex(-3.311475694, 10.939417137),
),
(
ConnectorPort.A,
-8,
Complex(14.257742572, -47.108341146),
Complex(3.564435643, -11.780014974),
- Complex(-2.851548514, 9.419793229)
+ Complex(-2.851548514, 9.419793229),
),
(
ConnectorPort.A,
-7,
Complex(13.825689766, -45.680815656),
Complex(2.932722072, -9.692624809),
- Complex(-2.419495709, 7.992267740)
+ Complex(-2.419495709, 7.992267740),
),
(
ConnectorPort.A,
-6,
Complex(13.419051832, -44.337262255),
Complex(2.368067970, -7.826817907),
- Complex(-2.012857775, 6.648714338)
+ Complex(-2.012857775, 6.648714338),
),
(
ConnectorPort.A,
-5,
Complex(13.035650351, -43.070483333),
Complex(1.862235764, -6.155375170),
- Complex(-1.629456294, 5.381935417)
+ Complex(-1.629456294, 5.381935417),
),
(
ConnectorPort.A,
-4,
Complex(12.673548952, -41.874081018),
Complex(1.408172106, -4.654990484),
- Complex(-1.267354895, 4.185533102)
+ Complex(-1.267354895, 4.185533102),
),
(
ConnectorPort.A,
-3,
Complex(12.331020602, -40.742349099),
Complex(0.999812481, -3.305625091),
- Complex(-0.924826545, 3.053801182)
+ Complex(-0.924826545, 3.053801182),
),
(
ConnectorPort.A,
-2,
Complex(12.006520060, -39.670182017),
Complex(0.631922108, -2.089981879),
- Complex(-0.600326003, 1.981634101)
+ Complex(-0.600326003, 1.981634101),
),
(
ConnectorPort.A,
-1,
Complex(11.698660571, -38.652997863),
Complex(0.299965656, -0.993074896),
- Complex(-0.292466514, 0.964449947)
+ Complex(-0.292466514, 0.964449947),
),
(
ConnectorPort.A,
0,
Complex(11.406194057, -37.686672917),
Complex(0.000000000, -0.001875000),
- Complex(0.000000000, -0.001875000)
+ Complex(0.000000000, -0.001875000),
),
(
ConnectorPort.A,
1,
Complex(11.127994202, -36.767485772),
Complex(-0.271414493, 0.894983294),
- Complex(0.278199855, -0.921062144)
+ Complex(0.278199855, -0.921062144),
),
(
ConnectorPort.A,
2,
Complex(10.863041959, -35.892069444),
Complex(-0.517287712, 1.707445484),
- Complex(0.543152098, -1.796478472)
+ Complex(0.543152098, -1.796478472),
),
(
ConnectorPort.A,
3,
Complex(10.610413076, -35.057370155),
Complex(-0.740261377, 2.444240535),
- Complex(0.795780981, -2.631177762)
+ Complex(0.795780981, -2.631177762),
),
(
ConnectorPort.A,
4,
Complex(10.369267325, -34.260611742),
Complex(-0.942660666, 3.113051481),
- Complex(1.036926732, -3.427936174)
+ Complex(1.036926732, -3.427936174),
),
(
ConnectorPort.A,
5,
Complex(10.138839162, -33.499264815),
Complex(-1.126537685, 3.720659053),
- Complex(1.267354895, -4.189283102)
+ Complex(1.267354895, -4.189283102),
),
(
ConnectorPort.A,
6,
Complex(9.918429615, -32.771019927),
Complex(-1.293708211, 4.273063091),
- Complex(1.487764442, -4.917527989)
+ Complex(1.487764442, -4.917527989),
),
(
ConnectorPort.A,
7,
Complex(9.707399198, -32.073764184),
Complex(-1.445782859, 4.775585521),
- Complex(1.698794860, -5.614783732)
+ Complex(1.698794860, -5.614783732),
),
(
ConnectorPort.A,
8,
Complex(9.505161714, -31.405560764),
Complex(-1.584193619, 5.232958044),
- Complex(1.901032343, -6.282987153)
+ Complex(1.901032343, -6.282987153),
),
(
ConnectorPort.A,
9,
Complex(9.311178822, -30.764630952),
Complex(-1.710216518, 5.649397022),
- Complex(2.095015235, -6.923916964)
+ Complex(2.095015235, -6.923916964),
),
(
ConnectorPort.A,
10,
Complex(9.124955246, -30.149338333),
Complex(-1.824991049, 6.028667667),
- Complex(2.281238811, -7.539209583)
+ Complex(2.281238811, -7.539209583),
),
(
ConnectorPort.B,
-10,
Complex(15.208258743, -50.248897222),
Complex(-3.802064686, 12.560349306),
- Complex(5.069419581, -16.752965741)
+ Complex(5.069419581, -16.752965741),
),
(
ConnectorPort.B,
-9,
Complex(14.717669751, -48.627965054),
Complex(-3.311475694, 10.939417137),
- Complex(4.272871863, -14.120918054)
+ Complex(4.272871863, -14.120918054),
),
(
ConnectorPort.B,
-8,
Complex(14.257742572, -47.108341146),
Complex(-2.851548514, 9.419793229),
- Complex(3.564435643, -11.780014974)
+ Complex(3.564435643, -11.780014974),
),
(
ConnectorPort.B,
-7,
Complex(13.825689766, -45.680815656),
Complex(-2.419495709, 7.992267740),
- Complex(2.932722072, -9.692624809)
+ Complex(2.932722072, -9.692624809),
),
(
ConnectorPort.B,
-6,
Complex(13.419051832, -44.337262255),
Complex(-2.012857775, 6.648714338),
- Complex(2.368067970, -7.826817907)
+ Complex(2.368067970, -7.826817907),
),
(
ConnectorPort.B,
-5,
Complex(13.035650351, -43.070483333),
Complex(-1.629456294, 5.381935417),
- Complex(1.862235764, -6.155375170)
+ Complex(1.862235764, -6.155375170),
),
(
ConnectorPort.B,
-4,
Complex(12.673548952, -41.874081018),
Complex(-1.267354895, 4.185533102),
- Complex(1.408172106, -4.654990484)
+ Complex(1.408172106, -4.654990484),
),
(
ConnectorPort.B,
-3,
Complex(12.331020602, -40.742349099),
Complex(-0.924826545, 3.053801182),
- Complex(0.999812481, -3.305625091)
+ Complex(0.999812481, -3.305625091),
),
(
ConnectorPort.B,
-2,
Complex(12.006520060, -39.670182017),
Complex(-0.600326003, 1.981634101),
- Complex(0.631922108, -2.089981879)
+ Complex(0.631922108, -2.089981879),
),
(
ConnectorPort.B,
-1,
Complex(11.698660571, -38.652997863),
Complex(-0.292466514, 0.964449947),
- Complex(0.299965656, -0.993074896)
+ Complex(0.299965656, -0.993074896),
),
(
ConnectorPort.B,
0,
Complex(11.406194057, -37.686672917),
Complex(0.000000000, -0.001875000),
- Complex(0.000000000, -0.001875000)
+ Complex(0.000000000, -0.001875000),
),
(
ConnectorPort.B,
1,
Complex(11.127994202, -36.767485772),
Complex(0.278199855, -0.921062144),
- Complex(-0.271414493, 0.894983294)
+ Complex(-0.271414493, 0.894983294),
),
(
ConnectorPort.B,
2,
Complex(10.863041959, -35.892069444),
Complex(0.543152098, -1.796478472),
- Complex(-0.517287712, 1.707445484)
+ Complex(-0.517287712, 1.707445484),
),
(
ConnectorPort.B,
3,
Complex(10.610413076, -35.057370155),
Complex(0.795780981, -2.631177762),
- Complex(-0.740261377, 2.444240535)
+ Complex(-0.740261377, 2.444240535),
),
(
ConnectorPort.B,
4,
Complex(10.369267325, -34.260611742),
Complex(1.036926732, -3.427936174),
- Complex(-0.942660666, 3.113051481)
+ Complex(-0.942660666, 3.113051481),
),
(
ConnectorPort.B,
5,
Complex(10.138839162, -33.499264815),
Complex(1.267354895, -4.189283102),
- Complex(-1.126537685, 3.720659053)
+ Complex(-1.126537685, 3.720659053),
),
(
ConnectorPort.B,
6,
Complex(9.918429615, -32.771019927),
Complex(1.487764442, -4.917527989),
- Complex(-1.293708211, 4.273063091)
+ Complex(-1.293708211, 4.273063091),
),
(
ConnectorPort.B,
7,
Complex(9.707399198, -32.073764184),
Complex(1.698794860, -5.614783732),
- Complex(-1.445782859, 4.775585521)
+ Complex(-1.445782859, 4.775585521),
),
(
ConnectorPort.B,
8,
Complex(9.505161714, -31.405560764),
Complex(1.901032343, -6.282987153),
- Complex(-1.584193619, 5.232958044)
+ Complex(-1.584193619, 5.232958044),
),
(
ConnectorPort.B,
9,
Complex(9.311178822, -30.764630952),
Complex(2.095015235, -6.923916964),
- Complex(-1.710216518, 5.649397022)
+ Complex(-1.710216518, 5.649397022),
),
(
ConnectorPort.B,
10,
Complex(9.124955246, -30.149338333),
Complex(2.281238811, -7.539209583),
- Complex(-1.824991049, 6.028667667)
- )
+ Complex(-1.824991049, 6.028667667),
+ ),
)
val tapDependentVoltRatio: TableFor2[Int, String] = Table(
@@ -339,6 +339,6 @@ trait TapTestData extends TransformerTestGrid {
(10, "12.65000"),
(11, "12.81500"),
(12, "12.98000"),
- (13, "13.14500")
+ (13, "13.14500"),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestData.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestData.scala
index c5211f1b19..26f26cc141 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestData.scala
@@ -31,7 +31,7 @@ trait TransformerTestData extends TransformerTestGrid {
val refSystem: RefSystem =
RefSystem(
Kilowatts(400d),
- Kilovolts(0.4d)
+ Kilovolts(0.4d),
)
val nodeUuidToIndexMap: Map[UUID, Int] = gridTapHv.getRawGrid.getNodes.asScala
@@ -46,581 +46,581 @@ trait TransformerTestData extends TransformerTestGrid {
-10,
BigDecimal("-1"),
1.33851987070328,
- 0.0182494793139813
+ 0.0182494793139813,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.9"),
1.33801926371388,
- 0.0164263707976911
+ 0.0164263707976911,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.8"),
1.3375133288976,
- 0.0146032622079102
+ 0.0146032622079102,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.7"),
1.33700205419089,
- 0.0127801535444722
+ 0.0127801535444722,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.6"),
1.33648542736584,
- 0.0109570448072086
+ 0.0109570448072086,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.5"),
1.33596343602858,
- 0.00913393599594799
+ 0.00913393599594799,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.4"),
1.33543606761768,
- 0.00731082711051762
+ 0.00731082711051762,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.3"),
1.33490330940244,
- 0.00548771815074156
+ 0.00548771815074156,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.2"),
1.33436514848123,
- 0.00366460911644194
+ 0.00366460911644194,
),
(
ConnectorPort.A,
-10,
BigDecimal("-0.1"),
1.33382157177974,
- 0.00184150000743841
+ 0.00184150000743841,
),
(
ConnectorPort.A,
-10,
BigDecimal("0"),
1.33327256604926,
- 0.0000183908235482318
+ 0.0000183908235482318,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.1"),
1.33271811786481,
- -0.00180471843541367
+ -0.00180471843541367,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.2"),
1.33215821362343,
- -0.0036278277696351
+ -0.0036278277696351,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.3"),
1.33159283954222,
- -0.00545093717930617
+ -0.00545093717930617,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.4"),
1.33102198165651,
- -0.00727404666461941
+ -0.00727404666461941,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.5"),
1.3304456258179,
- -0.00909715622576998
+ -0.00909715622576998,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.6"),
1.32986375769233,
- -0.0109202658629557
+ -0.0109202658629557,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.7"),
1.32927636275805,
- -0.0127433755763768
+ -0.0127433755763768,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.8"),
1.3286834263036,
- -0.0145664853662362
+ -0.0145664853662362,
),
(
ConnectorPort.A,
-10,
BigDecimal("0.9"),
1.32808493342574,
- -0.0163895952327395
+ -0.0163895952327395,
),
(
ConnectorPort.A,
-10,
BigDecimal("1"),
1.32748086902734,
- -0.0182127051760949
+ -0.0182127051760949,
),
(
ConnectorPort.A,
-9,
BigDecimal("-1"),
1.29566723975357,
- 0.0188565887526757
+ 0.0188565887526757,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.9"),
1.29515348415399,
- 0.0169727100013154
+ 0.0169727100013154,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.8"),
1.29463385801716,
- 0.0150888311689785
+ 0.0150888311689785,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.7"),
1.29410834724102,
- 0.0132049522554703
+ 0.0132049522554703,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.6"),
1.29357693751783,
- 0.0113210732605935
+ 0.0113210732605935,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.5"),
1.29303961433205,
- 0.00943719418414793
+ 0.00943719418414793,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.4"),
1.29249636295816,
- 0.00755331502593053
+ 0.00755331502593053,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.3"),
1.29194716845844,
- 0.00566943578573499
+ 0.00566943578573499,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.2"),
1.29139201568068,
- 0.00378555646335253
+ 0.00378555646335253,
),
(
ConnectorPort.A,
-9,
BigDecimal("-0.1"),
1.29083088925591,
- 0.00190167705857116
+ 0.00190167705857116,
),
(
ConnectorPort.A,
-9,
BigDecimal("0"),
1.29026377359605,
- 0.0000177975711757692
+ 0.0000177975711757692,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.1"),
1.28969065289147,
- -0.00186608199905174
+ -0.00186608199905174,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.2"),
1.28911151110854,
- -0.00374996165233242
+ -0.00374996165233242,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.3"),
1.28852633198721,
- -0.00563384138889061
+ -0.00563384138889061,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.4"),
1.28793509903837,
- -0.00751772120895373
+ -0.00751772120895373,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.5"),
1.2873377955413,
- -0.00940160111275256
+ -0.00940160111275256,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.6"),
1.28673440454104,
- -0.0112854811005208
+ -0.0112854811005208,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.7"),
1.28612490884563,
- -0.0131693611724957
+ -0.0131693611724957,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.8"),
1.28550929102345,
- -0.0150532413289176
+ -0.0150532413289176,
),
(
ConnectorPort.A,
-9,
BigDecimal("0.9"),
1.2848875334003,
- -0.0169371215700303
+ -0.0169371215700303,
),
(
ConnectorPort.A,
-9,
BigDecimal("1"),
1.28425961805663,
- -0.0188210018960812
+ -0.0188210018960812,
),
(
ConnectorPort.A,
-8,
BigDecimal("-1"),
1.25550083502857,
- 0.0194637352444932
+ 0.0194637352444932,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.9"),
1.25497427336787,
- 0.0175190862627879
+ 0.0175190862627879,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.8"),
1.25444126357415,
- 0.0155744371921388
+ 0.0155744371921388,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.7"),
1.25390178925247,
- 0.0136297880323194
+ 0.0136297880323194,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.6"),
1.25335583375242,
- 0.0116851387831005
+ 0.0116851387831005,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.5"),
1.2528033801653,
- 0.00974048944424847
+ 0.00974048944424847,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.4"),
1.25224441132123,
- 0.00779584001552676
+ 0.00779584001552676,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.3"),
1.25167890978624,
- 0.00585119049669465
+ 0.00585119049669465,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.2"),
1.25110685785927,
- 0.00390654088750816
+ 0.00390654088750816,
),
(
ConnectorPort.A,
-8,
BigDecimal("-0.1"),
1.25052823756908,
- 0.00196189118771932
+ 0.00196189118771932,
),
(
ConnectorPort.A,
-8,
BigDecimal("0"),
1.24994303067118,
- 0.0000172413970765007
+ 0.0000172413970765007,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.1"),
1.2493512186446,
- -0.00192740848467577
+ -0.00192740848467577,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.2"),
1.24875278268866,
- -0.00387205845779691
+ -0.00387205845779691,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.3"),
1.24814770371962,
- -0.00581670852255001
+ -0.00581670852255001,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.4"),
1.24753596236732,
- -0.00776135867920239
+ -0.00776135867920239,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.5"),
1.24691753897167,
- -0.00970600892802513
+ -0.00970600892802513,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.6"),
1.24629241357918,
- -0.0116506592692935
+ -0.0116506592692935,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.7"),
1.24566056593925,
- -0.0135953097032869
+ -0.0135953097032869,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.8"),
1.24502197550058,
- -0.0155399602302888
+ -0.0155399602302888,
),
(
ConnectorPort.A,
-8,
BigDecimal("0.9"),
1.24437662140734,
- -0.0174846108505867
+ -0.0174846108505867,
),
(
ConnectorPort.A,
-8,
BigDecimal("1"),
1.24372448249533,
- -0.0194292615644728
+ -0.0194292615644728,
),
(
ConnectorPort.A,
-7,
BigDecimal("-1"),
1.21777623277092,
- 0.0200709154179146
+ 0.0200709154179146,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.9"),
1.21723721781891,
- 0.0180654962107302
+ 0.0180654962107302,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.8"),
1.21669114127808,
- 0.0160600769061402
+ 0.0160600769061402,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.7"),
1.21613798418596,
- 0.0140546575038827
+ 0.0140546575038827,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.6"),
1.21557772726497,
- 0.0120492380036921
+ 0.0120492380036921,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.5"),
1.21501035091869,
- 0.0100438184052979
+ 0.0100438184052979,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.4"),
1.21443583522813,
- 0.00803839870842536
+ 0.00803839870842536,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.3"),
1.2138541599479,
- 0.00603297891279538
+ 0.00603297891279538,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.2"),
1.21326530450224,
- 0.00402755901812385
+ 0.00402755901812385,
),
(
ConnectorPort.A,
-7,
BigDecimal("-0.1"),
1.21266924798103,
- 0.00202213902412263
+ 0.00202213902412263,
),
(
ConnectorPort.A,
-7,
BigDecimal("0"),
1.21206596913569,
- 0.0000167189304984079
+ 0.0000167189304984079,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.1"),
1.21145544637497,
- -0.00198870126304644
+ -0.00198870126304644,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.2"),
1.21083765776068,
- -0.00399412155681467
+ -0.00399412155681467,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.3"),
1.21021258100326,
- -0.00599954195111372
+ -0.00599954195111372,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.4"),
1.20958019345734,
- -0.00800496244625593
+ -0.00800496244625593,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.5"),
1.20894047211713,
- -0.0100103830425586
+ -0.0100103830425586,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.6"),
1.20829339361172,
- -0.0120158037403442
+ -0.0120158037403442,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.7"),
1.20763893420033,
- -0.0140212245399401
+ -0.0140212245399401,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.8"),
1.20697706976735,
- -0.016026645441679
+ -0.016026645441679,
),
(
ConnectorPort.A,
-7,
BigDecimal("0.9"),
1.20630777581736,
- -0.0180320664458987
+ -0.0180320664458987,
),
(ConnectorPort.A, -7, BigDecimal("1"), 1.20563102747, -0.0200374875529425),
(
@@ -628,147 +628,147 @@ trait TransformerTestData extends TransformerTestGrid {
-6,
BigDecimal("-1"),
1.18227775868577,
- 0.0206781262979832
+ 0.0206781262979832,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.9"),
1.18172665336576,
- 0.0186119368703259
+ 0.0186119368703259,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.8"),
1.18116783617761,
- 0.0165457473362931
+ 0.0165457473362931,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.7"),
1.18060128529589,
- 0.0144795576955837
+ 0.0144795576955837,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.6"),
1.18002697850897,
- 0.0124133679478913
+ 0.0124133679478913,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.5"),
1.17944489321428,
- 0.010347178092904
+ 0.010347178092904,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.4"),
1.17885500641342,
- 0.00828098813030484
+ 0.00828098813030484,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.3"),
1.17825729470715,
- 0.00621479805977096
+ 0.00621479805977096,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.2"),
1.17765173429035,
- 0.00414860788097387
+ 0.00414860788097387,
),
(
ConnectorPort.A,
-6,
BigDecimal("-0.1"),
1.17703830094672,
- 0.00208241759357962
+ 0.00208241759357962,
),
(
ConnectorPort.A,
-6,
BigDecimal("0"),
1.17641697004346,
- 0.0000162271972485072
+ 0.0000162271972485072,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.1"),
1.17578771652579,
- -0.00204996330836518
+ -0.00204996330836518,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.2"),
1.17515051491134,
- -0.00411615392361289
+ -0.00411615392361289,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.3"),
1.17450533928438,
- -0.00618234464885208
+ -0.00618234464885208,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.4"),
1.17385216328999,
- -0.00824853548444636
+ -0.00824853548444636,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.5"),
1.173190960128,
- -0.0103147264307653
+ -0.0103147264307653,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.6"),
1.17252170254683,
- -0.0123809174881849
+ -0.0123809174881849,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.7"),
1.17184436283718,
- -0.0144471086570874
+ -0.0144471086570874,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.8"),
1.17115891282558,
- -0.0165132999378612
+ -0.0165132999378612,
),
(
ConnectorPort.A,
-6,
BigDecimal("0.9"),
1.17046532386777,
- -0.0185794913309016
+ -0.0185794913309016,
),
(
ConnectorPort.A,
-6,
BigDecimal("1"),
1.16976356684192,
- -0.0206456828366103
+ -0.0206456828366103,
),
(ConnectorPort.A, -5, BigDecimal("-1"), 1.1488143809165, 0.021285365249654),
(
@@ -776,280 +776,280 @@ trait TransformerTestData extends TransformerTestGrid {
-5,
BigDecimal("-0.9"),
1.14825155823767,
- 0.0191584056066691
+ 0.0191584056066691,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.8"),
1.14768033563545,
- 0.0170314458478176
+ 0.0170314458478176,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.7"),
1.14710068810469,
- 0.0149044859727549
+ 0.0149044859727549,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.6"),
1.14651259016991,
- 0.0127775259811294
+ 0.0127775259811294,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.5"),
1.14591601587918,
- 0.0106505658725832
+ 0.0106505658725832,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.4"),
1.14531093879785,
- 0.00852360564675163
+ 0.00852360564675163,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.3"),
1.14469733200211,
- 0.00639664530326336
+ 0.00639664530326336,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.2"),
1.14407516807242,
- 0.00426968484174022
+ 0.00426968484174022,
),
(
ConnectorPort.A,
-5,
BigDecimal("-0.1"),
1.14344441908675,
- 0.00214272426179696
+ 0.00214272426179696,
),
(
ConnectorPort.A,
-5,
BigDecimal("0"),
1.14280505661365,
- 0.0000157635630414583
+ 0.0000157635630414583,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.1"),
1.14215705170516,
- -0.00211119725492569
+ -0.00211119725492569,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.2"),
1.14150037488952,
- -0.00423815819251083
+ -0.00423815819251083,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.3"),
1.14083499616373,
- -0.00636511925012798
+ -0.00636511925012798,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.4"),
1.14016088498588,
- -0.00849208042819832
+ -0.00849208042819832,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.5"),
1.13947801026731,
- -0.0106190417271507
+ -0.0106190417271507,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.6"),
1.13878634036457,
- -0.0127460031474217
+ -0.0127460031474217,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.7"),
1.1380858430712,
- -0.0148729646894554
+ -0.0148729646894554,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.8"),
1.13737648560922,
- -0.016999926353704
+ -0.016999926353704,
),
(
ConnectorPort.A,
-5,
BigDecimal("0.9"),
1.13665823462052,
- -0.0191268881406277
+ -0.0191268881406277,
),
(
ConnectorPort.A,
-5,
BigDecimal("1"),
1.13593105615792,
- -0.0212538500506947
+ -0.0212538500506947,
),
(
ConnectorPort.A,
-4,
BigDecimal("-1"),
1.11721628752459,
- 0.0218926299305844
+ 0.0218926299305844,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.9"),
1.11664213051416,
- 0.0197049000775551
+ 0.0197049000775551,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.8"),
1.11605884680756,
- 0.0175171700986343
+ 0.0175171700986343,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.7"),
1.11546640788156,
- 0.0153294399934289
+ 0.0153294399934289,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.6"),
1.11486478464348,
- 0.0131417097615372
+ 0.0131417097615372,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.5"),
1.11425394742336,
- 0.0109539794025504
+ 0.0109539794025504,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.4"),
1.11363386596595,
- 0.00876624891605077
+ 0.00876624891605077,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.3"),
1.11300450942251,
- 0.00657851830161297
+ 0.00657851830161297,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.2"),
1.1123658463424,
- 0.00439078755880325
+ 0.00439078755880325,
),
(
ConnectorPort.A,
-4,
BigDecimal("-0.1"),
1.11171784466437,
- 0.00220305668717925
+ 0.00220305668717925,
),
(
ConnectorPort.A,
-4,
BigDecimal("0"),
1.11106047170771,
- 0.0000153256862902373
+ 0.0000153256862902373,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.1"),
1.1103936941631,
- -0.00217240544432333
+ -0.00217240544432333,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.2"),
1.10971747808323,
- -0.0043601367051296
+ -0.0043601367051296,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.3"),
1.10903178887323,
- -0.00654786809660587
+ -0.00654786809660587,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.4"),
1.10833659128073,
- -0.00873559961923841
+ -0.00873559961923841,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.5"),
1.10763184938575,
- -0.0109233312735225
+ -0.0109233312735225,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.6"),
1.10691752659028,
- -0.0131110630599632
+ -0.0131110630599632,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.7"),
1.10619358560756,
- -0.0152987949790748
+ -0.0152987949790748,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.8"),
1.10545998845117,
- -0.017486527031381
+ -0.017486527031381,
),
(
ConnectorPort.A,
-4,
BigDecimal("0.9"),
1.10471669642365,
- -0.019674259217416
+ -0.019674259217416,
),
(ConnectorPort.A, -4, BigDecimal("1"), 1.103963670105, -0.0218619915377232),
(ConnectorPort.A, -3, BigDecimal("-1"), 1.08733201897279, 0.02249991825158),
@@ -1058,3815 +1058,3815 @@ trait TransformerTestData extends TransformerTestGrid {
-3,
BigDecimal("-0.9"),
1.08674692060804,
- 0.0202514181939269
+ 0.0202514181939269,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.8"),
1.08615192912595,
- 0.0180029179998106
+ 0.0180029179998106,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.7"),
1.08554701212492,
- 0.0157544176687841
+ 0.0157544176687841,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.6"),
1.08493213651765,
- 0.0135059172003913
+ 0.0135059172003913,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.5"),
1.08430726852129,
- 0.011257416594166
+ 0.011257416594166,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.4"),
1.08367237364723,
- 0.00900891584963238
+ 0.00900891584963238,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.3"),
1.08302741669076,
- 0.00676041496630505
+ 0.00676041496630505,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.2"),
1.08237236172029,
- 0.0045119139436882
+ 0.0045119139436882,
),
(
ConnectorPort.A,
-3,
BigDecimal("-0.1"),
1.08170717206634,
- 0.00226341278127613
+ 0.00226341278127613,
),
(
ConnectorPort.A,
-3,
BigDecimal("0"),
1.08103181031021,
- 0.0000149114785526847
+ 0.0000149114785526847,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.1"),
1.08034623827232,
- -0.00223358996500881
+ -0.00223358996500881,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.2"),
1.07965041700025,
- -0.00448209154994563
+ -0.00448209154994563,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.3"),
1.07894430675637,
- -0.006730593276806
+ -0.006730593276806,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.4"),
1.07822786700521,
- -0.00897909514614891
+ -0.00897909514614891,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.5"),
1.07750105640042,
- -0.0112275971585447
+ -0.0112275971585447,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.6"),
1.07676383277138,
- -0.0134760993145751
+ -0.0134760993145751,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.7"),
1.0760161531094,
- -0.0157246016148335
+ -0.0157246016148335,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.8"),
1.07525797355358,
- -0.0179731040599247
+ -0.0179731040599247,
),
(
ConnectorPort.A,
-3,
BigDecimal("0.9"),
1.07448924937621,
- -0.0202216066504656
+ -0.0202216066504656,
),
(
ConnectorPort.A,
-3,
BigDecimal("1"),
1.0737099349678,
- -0.0224701093870857
+ -0.0224701093870857,
),
(
ConnectorPort.A,
-2,
BigDecimal("-1"),
1.05902605337401,
- 0.023107228343286
+ 0.023107228343286,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.9"),
1.05843041651441,
- 0.0207979580865661
+ 0.0207979580865661,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.8"),
1.05782407954748,
- 0.0184886876822516
+ 0.0184886876822516,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.7"),
1.05720700581082,
- 0.0161794171298368
+ 0.0161794171298368,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.6"),
1.05657915782069,
- 0.0138701464288047
+ 0.0138701464288047,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.5"),
1.05594049725962,
- 0.0115608755786267
+ 0.0115608755786267,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.4"),
1.05529098496362,
- 0.00925160457876267
+ 0.00925160457876267,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.3"),
1.05463058090905,
- 0.00694233342866044
+ 0.00694233342866044,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.2"),
1.0539592441991,
- 0.00463306212775601
+ 0.00463306212775601,
),
(
ConnectorPort.A,
-2,
BigDecimal("-0.1"),
1.0532769330498,
- 0.00232379067547291
+ 0.00232379067547291,
),
(
ConnectorPort.A,
-2,
BigDecimal("0"),
1.05258360477573,
- 0.0000145190712223228
+ 0.0000145190712223228,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.1"),
1.05187921577518,
- -0.00229475268559709
+ -0.00229475268559709,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.2"),
1.05116372151496,
- -0.00460402459559959
+ -0.00460402459559959,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.3"),
1.05043707651473,
- -0.00691329665941254
+ -0.00691329665941254,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.4"),
1.0496992343308,
- -0.00922256887767665
+ -0.00922256887767665,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.5"),
1.04895014753959,
- -0.011531841251046
+ -0.011531841251046,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.6"),
1.04818976772044,
- -0.0138411137801886
+ -0.0138411137801886,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.7"),
1.04741804543799,
- -0.0161503864657862
+ -0.0161503864657862,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.8"),
1.04663493022402,
- -0.018459659308535
+ -0.018459659308535,
),
(
ConnectorPort.A,
-2,
BigDecimal("0.9"),
1.04584037055874,
- -0.0207689323091456
+ -0.0207689323091456,
),
(
ConnectorPort.A,
-2,
BigDecimal("1"),
1.0450343138515,
- -0.0230782054683436
+ -0.0230782054683436,
),
(
ConnectorPort.A,
-1,
BigDecimal("-1"),
1.03217676324027,
- 0.0237145585280039
+ 0.0237145585280039,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.9"),
1.03157100055975,
- 0.0213445180779095
+ 0.0213445180779095,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.8"),
1.03095368930288,
- 0.0189744774685169
+ 0.0189744774685169,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.7"),
1.03032478814181,
- 0.0166044366992562
+ 0.0166044366992562,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.6"),
1.02968425476971,
- 0.0142343957695436
+ 0.0142343957695436,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.5"),
1.0290320458854,
- 0.0118643546787821
+ 0.0118643546787821,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.4"),
1.02836811717731,
- 0.00949431342636016
+ 0.00949431342636016,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.3"),
1.02769242330705,
- 0.00712427201165266
+ 0.00712427201165266,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.2"),
1.02700491789239,
- 0.00475423043401966
+ 0.00475423043401966,
),
(
ConnectorPort.A,
-1,
BigDecimal("-0.1"),
1.0263055534898,
- 0.00238418869280686
+ 0.00238418869280686,
),
(
ConnectorPort.A,
-1,
BigDecimal("0"),
1.02559428157635,
- 0.0000141467873448151
+ 0.0000141467873448151,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.1"),
1.02487105253106,
- -0.00235589528305081
+ -0.00235589528305081,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.2"),
1.02413581561573,
- -0.00472593751908002
+ -0.00472593751908002,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.3"),
1.02338851895507,
- -0.00709597992145828
+ -0.00709597992145828,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.4"),
1.0226291095163,
- -0.00946602249091709
+ -0.00946602249091709,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.5"),
1.02185753308803,
- -0.0118360652282043
+ -0.0118360652282043,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.6"),
1.02107373425853,
- -0.0142061081340842
+ -0.0142061081340842,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.7"),
1.02027765639326,
- -0.016576151209338
+ -0.016576151209338,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.8"),
1.01946924161175,
- -0.0189461944547642
+ -0.0189461944547642,
),
(
ConnectorPort.A,
-1,
BigDecimal("0.9"),
1.01864843076367,
- -0.0213162378711784
+ -0.0213162378711784,
),
(
ConnectorPort.A,
-1,
BigDecimal("1"),
1.01781516340425,
- -0.0236862814594147
+ -0.0236862814594147,
),
(
ConnectorPort.A,
0,
BigDecimal("-1"),
1.00667467871911,
- 0.0243219072957343
+ 0.0243219072957343,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.9"),
1.00605921263868,
- 0.0218910966580921
+ 0.0218910966580921,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.8"),
1.00543130713357,
- 0.0194602858488637
+ 0.0194602858488637,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.7"),
1.0047909157835,
- 0.0170294748674087
+ 0.0170294748674087,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.6"),
1.00413799100698,
- 0.0145986637130708
+ 0.0145986637130708,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.5"),
1.00347248404206,
- 0.0121678523851774
+ 0.0121678523851774,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.4"),
1.00279434492653,
- 0.00973704088303931
+ 0.00973704088303931,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.3"),
1.00210352247738,
- 0.00730622920595051
+ 0.00730622920595051,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.2"),
1.00139996426967,
- 0.00487541735318787
+ 0.00487541735318787,
),
(
ConnectorPort.A,
0,
BigDecimal("-0.1"),
1.00068361661459,
- 0.00244460532401091
+ 0.00244460532401091,
),
(
ConnectorPort.A,
0,
BigDecimal("0"),
0.999954424536941,
- 0.0000137931176612275
+ 0.0000137931176612275,
),
(
ConnectorPort.A,
0,
BigDecimal("0.1"),
0.999212331751719,
- -0.00241701926663761
+ -0.00241701926663761,
),
(
ConnectorPort.A,
0,
BigDecimal("0.2"),
0.99845728064004,
- -0.00484783182968026
+ -0.00484783182968026,
),
(
ConnectorPort.A,
0,
BigDecimal("0.3"),
0.997689212224214,
- -0.00727864457228031
+ -0.00727864457228031,
),
(
ConnectorPort.A,
0,
BigDecimal("0.4"),
0.996908066142007,
- -0.00970945749527035
+ -0.00970945749527035,
),
(
ConnectorPort.A,
0,
BigDecimal("0.5"),
0.996113780620051,
- -0.0121402705995025
+ -0.0121402705995025,
),
(
ConnectorPort.A,
0,
BigDecimal("0.6"),
0.995306292446364,
- -0.0145710838858487
+ -0.0145710838858487,
),
(
ConnectorPort.A,
0,
BigDecimal("0.7"),
0.994485536941961,
- -0.0170018973552011
+ -0.0170018973552011,
),
(
ConnectorPort.A,
0,
BigDecimal("0.8"),
0.993651447931513,
- -0.0194327110084725
+ -0.0194327110084725,
),
(
ConnectorPort.A,
0,
BigDecimal("0.9"),
0.99280395771303,
- -0.0218635248465967
+ -0.0218635248465967,
),
(
ConnectorPort.A,
0,
BigDecimal("1"),
0.99194299702652,
- -0.0242943388705291
+ -0.0242943388705291,
),
(
ConnectorPort.A,
1,
BigDecimal("-1"),
0.98242100499016,
- 0.0249292732837263
+ 0.0249292732837263,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.9"),
0.981796267611054,
- 0.0224376924644968
+ 0.0224376924644968,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.8"),
0.981158156688823,
- 0.019946111460796
+ 0.019946111460796,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.7"),
0.980506620261764,
- 0.0174545302719071
+ 0.0174545302719071,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.6"),
0.979841604996861,
- 0.0149629488970946
+ 0.0149629488970946,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.5"),
0.979163056166092,
- 0.0124713673356035
+ 0.0124713673356035,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.4"),
0.978470917621914,
- 0.00997978558665933
+ 0.00997978558665933,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.3"),
0.977765131771884,
- 0.00748820364946775
+ 0.00748820364946775,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.2"),
0.977045639552417,
- 0.00499662152321397
+ 0.00499662152321397,
),
(
ConnectorPort.A,
1,
BigDecimal("-0.1"),
0.976312380401615,
- 0.00250503920706258
+ 0.00250503920706258,
),
(
ConnectorPort.A,
1,
BigDecimal("0"),
0.975565292231162,
- 0.0000134567001572926
+ 0.0000134567001572926,
),
(
ConnectorPort.A,
1,
BigDecimal("0.1"),
0.974804311397244,
- -0.00247812599838001
+ -0.00247812599838001,
),
(
ConnectorPort.A,
1,
BigDecimal("0.2"),
0.974029372670451,
- -0.0049697088894488
+ -0.0049697088894488,
),
(
ConnectorPort.A,
1,
BigDecimal("0.3"),
0.973240409204641,
- -0.00746129197397136
+ -0.00746129197397136,
),
(
ConnectorPort.A,
1,
BigDecimal("0.4"),
0.972437352504711,
- -0.00995287525289237
+ -0.00995287525289237,
),
(
ConnectorPort.A,
1,
BigDecimal("0.5"),
0.971620132393254,
- -0.0124444587271799
+ -0.0124444587271799,
),
(
ConnectorPort.A,
1,
BigDecimal("0.6"),
0.97078867697604,
- -0.0149360423978257
+ -0.0149360423978257,
),
(
ConnectorPort.A,
1,
BigDecimal("0.7"),
0.969942912606292,
- -0.0174276262658455
+ -0.0174276262658455,
),
(
ConnectorPort.A,
1,
BigDecimal("0.8"),
0.969082763847711,
- -0.0199192103322799
+ -0.0199192103322799,
),
(
ConnectorPort.A,
1,
BigDecimal("0.9"),
0.968208153436193,
- -0.0224107945981947
+ -0.0224107945981947,
),
(
ConnectorPort.A,
1,
BigDecimal("1"),
0.967319002240194,
- -0.0249023790646814
+ -0.0249023790646814,
),
(
ConnectorPort.A,
2,
BigDecimal("-1"),
0.959326351461916,
- 0.025536655258949
+ 0.025536655258949,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.9"),
0.958692784499275,
- 0.022984304264225
+ 0.022984304264225,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.8"),
0.958044865723424,
- 0.0204319530715357
+ 0.0204319530715357,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.7"),
0.957382537160275,
- 0.0178796016800814
+ 0.0178796016800814,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.6"),
0.956705739223261,
- 0.0153272500890402
+ 0.0153272500890402,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.5"),
0.956014410684269,
- 0.0127748982975676
+ 0.0127748982975676,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.4"),
0.955308488643518,
- 0.0102225463047959
+ 0.0102225463047959,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.3"),
0.954587908498331,
- 0.0076701941098341
+ 0.0076701941098341,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.2"),
0.953852603910776,
- 0.00511784171176699
+ 0.00511784171176699,
),
(
ConnectorPort.A,
2,
BigDecimal("-0.1"),
0.953102506774115,
- 0.00256548910965531
+ 0.00256548910965531,
),
(
ConnectorPort.A,
2,
BigDecimal("0"),
0.952337547178039,
- 0.0000131363025345216
+ 0.0000131363025345216,
),
(
ConnectorPort.A,
2,
BigDecimal("0.1"),
0.951557653372631,
- -0.00253921671058493
+ -0.00253921671058493,
),
(
ConnectorPort.A,
2,
BigDecimal("0.2"),
0.950762751731016,
- -0.00509156993071863
+ -0.00509156993071863,
),
(
ConnectorPort.A,
2,
BigDecimal("0.3"),
0.949952766710642,
- -0.00764392335890866
+ -0.00764392335890866,
),
(
ConnectorPort.A,
2,
BigDecimal("0.4"),
0.949127620813153,
- -0.0101962769962239
+ -0.0101962769962239,
),
(
ConnectorPort.A,
2,
BigDecimal("0.5"),
0.948287234542783,
- -0.0127486308437611
+ -0.0127486308437611,
),
(
ConnectorPort.A,
2,
BigDecimal("0.6"),
0.947431526363226,
- -0.0153009849026448
+ -0.0153009849026448,
),
(
ConnectorPort.A,
2,
BigDecimal("0.7"),
0.946560412652918,
- -0.0178533391740284
+ -0.0178533391740284,
),
(
ConnectorPort.A,
2,
BigDecimal("0.8"),
0.945673807658669,
- -0.0204056936590949
+ -0.0204056936590949,
),
(
ConnectorPort.A,
2,
BigDecimal("0.9"),
0.944771623447575,
- -0.0229580483590572
+ -0.0229580483590572,
),
(
ConnectorPort.A,
2,
BigDecimal("1"),
0.943853769857146,
- -0.0255104032751589
+ -0.0255104032751589,
),
(
ConnectorPort.A,
3,
BigDecimal("-1"),
0.937309638289396,
- 0.0261440521030076
+ 0.0261440521030076,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.9"),
0.936667693006795,
- 0.0235309309390136
+ 0.0235309309390136,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.8"),
0.936010372616478,
- 0.0209178095629393
+ 0.0209178095629393,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.7"),
0.93533761263946,
- 0.0183046879738953
+ 0.0183046879738953,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.6"),
0.934649346708417,
- 0.015691566170966
+ 0.015691566170966,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.5"),
0.933945506532227,
- 0.0130784441532096
+ 0.0130784441532096,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.4"),
0.933226021859156,
- 0.0104653219196569
+ 0.0104653219196569,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.3"),
0.932490820438636,
- 0.00785219946931139
+ 0.00785219946931139,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.2"),
0.931739827981568,
- 0.00523907680114834
+ 0.00523907680114834,
),
(
ConnectorPort.A,
3,
BigDecimal("-0.1"),
0.930972968119121,
- 0.00262595391411435
+ 0.00262595391411435,
),
(
ConnectorPort.A,
3,
BigDecimal("0"),
0.930190162359945,
- 0.0000128308071266819
+ 0.0000128308071266819,
),
(
ConnectorPort.A,
3,
BigDecimal("0.1"),
0.929391330045746,
- -0.00260029252092719
+ -0.00260029252092719,
),
(
ConnectorPort.A,
3,
BigDecimal("0.2"),
0.928576388305162,
- -0.00521341607119046
+ -0.00521341607119046,
),
(
ConnectorPort.A,
3,
BigDecimal("0.3"),
0.927745252005883,
- -0.00782653984483726
+ -0.00782653984483726,
),
(
ConnectorPort.A,
3,
BigDecimal("0.4"),
0.92689783370491,
- -0.010439663843074
+ -0.010439663843074,
),
(
ConnectorPort.A,
3,
BigDecimal("0.5"),
0.926034043596923,
- -0.0130527880671392
+ -0.0130527880671392,
),
(
ConnectorPort.A,
3,
BigDecimal("0.6"),
0.925153789460646,
- -0.0156659125183051
+ -0.0156659125183051,
),
(
ConnectorPort.A,
3,
BigDecimal("0.7"),
0.924256976603146,
- -0.0182790371978777
+ -0.0182790371978777,
),
(
ConnectorPort.A,
3,
BigDecimal("0.8"),
0.923343507801969,
- -0.0208921621071982
+ -0.0208921621071982,
),
(
ConnectorPort.A,
3,
BigDecimal("0.9"),
0.922413283245032,
- -0.0235052872476434
+ -0.0235052872476434,
),
(
ConnectorPort.A,
3,
BigDecimal("1"),
0.921466200468159,
- -0.0261184126206265
+ -0.0261184126206265,
),
(
ConnectorPort.A,
4,
BigDecimal("-1"),
0.916297152002557,
- 0.0267514627991171
+ 0.0267514627991171,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.9"),
0.915647289147419,
- 0.0240775714722086
+ 0.0240775714722086,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.8"),
0.914980982001204,
- 0.0214036799184718
+ 0.0214036799184718,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.7"),
0.914298159066424,
- 0.0187297881369204
+ 0.0187297881369204,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.6"),
0.913598746642793,
- 0.0160558961265377
+ 0.0160558961265377,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.5"),
0.912882668784197,
- 0.0133820038862763
+ 0.0133820038862763,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.4"),
0.912149847253943,
- 0.010708111415057
+ 0.010708111415057,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.3"),
0.911400201478191,
- 0.00803421871176822
+ 0.00803421871176822,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.2"),
0.910633648497533,
- 0.00536032577526581
+ 0.00536032577526581,
),
(
ConnectorPort.A,
4,
BigDecimal("-0.1"),
0.909850102916617,
- 0.00268643260437178
+ 0.00268643260437178,
),
(
ConnectorPort.A,
4,
BigDecimal("0"),
0.909049476851765,
- 0.0000125391978738261
+ 0.0000125391978738261,
),
(
ConnectorPort.A,
4,
BigDecimal("0.1"),
0.908231679876474,
- -0.00266135444547526
+ -0.00266135444547526,
),
(
ConnectorPort.A,
4,
BigDecimal("0.2"),
0.907396618964748,
- -0.00533524832695871
+ -0.00533524832695871,
),
(
ConnectorPort.A,
4,
BigDecimal("0.3"),
0.90654419843214,
- -0.00800914244789641
+ -0.00800914244789641,
),
(
ConnectorPort.A,
4,
BigDecimal("0.4"),
0.905674319874432,
- -0.0106830368096457
+ -0.0106830368096457,
),
(
ConnectorPort.A,
4,
BigDecimal("0.5"),
0.904786882103834,
- -0.0133569314136022
+ -0.0133569314136022,
),
(
ConnectorPort.A,
4,
BigDecimal("0.6"),
0.903881781082618,
- -0.0160308262612012
+ -0.0160308262612012,
),
(
ConnectorPort.A,
4,
BigDecimal("0.7"),
0.902958909854047,
- -0.0187047213539179
+ -0.0187047213539179,
),
(
ConnectorPort.A,
4,
BigDecimal("0.8"),
0.902018158470513,
- -0.0213786166932688
+ -0.0213786166932688,
),
(
ConnectorPort.A,
4,
BigDecimal("0.9"),
0.901059413918729,
- -0.0240525122808131
+ -0.0240525122808131,
),
(
ConnectorPort.A,
4,
BigDecimal("1"),
0.900082560041867,
- -0.0267264081181527
+ -0.0267264081181527,
),
(
ConnectorPort.A,
5,
BigDecimal("-1"),
0.896221727050347,
- 0.0273588864208129
+ 0.0273588864208129,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.9"),
0.895564416790433,
- 0.0246242249374753
+ 0.0246242249374753,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.8"),
0.894889546310647,
- 0.0218895632119166
+ 0.0218895632119166,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.7"),
0.894197036560895,
- 0.0191549012430461
+ 0.0191549012430461,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.6"),
0.893486805931015,
- 0.0164202390297385
+ 0.0164202390297385,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.5"),
0.892758770198814,
- 0.0136855765708318
+ 0.0136855765708318,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.4"),
0.892012842475918,
- 0.0109509138651274
+ 0.0109509138651274,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.3"),
0.891248933151341,
- 0.00821625091138941
+ 0.00821625091138941,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.2"),
0.890466949832678,
- 0.00548158770834329
+ 0.00548158770834329,
),
(
ConnectorPort.A,
5,
BigDecimal("-0.1"),
0.889666797284832,
- 0.00274692425467551
+ 0.00274692425467551,
),
(
ConnectorPort.A,
5,
BigDecimal("0"),
0.88884837736617,
- 0.0000122605490321726
+ 0.0000122605490321726,
),
(
ConnectorPort.A,
5,
BigDecimal("0.1"),
0.888011588961992,
- -0.00272240340998137
+ -0.00272240340998137,
),
(
ConnectorPort.A,
5,
BigDecimal("0.2"),
0.887156327915216,
- -0.0054570676238018
+ -0.0054570676238018,
),
(
ConnectorPort.A,
5,
BigDecimal("0.3"),
0.886282486954139,
- -0.00819173209390892
+ -0.00819173209390892,
),
(
ConnectorPort.A,
5,
BigDecimal("0.4"),
0.885389955617163,
- -0.0109263968218263
+ -0.0109263968218263,
),
(
ConnectorPort.A,
5,
BigDecimal("0.5"),
0.884478620174337,
- -0.0136610618091228
+ -0.0136610618091228,
),
(
ConnectorPort.A,
5,
BigDecimal("0.6"),
0.883548363545585,
- -0.0163957270574133
+ -0.0163957270574133,
),
(
ConnectorPort.A,
5,
BigDecimal("0.7"),
0.882599065215458,
- -0.0191303925683602
+ -0.0191303925683602,
),
(
ConnectorPort.A,
5,
BigDecimal("0.8"),
0.881630601144261,
- -0.0218650583436745
+ -0.0218650583436745,
),
(
ConnectorPort.A,
5,
BigDecimal("0.9"),
0.880642843675374,
- -0.024599724385117
+ -0.024599724385117,
),
(
ConnectorPort.A,
5,
BigDecimal("1"),
0.879635661438599,
- -0.0273343906944998
+ -0.0273343906944998,
),
(
ConnectorPort.A,
6,
BigDecimal("-1"),
0.877022034099252,
- 0.0279663221221337
+ 0.0279663221221337,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.9"),
0.876357755960366,
- 0.0251708904889815
+ 0.0251708904889815,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.8"),
0.875674754078128,
- 0.0223754585975588
+ 0.0223754585975588,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.7"),
0.874972941295997,
- 0.0195800264466633
+ 0.0195800264466633,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.6"),
0.874252227492712,
- 0.0167845940350519
+ 0.0167845940350519,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.5"),
0.873512519519852,
- 0.0139891613614397
+ 0.0139891613614397,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.4"),
0.872753721136625,
- 0.0111937284244993
+ 0.0111937284244993,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.3"),
0.871975732941789,
- 0.00839829522285939
+ 0.00839829522285939,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.2"),
0.871178452302574,
- 0.00560286175510445
+ 0.00560286175510445,
),
(
ConnectorPort.A,
6,
BigDecimal("-0.1"),
0.870361773280489,
- 0.00280742801977313
+ 0.00280742801977313,
),
(
ConnectorPort.A,
6,
BigDecimal("0"),
0.869525586553862,
- 0.0000119940153575949
+ 0.0000119940153575949,
),
(
ConnectorPort.A,
6,
BigDecimal("0.1"),
0.868669779336989,
- -0.00278344025969814
+ -0.00278344025969814,
),
(
ConnectorPort.A,
6,
BigDecimal("0.2"),
0.867794235295722,
- -0.00557887480699857
+ -0.00557887480699857,
),
(
ConnectorPort.A,
6,
BigDecimal("0.3"),
0.866898834459354,
- -0.00837430962819862
+ -0.00837430962819862,
),
(
ConnectorPort.A,
6,
BigDecimal("0.4"),
0.865983453128614,
- -0.0111697447250045
+ -0.0111697447250045,
),
(
ConnectorPort.A,
6,
BigDecimal("0.5"),
0.865047963779603,
- -0.0139651800991754
+ -0.0139651800991754,
),
(
ConnectorPort.A,
6,
BigDecimal("0.6"),
0.864092234963478,
- -0.0167606157525242
+ -0.0167606157525242,
),
(
ConnectorPort.A,
6,
BigDecimal("0.7"),
0.863116131201677,
- -0.0195560516869199
+ -0.0195560516869199,
),
(
ConnectorPort.A,
6,
BigDecimal("0.8"),
0.862119512876472,
- -0.0223514879042882
+ -0.0223514879042882,
),
(
ConnectorPort.A,
6,
BigDecimal("0.9"),
0.861102236116629,
- -0.0251469244066139
+ -0.0251469244066139,
),
(
ConnectorPort.A,
6,
BigDecimal("1"),
0.860064152677919,
- -0.0279423611959415
+ -0.0279423611959415,
),
(
ConnectorPort.A,
7,
BigDecimal("-1"),
0.858641959186664,
- 0.0285737691290579
+ 0.0285737691290579,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.9"),
0.857971201991724,
- 0.0257175673528338
+ 0.0257175673528338,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.8"),
0.857280509092796,
- 0.0228613653016216
+ 0.0228613653016216,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.7"),
0.856569784654187,
- 0.0200051629740995
+ 0.0200051629740995,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.6"),
0.855838929418557,
- 0.0171489603688983
+ 0.0171489603688983,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.5"),
0.855087840632218,
- 0.0142927574846006
+ 0.0142927574846006,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.4"),
0.854316411966964,
- 0.0114365543197398
+ 0.0114365543197398,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.3"),
0.853524533438308,
- 0.00858035087279871
+ 0.00858035087279871,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.2"),
0.852712091319953,
- 0.00572414714220867
+ 0.00572414714220867,
),
(
ConnectorPort.A,
7,
BigDecimal("-0.1"),
0.851878968054342,
- 0.00286794312634817
+ 0.00286794312634817,
),
(
ConnectorPort.A,
7,
BigDecimal("0"),
0.851025042159099,
- 0.0000117388235414818
+ 0.0000117388235414818,
),
(
ConnectorPort.A,
7,
BigDecimal("0.1"),
0.850150188129183,
- -0.00284446576794258
+ -0.00284446576794258,
),
(
ConnectorPort.A,
7,
BigDecimal("0.2"),
0.849254276334546,
- -0.00570067064989212
+ -0.00570067064989212,
),
(
ConnectorPort.A,
7,
BigDecimal("0.3"),
0.848337172913088,
- -0.00855687582415354
+ -0.00855687582415354,
),
(
ConnectorPort.A,
7,
BigDecimal("0.4"),
0.847398739658691,
- -0.0114130812926333
+ -0.0114130812926333,
),
(
ConnectorPort.A,
7,
BigDecimal("0.5"),
0.846438833904073,
- -0.0142692870572994
+ -0.0142692870572994,
),
(
ConnectorPort.A,
7,
BigDecimal("0.6"),
0.845457308398231,
- -0.0171254931201827
+ -0.0171254931201827,
),
(
ConnectorPort.A,
7,
BigDecimal("0.7"),
0.844454011178183,
- -0.0199816994833795
+ -0.0199816994833795,
),
(
ConnectorPort.A,
7,
BigDecimal("0.8"),
0.843428785434739,
- -0.0228379061490529
+ -0.0228379061490529,
),
(
ConnectorPort.A,
7,
BigDecimal("0.9"),
0.842381469371976,
- -0.0256941131194345
+ -0.0256941131194345,
),
(
ConnectorPort.A,
7,
BigDecimal("1"),
0.841311896060106,
- -0.0285503203968269
+ -0.0285503203968269,
),
(
ConnectorPort.A,
8,
BigDecimal("-1"),
0.841030060479342,
- 0.0291812267320097
+ 0.0291812267320097,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.9"),
0.840353322288959,
- 0.0262642548195838
+ 0.0262642548195838,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.8"),
0.83965538716051,
- 0.0233472826147727
+ 0.0233472826147727,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.7"),
0.838936149988622,
- 0.0204303101161266
+ 0.0204303101161266,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.6"),
0.838195501731806,
- 0.0175133373221414
+ 0.0175133373221414,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.5"),
0.83743332932346,
- 0.0145963642312576
+ 0.0145963642312576,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.4"),
0.836649515578583,
- 0.0116793908418586
+ 0.0116793908418586,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.3"),
0.835843939095994,
- 0.00876241715226996
+ 0.00876241715226996,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.2"),
0.835016474155855,
- 0.0058454431607575
+ 0.0058454431607575,
),
(
ConnectorPort.A,
8,
BigDecimal("-0.1"),
0.834166990612276,
- 0.00292846886552601
+ 0.00292846886552601,
),
(
ConnectorPort.A,
8,
BigDecimal("0"),
0.833295353780784,
- 0.0000114942647176662
+ 0.0000114942647176662,
),
(
ConnectorPort.A,
8,
BigDecimal("0.1"),
0.8324014243204,
- -0.00290548064358957
+ -0.00290548064358957,
),
(
ConnectorPort.A,
8,
BigDecimal("0.2"),
0.831485058110058,
- -0.00582245586138358
+ -0.00582245586138358,
),
(
ConnectorPort.A,
8,
BigDecimal("0.3"),
0.830546106119103,
- -0.00873943139072012
+ -0.00873943139072012,
),
(
ConnectorPort.A,
8,
BigDecimal("0.4"),
0.829584414271562,
- -0.0116564072337245
+ -0.0116564072337245,
),
(
ConnectorPort.A,
8,
BigDecimal("0.5"),
0.828599823303866,
- -0.0145733833925936
+ -0.0145733833925936,
),
(
ConnectorPort.A,
8,
BigDecimal("0.6"),
0.827592168615705,
- -0.0174903598695977
+ -0.0174903598695977,
),
(
ConnectorPort.A,
8,
BigDecimal("0.7"),
0.826561280113639,
- -0.0204073366670832
+ -0.0204073366670832,
),
(
ConnectorPort.A,
8,
BigDecimal("0.8"),
0.825506982047084,
- -0.0233243137874742
+ -0.0233243137874742,
),
(
ConnectorPort.A,
8,
BigDecimal("0.9"),
0.824429092836285,
- -0.0262412912332756
+ -0.0262412912332756,
),
(
ConnectorPort.A,
8,
BigDecimal("1"),
0.823327424891804,
- -0.0291582690070747
+ -0.0291582690070747,
),
(
ConnectorPort.A,
9,
BigDecimal("-1"),
0.824139091550437,
- 0.0297886942792841
+ 0.0297886942792841,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.9"),
0.823456879605168,
- 0.026810952237653
+ 0.026810952237653,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.8"),
0.822752159383568,
- 0.0238332098855486
+ 0.0238332098855486,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.7"),
0.822024815903447,
- 0.0208554672213847
+ 0.0208554672213847,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.6"),
0.821274729668807,
- 0.017877724243513
+ 0.017877724243513,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.5"),
0.820501776564291,
- 0.0148999809502215
+ 0.0148999809502215,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.4"),
0.819705827744304,
- 0.0119222373397329
+ 0.0119222373397329,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.3"),
0.818886749516568,
- 0.00894449341020311
+ 0.00894449341020311,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.2"),
0.818044403219829,
- 0.0059667491597195
+ 0.0059667491597195,
),
(
ConnectorPort.A,
9,
BigDecimal("-0.1"),
0.817178645095452,
- 0.00298900458629903
+ 0.00298900458629903,
),
(
ConnectorPort.A,
9,
BigDecimal("0"),
0.816289326152605,
- 0.0000112596878866997
+ 0.0000112596878866997,
),
(
ConnectorPort.A,
9,
BigDecimal("0.1"),
0.815376292026695,
- -0.0029664855376469
+ -0.0029664855376469,
),
(
ConnectorPort.A,
9,
BigDecimal("0.2"),
0.81443938283074,
- -0.00594423109250713
+ -0.00594423109250713,
),
(
ConnectorPort.A,
9,
BigDecimal("0.3"),
0.813478432999293,
- -0.00892197697897786
+ -0.00892197697897786,
),
(
ConnectorPort.A,
9,
BigDecimal("0.4"),
0.812493271124543,
- -0.0118997231994233
+ -0.0118997231994233,
),
(
ConnectorPort.A,
9,
BigDecimal("0.5"),
0.811483719784173,
- -0.0148774697562908
+ -0.0148774697562908,
),
(
ConnectorPort.A,
9,
BigDecimal("0.6"),
0.810449595360531,
- -0.0178552166521133
+ -0.0178552166521133,
),
(
ConnectorPort.A,
9,
BigDecimal("0.7"),
0.809390707850645,
- -0.0208329638895114
+ -0.0208329638895114,
),
(
ConnectorPort.A,
9,
BigDecimal("0.8"),
0.808306860666568,
- -0.023810711471197
+ -0.023810711471197,
),
(
ConnectorPort.A,
9,
BigDecimal("0.9"),
0.807197850425516,
- -0.0267884593999754
+ -0.0267884593999754,
),
(
ConnectorPort.A,
9,
BigDecimal("1"),
0.806063466729219,
- -0.0297662076787489
+ -0.0297662076787489,
),
(
ConnectorPort.A,
10,
BigDecimal("-1"),
0.807925581862439,
- 0.0303961711712593
+ 0.0303961711712593,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.9"),
0.80723841252684,
- 0.0273576590075458
+ 0.0273576590075458,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.8"),
0.806527372646608,
- 0.0243191465145681
+ 0.0243191465145681,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.7"),
0.805792336740328,
- 0.0212806336905953
+ 0.0212806336905953,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.6"),
0.80503317416582,
- 0.0182421205338254
+ 0.0182421205338254,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.5"),
0.80424974899546,
- 0.0152036070423834
+ 0.0152036070423834,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.4"),
0.803441919884922,
- 0.0121650932143196
+ 0.0121650932143196,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.3"),
0.802609539935054,
- 0.0091265790476078
+ 0.0091265790476078,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.2"),
0.801752456546514,
- 0.00608806454014298
+ 0.00608806454014298,
),
(
ConnectorPort.A,
10,
BigDecimal("-0.1"),
0.800870511266842,
- 0.0030495496897396
+ 0.0030495496897396,
),
(
ConnectorPort.A,
10,
BigDecimal("0"),
0.799963539629553,
- 0.0000110344941289201
+ 0.0000110344941289201,
),
(
ConnectorPort.A,
10,
BigDecimal("0.1"),
0.799031370984863,
- -0.0030274810490428
+ -0.0030274810490428,
),
(
ConnectorPort.A,
10,
BigDecimal("0.2"),
0.798073828321583,
- -0.00606599694221734
+ -0.00606599694221734,
),
(
ConnectorPort.A,
10,
BigDecimal("0.3"),
0.797090728079733,
- -0.00910451318792664
+ -0.00910451318792664,
),
(
ConnectorPort.A,
10,
BigDecimal("0.4"),
0.796081879953352,
- -0.0121430297887958
+ -0.0121430297887958,
),
(
ConnectorPort.A,
10,
BigDecimal("0.5"),
0.79504708668297,
- -0.0151815467475456
+ -0.0151815467475456,
),
(
ConnectorPort.A,
10,
BigDecimal("0.6"),
0.793986143837166,
- -0.018220064066996
+ -0.018220064066996,
),
(
ConnectorPort.A,
10,
BigDecimal("0.7"),
0.792898839582582,
- -0.021258581750069
+ -0.021258581750069,
),
(
ConnectorPort.A,
10,
BigDecimal("0.8"),
0.791784954441728,
- -0.024297099799792
+ -0.024297099799792,
),
(
ConnectorPort.A,
10,
BigDecimal("0.9"),
0.790644261037859,
- -0.0273356182193014
+ -0.0273356182193014,
),
(
ConnectorPort.A,
10,
BigDecimal("1"),
0.789476523826166,
- -0.0303741370118462
+ -0.0303741370118462,
),
(
ConnectorPort.B,
-10,
BigDecimal("-1"),
0.755006009039332,
- 0.0182414304718007
+ 0.0182414304718007,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.9"),
0.754544409479012,
- 0.0164183224935691
+ 0.0164183224935691,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.8"),
0.754073480350179,
- 0.0145952143866478
+ 0.0145952143866478,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.7"),
0.753593186837628,
- 0.0127721061505565
+ 0.0127721061505565,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.6"),
0.753103493255236,
- 0.0109489977848031
+ 0.0109489977848031,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.5"),
0.752604363031546,
- 0.00912588928888304
+ 0.00912588928888304,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.4"),
0.752095758694897,
- 0.00730278066227947
+ 0.00730278066227947,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.3"),
0.751577641858037,
- 0.00547967190446287
+ 0.00547967190446287,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.2"),
0.75104997320225,
- 0.00365656301489095
+ 0.00365656301489095,
),
(
ConnectorPort.B,
-10,
BigDecimal("-0.1"),
0.750512712460944,
- 0.00183345399300819
+ 0.00183345399300819,
),
(
ConnectorPort.B,
-10,
BigDecimal("0"),
0.749965818402706,
- 0.0000103448382458858
+ 0.0000103448382458858,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.1"),
0.749409248813789,
- -0.00181276444997821
+ -0.00181276444997821,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.2"),
0.74884296048003,
- -0.00363587387226018
+ -0.00363587387226018,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.3"),
0.74826690916816,
- -0.00545898342921021
+ -0.00545898342921021,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.4"),
0.747681049606505,
- -0.00728209312145274
+ -0.00728209312145274,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.5"),
0.747085335465038,
- -0.00910520294962691
+ -0.00910520294962691,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.6"),
0.746479719334773,
- -0.0109283129143865
+ -0.0109283129143865,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.7"),
0.74586415270647,
- -0.0127514230164008
+ -0.0127514230164008,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.8"),
0.745238585948635,
- -0.0145745332563544
+ -0.0145745332563544,
),
(
ConnectorPort.B,
-10,
BigDecimal("0.9"),
0.744602968284773,
- -0.0163976436349476
+ -0.0163976436349476,
),
(
ConnectorPort.B,
-10,
BigDecimal("1"),
0.74395724776989,
- -0.0182207541528969
+ -0.0182207541528969,
),
(
ConnectorPort.B,
-9,
BigDecimal("-1"),
0.78017287600731,
- 0.018849478154194
+ 0.018849478154194,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.9"),
0.779695889794979,
- 0.0169655999100214
+ 0.0169655999100214,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.8"),
0.779209263028518,
- 0.0150817215328694
+ 0.0150817215328694,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.7"),
0.778712959732216,
- 0.0131978430222418
+ 0.0131978430222418,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.6"),
0.77820694303041,
- 0.0113139643776299
+ 0.0113139643776299,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.5"),
0.777691175132598,
- 0.00943008559851245
+ 0.00943008559851245,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.4"),
0.77716561731806,
- 0.00754620668435543
+ 0.00754620668435543,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.3"),
0.776630229919971,
- 0.00566232763461165
+ 0.00566232763461165,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.2"),
0.776084972308991,
- 0.00377844844872068
+ 0.00377844844872068,
),
(
ConnectorPort.B,
-9,
BigDecimal("-0.1"),
0.775529802876309,
- 0.00189456912610848
+ 0.00189456912610848,
),
(
ConnectorPort.B,
-9,
BigDecimal("0"),
0.77496467901613,
- 0.0000106896661874416
+ 0.0000106896661874416,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.1"),
0.774389557107582,
- -0.00187318993164411
+ -0.00187318993164411,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.2"),
0.773804392496031,
- -0.00375706966800219
+ -0.00375706966800219,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.3"),
0.773209139473766,
- -0.00564094954351721
+ -0.00564094954351721,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.4"),
0.772603751260056,
- -0.00752482955883452
+ -0.00752482955883452,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.5"),
0.77198817998054,
- -0.00940870971461444
+ -0.00940870971461444,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.6"),
0.771362376645932,
- -0.0112925900115327
+ -0.0112925900115327,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.7"),
0.770726291130019,
- -0.0131764704502808
+ -0.0131764704502808,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.8"),
0.770079872146923,
- -0.0150603510315661
+ -0.0150603510315661,
),
(
ConnectorPort.B,
-9,
BigDecimal("0.9"),
0.769423067227599,
- -0.0169442317561124
+ -0.0169442317561124,
),
(
ConnectorPort.B,
-9,
BigDecimal("1"),
0.768755822695553,
- -0.0188281126246601
+ -0.0188281126246601,
),
(
ConnectorPort.B,
-8,
BigDecimal("-1"),
0.805339742975287,
- 0.0194575258365874
+ 0.0194575258365874,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.9"),
0.804847370110946,
- 0.0175128773264737
+ 0.0175128773264737,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.8"),
0.804345045706857,
- 0.015568228679091
+ 0.015568228679091,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.7"),
0.803832732626804,
- 0.0136235798939269
+ 0.0136235798939269,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.6"),
0.803310392805585,
- 0.0116789309704566
+ 0.0116789309704566,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.5"),
0.802777987233649,
- 0.00973428190814192
+ 0.00973428190814192,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.4"),
0.802235475941223,
- 0.00778963270643144
+ 0.00778963270643144,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.3"),
0.801682817981906,
- 0.0058449833647604
+ 0.0058449833647604,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.2"),
0.801119971415733,
- 0.00390033388255032
+ 0.00390033388255032,
),
(
ConnectorPort.B,
-8,
BigDecimal("-0.1"),
0.800546893291674,
- 0.00195568425920872
+ 0.00195568425920872,
),
(
ConnectorPort.B,
-8,
BigDecimal("0"),
0.799963539629553,
- 0.0000110344941289503
+ 0.0000110344941289503,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.1"),
0.799369865401375,
- -0.00193361541331008
+ -0.00193361541331008,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.2"),
0.798765824512032,
- -0.0038782654637442
+ -0.0038782654637442,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.3"),
0.798151369779371,
- -0.00582291565782424
+ -0.00582291565782424,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.4"),
0.797526452913606,
- -0.00776756599621625
+ -0.00776756599621625,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.5"),
0.796891024496041,
- -0.00971221647960199
+ -0.00971221647960199,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.6"),
0.796245033957091,
- -0.011656867108679
+ -0.011656867108679,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.7"),
0.795588429553568,
- -0.0136015178841608
+ -0.0136015178841608,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.8"),
0.79492115834521,
- -0.015546168806778
+ -0.015546168806778,
),
(
ConnectorPort.B,
-8,
BigDecimal("0.9"),
0.794243166170425,
- -0.0174908198772774
+ -0.0174908198772774,
),
(
ConnectorPort.B,
-8,
BigDecimal("1"),
0.793554397621216,
- -0.0194354710964233
+ -0.0194354710964233,
),
(
ConnectorPort.B,
-7,
BigDecimal("-1"),
0.830506609943265,
- 0.0200655735189807
+ 0.0200655735189807,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.9"),
0.829998850426913,
- 0.018060154742926
+ 0.018060154742926,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.8"),
0.829480828385196,
- 0.0160547358253125
+ 0.0160547358253125,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.7"),
0.828952505521391,
- 0.0140493167656122
+ 0.0140493167656122,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.6"),
0.828413842580759,
- 0.0120438975632834
+ 0.0120438975632834,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.5"),
0.827864799334701,
- 0.0100384782177713
+ 0.0100384782177713,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.4"),
0.827305334564386,
- 0.00803305872850738
+ 0.00803305872850738,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.3"),
0.82673540604384,
- 0.00602763909490915
+ 0.00602763909490915,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.2"),
0.826154970522475,
- 0.00402221931638
+ 0.00402221931638,
),
(
ConnectorPort.B,
-7,
BigDecimal("-0.1"),
0.825563983707039,
- 0.00201679939230899
+ 0.00201679939230899,
),
(
ConnectorPort.B,
-7,
BigDecimal("0"),
0.824962400242977,
- 0.0000113793220704718
+ 0.0000113793220704718,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.1"),
0.824350173695168,
- -0.00199404089497599
+ -0.00199404089497599,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.2"),
0.823727256528033,
- -0.00399946125948622
+ -0.00399946125948622,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.3"),
0.823093600084976,
- -0.00600488177213127
+ -0.00600488177213127,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.4"),
0.822449154567156,
- -0.00801030243359804
+ -0.00801030243359804,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.5"),
0.821793869011542,
- -0.0100157232445896
+ -0.0100157232445896,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.6"),
0.82112769126825,
- -0.0120211442058252
+ -0.0120211442058252,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.7"),
0.820450567977117,
- -0.0140265653180409
+ -0.0140265653180409,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.8"),
0.819762444543498,
- -0.0160319865819898
+ -0.0160319865819898,
),
(
ConnectorPort.B,
-7,
BigDecimal("0.9"),
0.81906326511325,
- -0.0180374079984423
+ -0.0180374079984423,
),
(
ConnectorPort.B,
-7,
BigDecimal("1"),
0.818352972546879,
- -0.0200428295681866
+ -0.0200428295681866,
),
(
ConnectorPort.B,
-6,
BigDecimal("-1"),
0.855673476911243,
- 0.0206736212013741
+ 0.0206736212013741,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.9"),
0.85515033074288,
- 0.0186074321593783
+ 0.0186074321593783,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.8"),
0.854616611063536,
- 0.0165412429715342
+ 0.0165412429715342,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.7"),
0.854072278415979,
- 0.0144750536372974
+ 0.0144750536372974,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.6"),
0.853517292355934,
- 0.0124088641561102
+ 0.0124088641561102,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.5"),
0.852951611435753,
- 0.0103426745274008
+ 0.0103426745274008,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.4"),
0.852375193187549,
- 0.0082764847505834
+ 0.0082764847505834,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.3"),
0.851787994105775,
- 0.00621029482505794
+ 0.00621029482505794,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.2"),
0.851189969629216,
- 0.00414410475020971
+ 0.00414410475020971,
),
(
ConnectorPort.B,
-6,
BigDecimal("-0.1"),
0.850581074122403,
- 0.00207791452540928
+ 0.00207791452540928,
),
(
ConnectorPort.B,
-6,
BigDecimal("0"),
0.8499612608564,
- 0.0000117241500120423
+ 0.0000117241500120423,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.1"),
0.849330481988961,
- -0.00205446637664193
+ -0.00205446637664193,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.2"),
0.848688688544034,
- -0.0041206570552282
+ -0.0041206570552282,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.3"),
0.848035830390582,
- -0.00618684788643825
+ -0.00618684788643825,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.4"),
0.847371856220706,
- -0.00825303887097983
+ -0.00825303887097983,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.5"),
0.846696713527044,
- -0.0103192300095771
+ -0.0103192300095771,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.6"),
0.84601034857941,
- -0.0123854213029714
+ -0.0123854213029714,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.7"),
0.845312706400666,
- -0.0144516127519209
+ -0.0144516127519209,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.8"),
0.844603730741786,
- -0.0165178043572016
+ -0.0165178043572016,
),
(
ConnectorPort.B,
-6,
BigDecimal("0.9"),
0.843883364056076,
- -0.0185839961196072
+ -0.0185839961196072,
),
(
ConnectorPort.B,
-6,
BigDecimal("1"),
0.843151547472542,
- -0.0206501880399498
+ -0.0206501880399498,
),
(
ConnectorPort.B,
-5,
BigDecimal("-1"),
0.88084034387922,
- 0.0212816688837675
+ 0.0212816688837675,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.9"),
0.880301811058847,
- 0.0191547095758306
+ 0.0191547095758306,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.8"),
0.879752393741875,
- 0.0170277501177557
+ 0.0170277501177557,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.7"),
0.879192051310566,
- 0.0149007905089826
+ 0.0149007905089826,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.6"),
0.878620742131108,
- 0.0127738307489369
+ 0.0127738307489369,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.5"),
0.878038423536804,
- 0.0106468708370302
+ 0.0106468708370302,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.4"),
0.877445051810713,
- 0.00851991077265941
+ 0.00851991077265941,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.3"),
0.876840582167709,
- 0.00639295055520668
+ 0.00639295055520668,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.2"),
0.876224968735958,
- 0.00426599018403942
+ 0.00426599018403942,
),
(
ConnectorPort.B,
-5,
BigDecimal("-0.1"),
0.875598164537768,
- 0.00213902965850952
+ 0.00213902965850952,
),
(
ConnectorPort.B,
-5,
BigDecimal("0"),
0.874960121469824,
- 0.0000120689779535628
+ 0.0000120689779535628,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.1"),
0.874310790282754,
- -0.00211489185830792
+ -0.00211489185830792,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.2"),
0.873650120560035,
- -0.00424185285097023
+ -0.00424185285097023,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.3"),
0.872978060696187,
- -0.00636881400074527
+ -0.00636881400074527,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.4"),
0.872294557874257,
- -0.0084957753083616
+ -0.0084957753083616,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.5"),
0.871599558042545,
- -0.0106227367745647
+ -0.0106227367745647,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.6"),
0.870893005890569,
- -0.0127496984001176
+ -0.0127496984001176,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.7"),
0.870174844824216,
- -0.014876660185801
+ -0.014876660185801,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.8"),
0.869445016940074,
- -0.0170036221324134
+ -0.0170036221324134,
),
(
ConnectorPort.B,
-5,
BigDecimal("0.9"),
0.868703462998902,
- -0.0191305842407722
+ -0.0191305842407722,
),
(
ConnectorPort.B,
-5,
BigDecimal("1"),
0.867950122398205,
- -0.0212575465117131
+ -0.0212575465117131,
),
(
ConnectorPort.B,
-4,
BigDecimal("-1"),
0.906007210847198,
- 0.0218897165661608
+ 0.0218897165661608,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.9"),
0.905453291374814,
- 0.0197019869922829
+ 0.0197019869922829,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.8"),
0.904888176420214,
- 0.0175142572639773
+ 0.0175142572639773,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.7"),
0.904311824205154,
- 0.0153265273806679
+ 0.0153265273806679,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.6"),
0.903724191906283,
- 0.0131387973417637
+ 0.0131387973417637,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.5"),
0.903125235637856,
- 0.0109510671466597
+ 0.0109510671466597,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.4"),
0.902514910433876,
- 0.00876333679473538
+ 0.00876333679473538,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.3"),
0.901893170229644,
- 0.00657560628535547
+ 0.00657560628535547,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.2"),
0.901259967842699,
- 0.00438787561786912
+ 0.00438787561786912,
),
(
ConnectorPort.B,
-4,
BigDecimal("-0.1"),
0.900615254953133,
- 0.00220014479160983
+ 0.00220014479160983,
),
(
ConnectorPort.B,
-4,
BigDecimal("0"),
0.899958982083247,
- 0.0000124138058950773
+ 0.0000124138058950773,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.1"),
0.899291098576547,
- -0.0021753173399738
+ -0.0021753173399738,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.2"),
0.898611552576036,
- -0.00436304864671219
+ -0.00436304864671219,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.3"),
0.897920291001792,
- -0.00655078011505222
+ -0.00655078011505222,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.4"),
0.897217259527807,
- -0.00873851174574332
+ -0.00873851174574332,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.5"),
0.896502402558046,
- -0.0109262435395523
+ -0.0109262435395523,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.6"),
0.895775663201728,
- -0.0131139754972638
+ -0.0131139754972638,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.7"),
0.895036983247764,
- -0.015301707619681
+ -0.015301707619681,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.8"),
0.894286303138362,
- -0.0174894399076252
+ -0.0174894399076252,
),
(
ConnectorPort.B,
-4,
BigDecimal("0.9"),
0.893523561941728,
- -0.019677172361937
+ -0.019677172361937,
),
(
ConnectorPort.B,
-4,
BigDecimal("1"),
0.892748697323868,
- -0.0218649049834762
+ -0.0218649049834762,
),
(
ConnectorPort.B,
-3,
BigDecimal("-1"),
0.931174077815176,
- 0.0224977642485542
+ 0.0224977642485542,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.9"),
0.930604771690782,
- 0.0202492644087352
+ 0.0202492644087352,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.8"),
0.930023959098554,
- 0.018000764410199
+ 0.018000764410199,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.7"),
0.929431597099742,
- 0.0157522642523531
+ 0.0157522642523531,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.6"),
0.928827641681457,
- 0.0135037639345905
+ 0.0135037639345905,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.5"),
0.928212047738907,
- 0.0112552634562891
+ 0.0112552634562891,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.4"),
0.927584769057039,
- 0.00900676281681138
+ 0.00900676281681138,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.3"),
0.926945758291578,
- 0.00675826201550427
+ 0.00675826201550427,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.2"),
0.926294966949441,
- 0.00450976105169881
+ 0.00450976105169881,
),
(
ConnectorPort.B,
-3,
BigDecimal("-0.1"),
0.925632345368498,
- 0.00226125992471011
+ 0.00226125992471011,
),
(
ConnectorPort.B,
-3,
BigDecimal("0"),
0.924957842696671,
- 0.0000127586338366326
+ 0.0000127586338366326,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.1"),
0.92427140687034,
- -0.00223574282163975
+ -0.00223574282163975,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.2"),
0.923572984592037,
- -0.00448424444245421
+ -0.00448424444245421,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.3"),
0.922862521307398,
- -0.00673274622935925
+ -0.00673274622935925,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.4"),
0.922139961181357,
- -0.00898124818312505
+ -0.00898124818312505,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.5"),
0.921405247073547,
- -0.0112297503045398
+ -0.0112297503045398,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.6"),
0.920658320512887,
- -0.0134782525944101
+ -0.0134782525944101,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.7"),
0.919899121671314,
- -0.015726755053561
+ -0.015726755053561,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.8"),
0.91912758933665,
- -0.017975257682837
+ -0.017975257682837,
),
(
ConnectorPort.B,
-3,
BigDecimal("0.9"),
0.918343660884553,
- -0.0202237604831019
+ -0.0202237604831019,
),
(
ConnectorPort.B,
-3,
BigDecimal("1"),
0.917547272249531,
- -0.0224722634552394
+ -0.0224722634552394,
),
(
ConnectorPort.B,
-2,
BigDecimal("-1"),
0.956340944783154,
- 0.0231058119309475
+ 0.0231058119309475,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.9"),
0.955756252006749,
- 0.0207965418251875
+ 0.0207965418251875,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.8"),
0.955159741776893,
- 0.0184872715564205
+ 0.0184872715564205,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.7"),
0.954551369994329,
- 0.0161780011240382
+ 0.0161780011240382,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.6"),
0.953931091456632,
- 0.0138687305274172
+ 0.0138687305274172,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.5"),
0.953298859839959,
- 0.0115594597659185
+ 0.0115594597659185,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.4"),
0.952654627680202,
- 0.00925018883888728
+ 0.00925018883888728,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.3"),
0.951998346353513,
- 0.00694091774565297
+ 0.00694091774565297,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.2"),
0.951329966056183,
- 0.00463164648552851
+ 0.00463164648552851,
),
(
ConnectorPort.B,
-2,
BigDecimal("-0.1"),
0.950649435783863,
- 0.00232237505781035
+ 0.00232237505781035,
),
(
ConnectorPort.B,
-2,
BigDecimal("0"),
0.949956703310094,
- 0.000013103461778146
+ 0.000013103461778146,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.1"),
0.949251715164133,
- -0.00229616830330571
+ -0.00229616830330571,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.2"),
0.948534416608038,
- -0.00460544023819623
+ -0.00460544023819623,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.3"),
0.947804751613003,
- -0.00691471234366629
+ -0.00691471234366629,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.4"),
0.947062662834907,
- -0.00922398462050684
+ -0.00922398462050684,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.5"),
0.946308091589049,
- -0.0115332570695274
+ -0.0115332570695274,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.6"),
0.945540977824046,
- -0.0138425296915563
+ -0.0138425296915563,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.7"),
0.944761260094862,
- -0.0161518024874411
+ -0.0161518024874411,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.8"),
0.943968875534937,
- -0.0184610754580489
+ -0.0184610754580489,
),
(
ConnectorPort.B,
-2,
BigDecimal("0.9"),
0.943163759827379,
- -0.0207703486042669
+ -0.0207703486042669,
),
(
ConnectorPort.B,
-2,
BigDecimal("1"),
0.942345847175194,
- -0.0230796219270027
+ -0.0230796219270027,
),
(
ConnectorPort.B,
-1,
BigDecimal("-1"),
0.981507811751131,
- 0.023713859613341
+ 0.023713859613341,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.9"),
0.980907732322716,
- 0.0213438192416398
+ 0.0213438192416398,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.8"),
0.980295524455232,
- 0.0189737787026421
+ 0.0189737787026421,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.7"),
0.979671142888917,
- 0.0166037379957236
+ 0.0166037379957236,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.6"),
0.979034541231806,
- 0.0142336971202441
+ 0.0142336971202441,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.5"),
0.97838567194101,
- 0.0118636560755479
+ 0.0118636560755479,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.4"),
0.977724486303365,
- 0.00949361486096332
+ 0.00949361486096332,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.3"),
0.977050934415447,
- 0.00712357347580179
+ 0.00712357347580179,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.2"),
0.976364965162924,
- 0.00475353191935825
+ 0.00475353191935825,
),
(
ConnectorPort.B,
-1,
BigDecimal("-0.1"),
0.975666526199227,
- 0.00238349019091072
+ 0.00238349019091072,
),
(
ConnectorPort.B,
-1,
BigDecimal("0"),
0.974955563923518,
- 0.0000134482897197081
+ 0.0000134482897197081,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.1"),
0.974232023457926,
- -0.0023565937849716
+ -0.0023565937849716,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.2"),
0.973495848624039,
- -0.00472663603393815
+ -0.00472663603393815,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.3"),
0.972746981918608,
- -0.00709667845797326
+ -0.00709667845797326,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.4"),
0.971985364488457,
- -0.00946672105788853
+ -0.00946672105788853,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.5"),
0.97121093610455,
- -0.0118367638345149
+ -0.0118367638345149,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.6"),
0.970423635135205,
- -0.0142068067887024
+ -0.0142068067887024,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.7"),
0.969623398518412,
- -0.016576849921321
+ -0.016576849921321,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.8"),
0.968810161733225,
- -0.0189468932332606
+ -0.0189468932332606,
),
(
ConnectorPort.B,
-1,
BigDecimal("0.9"),
0.967983858770205,
- -0.0213169367254318
+ -0.0213169367254318,
),
(
ConnectorPort.B,
-1,
BigDecimal("1"),
0.967144422100857,
- -0.0236869803987659
+ -0.0236869803987659,
),
(
ConnectorPort.B,
0,
BigDecimal("-1"),
1.00667467871911,
- 0.0243219072957343
+ 0.0243219072957343,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.9"),
1.00605921263868,
- 0.0218910966580921
+ 0.0218910966580921,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.8"),
1.00543130713357,
- 0.0194602858488637
+ 0.0194602858488637,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.7"),
1.0047909157835,
- 0.0170294748674087
+ 0.0170294748674087,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.6"),
1.00413799100698,
- 0.0145986637130708
+ 0.0145986637130708,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.5"),
1.00347248404206,
- 0.0121678523851774
+ 0.0121678523851774,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.4"),
1.00279434492653,
- 0.00973704088303931
+ 0.00973704088303931,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.3"),
1.00210352247738,
- 0.00730622920595051
+ 0.00730622920595051,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.2"),
1.00139996426967,
- 0.00487541735318787
+ 0.00487541735318787,
),
(
ConnectorPort.B,
0,
BigDecimal("-0.1"),
1.00068361661459,
- 0.00244460532401091
+ 0.00244460532401091,
),
(
ConnectorPort.B,
0,
BigDecimal("0"),
0.999954424536941,
- 0.0000137931176612275
+ 0.0000137931176612275,
),
(
ConnectorPort.B,
0,
BigDecimal("0.1"),
0.999212331751719,
- -0.00241701926663761
+ -0.00241701926663761,
),
(
ConnectorPort.B,
0,
BigDecimal("0.2"),
0.99845728064004,
- -0.00484783182968026
+ -0.00484783182968026,
),
(
ConnectorPort.B,
0,
BigDecimal("0.3"),
0.997689212224214,
- -0.00727864457228031
+ -0.00727864457228031,
),
(
ConnectorPort.B,
0,
BigDecimal("0.4"),
0.996908066142007,
- -0.00970945749527035
+ -0.00970945749527035,
),
(
ConnectorPort.B,
0,
BigDecimal("0.5"),
0.996113780620051,
- -0.0121402705995025
+ -0.0121402705995025,
),
(
ConnectorPort.B,
0,
BigDecimal("0.6"),
0.995306292446364,
- -0.0145710838858487
+ -0.0145710838858487,
),
(
ConnectorPort.B,
0,
BigDecimal("0.7"),
0.994485536941961,
- -0.0170018973552011
+ -0.0170018973552011,
),
(
ConnectorPort.B,
0,
BigDecimal("0.8"),
0.993651447931513,
- -0.0194327110084725
+ -0.0194327110084725,
),
(
ConnectorPort.B,
0,
BigDecimal("0.9"),
0.99280395771303,
- -0.0218635248465967
+ -0.0218635248465967,
),
(
ConnectorPort.B,
0,
BigDecimal("1"),
0.99194299702652,
- -0.0242943388705291
+ -0.0242943388705291,
),
(
ConnectorPort.B,
1,
BigDecimal("-1"),
1.03184154568709,
- 0.0249299549781277
+ 0.0249299549781277,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.9"),
1.03121069295465,
- 0.0224383740745444
+ 0.0224383740745444,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.8"),
1.03056708981191,
- 0.0199467929950853
+ 0.0199467929950853,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.7"),
1.02991068867809,
- 0.0174552117390939
+ 0.0174552117390939,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.6"),
1.02924144078216,
- 0.0149636303058977
+ 0.0149636303058977,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.5"),
1.02855929614311,
- 0.0124720486948069
+ 0.0124720486948069,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.4"),
1.02786420354969,
- 0.00998046690511528
+ 0.00998046690511528,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.3"),
1.02715611053932,
- 0.00748888493609935
+ 0.00748888493609935,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.2"),
1.02643496337641,
- 0.00499730278701767
+ 0.00499730278701767,
),
(
ConnectorPort.B,
1,
BigDecimal("-0.1"),
1.02570070702996,
- 0.00250572045711118
+ 0.00250572045711118,
),
(
ConnectorPort.B,
1,
BigDecimal("0"),
1.02495328515036,
- 0.0000141379456027874
+ 0.0000141379456027874,
),
(
ConnectorPort.B,
1,
BigDecimal("0.1"),
1.02419264004551,
- -0.00247744474830352
+ -0.00247744474830352,
),
(
ConnectorPort.B,
1,
BigDecimal("0.2"),
1.02341871265604,
- -0.00496902762542224
+ -0.00496902762542224,
),
(
ConnectorPort.B,
1,
BigDecimal("0.3"),
1.02263144252982,
- -0.00746061068658732
+ -0.00746061068658732,
),
(
ConnectorPort.B,
1,
BigDecimal("0.4"),
1.02183076779556,
- -0.00995219393265205
+ -0.00995219393265205,
),
(
ConnectorPort.B,
1,
BigDecimal("0.5"),
1.02101662513555,
- -0.01244377736449
+ -0.01244377736449,
),
(
ConnectorPort.B,
1,
BigDecimal("0.6"),
1.02018894975752,
- -0.0149353609829949
+ -0.0149353609829949,
),
(
ConnectorPort.B,
1,
BigDecimal("0.7"),
1.01934767536551,
- -0.0174269447890811
+ -0.0174269447890811,
),
(
ConnectorPort.B,
1,
BigDecimal("0.8"),
1.0184927341298,
- -0.0199185287836842
+ -0.0199185287836842,
),
(
ConnectorPort.B,
1,
BigDecimal("0.9"),
1.01762405665586,
- -0.0224101129677615
+ -0.0224101129677615,
),
(
ConnectorPort.B,
1,
BigDecimal("1"),
1.01674157195218,
- -0.0249016973422923
+ -0.0249016973422923,
),
(ConnectorPort.B, 2, BigDecimal("-1"), 1.05700841265506, 0.025538002660521),
(
@@ -4874,434 +4874,434 @@ trait TransformerTestData extends TransformerTestGrid {
2,
BigDecimal("-0.9"),
1.05636217327062,
- 0.0229856514909967
+ 0.0229856514909967,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.8"),
1.05570287249025,
- 0.0204333001413069
+ 0.0204333001413069,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.7"),
1.05503046157268,
- 0.0178809486107791
+ 0.0178809486107791,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.6"),
1.05434489055733,
- 0.0153285968987244
+ 0.0153285968987244,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.5"),
1.05364610824416,
- 0.0127762450044363
+ 0.0127762450044363,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.4"),
1.05293406217286,
- 0.0102238929271913
+ 0.0102238929271913,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.3"),
1.05220869860125,
- 0.00767154066624801
+ 0.00767154066624801,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.2"),
1.05146996248315,
- 0.00511918822084738
+ 0.00511918822084738,
),
(
ConnectorPort.B,
2,
BigDecimal("-0.1"),
1.05071779744532,
- 0.00256683559021147
+ 0.00256683559021147,
),
(
ConnectorPort.B,
2,
BigDecimal("0"),
1.04995214576379,
- 0.0000144827735443296
+ 0.0000144827735443296,
),
(
ConnectorPort.B,
2,
BigDecimal("0.1"),
1.04917294833931,
- -0.00253787022996945
+ -0.00253787022996945,
),
(
ConnectorPort.B,
2,
BigDecimal("0.2"),
1.04838014467204,
- -0.00509022342116422
+ -0.00509022342116422,
),
(
ConnectorPort.B,
2,
BigDecimal("0.3"),
1.04757367283542,
- -0.00764257680089427
+ -0.00764257680089427,
),
(
ConnectorPort.B,
2,
BigDecimal("0.4"),
1.04675346944911,
- -0.0101949303700338
+ -0.0101949303700338,
),
(
ConnectorPort.B,
2,
BigDecimal("0.5"),
1.04591946965105,
- -0.0127472841294777
+ -0.0127472841294777,
),
(
ConnectorPort.B,
2,
BigDecimal("0.6"),
1.04507160706868,
- -0.0152996380801412
+ -0.0152996380801412,
),
(
ConnectorPort.B,
2,
BigDecimal("0.7"),
1.04420981378906,
- -0.0178519922229611
+ -0.0178519922229611,
),
(
ConnectorPort.B,
2,
BigDecimal("0.8"),
1.04333402032809,
- -0.0204043465588961
+ -0.0204043465588961,
),
(
ConnectorPort.B,
2,
BigDecimal("0.9"),
1.04244415559868,
- -0.0229567010889265
+ -0.0229567010889265,
),
(
ConnectorPort.B,
2,
BigDecimal("1"),
1.04154014687785,
- -0.0255090558140557
+ -0.0255090558140557,
),
(
ConnectorPort.B,
3,
BigDecimal("-1"),
1.08217527962304,
- 0.0261460503429143
+ 0.0261460503429143,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.9"),
1.08151365358658,
- 0.023532928907449
+ 0.023532928907449,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.8"),
1.08083865516859,
- 0.0209198072875285
+ 0.0209198072875285,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.7"),
1.08015023446727,
- 0.0183066854824644
+ 0.0183066854824644,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.6"),
1.0794483403325,
- 0.0156935634915511
+ 0.0156935634915511,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.5"),
1.07873292034522,
- 0.0130804413140657
+ 0.0130804413140657,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.4"),
1.07800392079602,
- 0.0104673189492672
+ 0.0104673189492672,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.3"),
1.07726128666319,
- 0.00785419639639677
+ 0.00785419639639677,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.2"),
1.07650496158989,
- 0.00524107365467694
+ 0.00524107365467694,
),
(
ConnectorPort.B,
3,
BigDecimal("-0.1"),
1.07573488786069,
- 0.00262795072331172
+ 0.00262795072331172,
),
(
ConnectorPort.B,
3,
BigDecimal("0"),
1.07495100637721,
- 0.0000148276014858161
+ 0.0000148276014858161,
),
(
ConnectorPort.B,
3,
BigDecimal("0.1"),
1.0741532566331,
- -0.00259829571163542
+ -0.00259829571163542,
),
(
ConnectorPort.B,
3,
BigDecimal("0.2"),
1.07334157668804,
- -0.00521141921690625
+ -0.00521141921690625,
),
(
ConnectorPort.B,
3,
BigDecimal("0.3"),
1.07251590314103,
- -0.00782454291520131
+ -0.00782454291520131,
),
(
ConnectorPort.B,
3,
BigDecimal("0.4"),
1.07167617110266,
- -0.0104376668074156
+ -0.0104376668074156,
),
(
ConnectorPort.B,
3,
BigDecimal("0.5"),
1.07082231416656,
- -0.0130507908944652
+ -0.0130507908944652,
),
(
ConnectorPort.B,
3,
BigDecimal("0.6"),
1.06995426437984,
- -0.0156639151772874
+ -0.0156639151772874,
),
(
ConnectorPort.B,
3,
BigDecimal("0.7"),
1.06907195221261,
- -0.0182770396568411
+ -0.0182770396568411,
),
(
ConnectorPort.B,
3,
BigDecimal("0.8"),
1.06817530652638,
- -0.0208901643341079
+ -0.0208901643341079,
),
(
ConnectorPort.B,
3,
BigDecimal("0.9"),
1.06726425454151,
- -0.0235032892100915
+ -0.0235032892100915,
),
(
ConnectorPort.B,
3,
BigDecimal("1"),
1.06633872180351,
- -0.0261164142858189
+ -0.0261164142858189,
),
(
ConnectorPort.B,
4,
BigDecimal("-1"),
1.10734214659102,
- 0.0267540980253077
+ 0.0267540980253077,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.9"),
1.10666513390255,
- 0.0240802063239013
+ 0.0240802063239013,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.8"),
1.10597443784693,
- 0.0214063144337501
+ 0.0214063144337501,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.7"),
1.10527000736186,
- 0.0187324223541496
+ 0.0187324223541496,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.6"),
1.10455179010768,
- 0.016058530084378
+ 0.016058530084378,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.5"),
1.10381973244627,
- 0.0133846376236952
+ 0.0133846376236952,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.4"),
1.10307377941918,
- 0.0107107449713433
+ 0.0107107449713433,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.3"),
1.10231387472512,
- 0.00803685212654552
+ 0.00803685212654552,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.2"),
1.10153996069663,
- 0.00536295908850669
+ 0.00536295908850669,
),
(
ConnectorPort.B,
4,
BigDecimal("-0.1"),
1.10075197827605,
- 0.00268906585641202
+ 0.00268906585641202,
),
(
ConnectorPort.B,
4,
BigDecimal("0"),
1.09994986699064,
- 0.0000151724294273726
+ 0.0000151724294273726,
),
(
ConnectorPort.B,
4,
BigDecimal("0.1"),
1.09913356492689,
- -0.00265872119330126
+ -0.00265872119330126,
),
(
ConnectorPort.B,
4,
BigDecimal("0.2"),
1.09830300870404,
- -0.00533261501264826
+ -0.00533261501264826,
),
(
ConnectorPort.B,
4,
BigDecimal("0.3"),
1.09745813344664,
- -0.00800650902950831
+ -0.00800650902950831,
),
(
ConnectorPort.B,
4,
BigDecimal("0.4"),
1.09659887275621,
- -0.0106804032447974
+ -0.0106804032447974,
),
(
ConnectorPort.B,
4,
BigDecimal("0.5"),
1.09572515868206,
- -0.0133542976594527
+ -0.0133542976594527,
),
(
ConnectorPort.B,
4,
BigDecimal("0.6"),
1.094836921691,
- -0.0160281922744335
+ -0.0160281922744335,
),
(
ConnectorPort.B,
4,
BigDecimal("0.7"),
1.09393409063616,
- -0.0187020870907211
+ -0.0187020870907211,
),
(
ConnectorPort.B,
4,
BigDecimal("0.8"),
1.09301659272466,
- -0.0213759821093196
+ -0.0213759821093196,
),
(
ConnectorPort.B,
4,
BigDecimal("0.9"),
1.09208435348433,
- -0.0240498773312563
+ -0.0240498773312563,
),
(
ConnectorPort.B,
4,
BigDecimal("1"),
1.09113729672917,
- -0.0267237727575821
+ -0.0267237727575821,
),
(ConnectorPort.B, 5, BigDecimal("-1"), 1.132509013559, 0.027362145707701),
(
@@ -5309,280 +5309,280 @@ trait TransformerTestData extends TransformerTestGrid {
5,
BigDecimal("-0.9"),
1.13181661421852,
- 0.0246274837403537
+ 0.0246274837403537,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.8"),
1.13111022052527,
- 0.0218928215799717
+ 0.0218928215799717,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.7"),
1.13038978025644,
- 0.0191581592258348
+ 0.0191581592258348,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.6"),
1.12965523988285,
- 0.0164234966772046
+ 0.0164234966772046,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.5"),
1.12890654454732,
- 0.0136888339333246
+ 0.0136888339333246,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.4"),
1.12814363804234,
- 0.0109541709934192
+ 0.0109541709934192,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.3"),
1.12736646278705,
- 0.00821950785669432
+ 0.00821950785669432,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.2"),
1.12657495980337,
- 0.00548484452233641
+ 0.00548484452233641,
),
(
ConnectorPort.B,
5,
BigDecimal("-0.1"),
1.12576906869142,
- 0.00275018098951233
+ 0.00275018098951233,
),
(
ConnectorPort.B,
5,
BigDecimal("0"),
1.12494872760406,
- 0.0000155172573689131
+ 0.0000155172573689131,
),
(
ConnectorPort.B,
5,
BigDecimal("0.1"),
1.12411387322068,
- -0.00271914667496727
+ -0.00271914667496727,
),
(
ConnectorPort.B,
5,
BigDecimal("0.2"),
1.12326444072004,
- -0.00545381080839021
+ -0.00545381080839021,
),
(
ConnectorPort.B,
5,
BigDecimal("0.3"),
1.12240036375224,
- -0.00818847514381527
+ -0.00818847514381527,
),
(
ConnectorPort.B,
5,
BigDecimal("0.4"),
1.12152157440976,
- -0.0109231396821791
+ -0.0109231396821791,
),
(
ConnectorPort.B,
5,
BigDecimal("0.5"),
1.12062800319756,
- -0.0136578044244403
+ -0.0136578044244403,
),
(
ConnectorPort.B,
5,
BigDecimal("0.6"),
1.11971957900216,
- -0.0163924693715797
+ -0.0163924693715797,
),
(
ConnectorPort.B,
5,
BigDecimal("0.7"),
1.11879622905971,
- -0.0191271345246012
+ -0.0191271345246012,
),
(
ConnectorPort.B,
5,
BigDecimal("0.8"),
1.11785787892295,
- -0.0218617998845315
+ -0.0218617998845315,
),
(
ConnectorPort.B,
5,
BigDecimal("0.9"),
1.11690445242716,
- -0.0245964654524212
+ -0.0245964654524212,
),
(
ConnectorPort.B,
5,
BigDecimal("1"),
1.11593587165483,
- -0.0273311312293454
+ -0.0273311312293454,
),
(
ConnectorPort.B,
6,
BigDecimal("-1"),
1.15767588052698,
- 0.0279701933900944
+ 0.0279701933900944,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.9"),
1.15696809453449,
- 0.0251747611568059
+ 0.0251747611568059,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.8"),
1.15624600320361,
- 0.0223793287261932
+ 0.0223793287261932,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.7"),
1.15550955315103,
- 0.01958389609752
+ 0.01958389609752,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.6"),
1.15475868965803,
- 0.0167884632700314
+ 0.0167884632700314,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.5"),
1.15399335664837,
- 0.0139930302429539
+ 0.0139930302429539,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.4"),
1.15321349666551,
- 0.0111975970154952
+ 0.0111975970154952,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.3"),
1.15241905084899,
- 0.00840216358684306
+ 0.00840216358684306,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.2"),
1.15160995891012,
- 0.00560672995616601
+ 0.00560672995616601,
),
(
ConnectorPort.B,
6,
BigDecimal("-0.1"),
1.15078615910678,
- 0.00281129612261251
+ 0.00281129612261251,
),
(
ConnectorPort.B,
6,
BigDecimal("0"),
1.14994758821748,
- 0.0000158620853104297
+ 0.0000158620853104297,
),
(
ConnectorPort.B,
6,
BigDecimal("0.1"),
1.14909418151448,
- -0.00277957215663318
+ -0.00277957215663318,
),
(
ConnectorPort.B,
6,
BigDecimal("0.2"),
1.14822587273605,
- -0.0055750066041323
+ -0.0055750066041323,
),
(
ConnectorPort.B,
6,
BigDecimal("0.3"),
1.14734259405785,
- -0.00837044125812234
+ -0.00837044125812234,
),
(
ConnectorPort.B,
6,
BigDecimal("0.4"),
1.14644427606331,
- -0.0111658761195609
+ -0.0111658761195609,
),
(
ConnectorPort.B,
6,
BigDecimal("0.5"),
1.14553084771306,
- -0.0139613111894279
+ -0.0139613111894279,
),
(
ConnectorPort.B,
6,
BigDecimal("0.6"),
1.14460223631332,
- -0.016756746468726
+ -0.016756746468726,
),
(
ConnectorPort.B,
6,
BigDecimal("0.7"),
1.14365836748325,
- -0.0195521819584812
+ -0.0195521819584812,
),
(
ConnectorPort.B,
6,
BigDecimal("0.8"),
1.14269916512124,
- -0.0223476176597433
+ -0.0223476176597433,
),
(
ConnectorPort.B,
6,
BigDecimal("0.9"),
1.14172455136999,
- -0.0251430535735862
+ -0.0251430535735862,
),
(ConnectorPort.B, 6, BigDecimal("1"), 1.1407344465805, -0.0279384897011086),
(
@@ -5590,287 +5590,287 @@ trait TransformerTestData extends TransformerTestGrid {
7,
BigDecimal("-1"),
1.18284274749495,
- 0.0285782410724878
+ 0.0285782410724878,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.9"),
1.18211957485045,
- 0.0257220385732582
+ 0.0257220385732582,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.8"),
1.18138178588195,
- 0.0228658358724148
+ 0.0228658358724148,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.7"),
1.18062932604562,
- 0.0200096329692052
+ 0.0200096329692052,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.6"),
1.1798621394332,
- 0.0171534298628582
+ 0.0171534298628582,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.5"),
1.17908016874942,
- 0.0142972265525834
+ 0.0142972265525834,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.4"),
1.17828335528867,
- 0.0114410230375711
+ 0.0114410230375711,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.3"),
1.17747163891092,
- 0.00858481931699185
+ 0.00858481931699185,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.2"),
1.17664495801686,
- 0.00572861538999577
+ 0.00572861538999577,
),
(
ConnectorPort.B,
7,
BigDecimal("-0.1"),
1.17580324952215,
- 0.00287241125571286
+ 0.00287241125571286,
),
(
ConnectorPort.B,
7,
BigDecimal("0"),
1.17494644883091,
- 0.0000162069132519891
+ 0.0000162069132519891,
),
(
ConnectorPort.B,
7,
BigDecimal("0.1"),
1.17407448980827,
- -0.00283999763829914
+ -0.00283999763829914,
),
(
ConnectorPort.B,
7,
BigDecimal("0.2"),
1.17318730475205,
- -0.00569620239987423
+ -0.00569620239987423,
),
(
ConnectorPort.B,
7,
BigDecimal("0.3"),
1.17228482436345,
- -0.0085524073724293
+ -0.0085524073724293,
),
(
ConnectorPort.B,
7,
BigDecimal("0.4"),
1.17136697771686,
- -0.0114086125569427
+ -0.0114086125569427,
),
(
ConnectorPort.B,
7,
BigDecimal("0.5"),
1.17043369222856,
- -0.0142648179544154
+ -0.0142648179544154,
),
(
ConnectorPort.B,
7,
BigDecimal("0.6"),
1.16948489362448,
- -0.0171210235658722
+ -0.0171210235658722,
),
(
ConnectorPort.B,
7,
BigDecimal("0.7"),
1.1685205059068,
- -0.0199772293923612
+ -0.0199772293923612,
),
(
ConnectorPort.B,
7,
BigDecimal("0.8"),
1.16754045131953,
- -0.0228334354349551
+ -0.0228334354349551,
),
(
ConnectorPort.B,
7,
BigDecimal("0.9"),
1.16654465031281,
- -0.0256896416947511
+ -0.0256896416947511,
),
(
ConnectorPort.B,
7,
BigDecimal("1"),
1.16553302150616,
- -0.0285458481728718
+ -0.0285458481728718,
),
(
ConnectorPort.B,
8,
BigDecimal("-1"),
1.20800961446293,
- 0.0291862887548811
+ 0.0291862887548811,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.9"),
1.20727105516642,
- 0.0262693159897105
+ 0.0262693159897105,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.8"),
1.20651756856029,
- 0.0233523430186365
+ 0.0233523430186365,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.7"),
1.20574909894021,
- 0.0204353698408905
+ 0.0204353698408905,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.6"),
1.20496558920838,
- 0.017518396455685
+ 0.017518396455685,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.5"),
1.20416698085047,
- 0.0146014228622129
+ 0.0146014228622129,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.4"),
1.20335321391183,
- 0.0116844490596472
+ 0.0116844490596472,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.3"),
1.20252422697286,
- 0.00876747504714063
+ 0.00876747504714063,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.2"),
1.2016799571236,
- 0.00585050082382552
+ 0.00585050082382552,
),
(
ConnectorPort.B,
8,
BigDecimal("-0.1"),
1.20082033993751,
- 0.00293352638881315
+ 0.00293352638881315,
),
(
ConnectorPort.B,
8,
BigDecimal("0"),
1.19994530944433,
- 0.0000165517411934878
+ 0.0000165517411934878,
),
(
ConnectorPort.B,
8,
BigDecimal("0.1"),
1.19905479810206,
- -0.00290042311996509
+ -0.00290042311996509,
),
(
ConnectorPort.B,
8,
BigDecimal("0.2"),
1.19814873676805,
- -0.00581739819561628
+ -0.00581739819561628,
),
(
ConnectorPort.B,
8,
BigDecimal("0.3"),
1.19722705466906,
- -0.00873437348673637
+ -0.00873437348673637,
),
(
ConnectorPort.B,
8,
BigDecimal("0.4"),
1.19628967937041,
- -0.0116513489943244
+ -0.0116513489943244,
),
(
ConnectorPort.B,
8,
BigDecimal("0.5"),
1.19533653674406,
- -0.014568324719403
+ -0.014568324719403,
),
(
ConnectorPort.B,
8,
BigDecimal("0.6"),
1.19436755093564,
- -0.0174853006630184
+ -0.0174853006630184,
),
(
ConnectorPort.B,
8,
BigDecimal("0.7"),
1.19338264433035,
- -0.0204022768262413
+ -0.0204022768262413,
),
(
ConnectorPort.B,
8,
BigDecimal("0.8"),
1.19238173751782,
- -0.0233192532101669
+ -0.0233192532101669,
),
(
ConnectorPort.B,
8,
BigDecimal("0.9"),
1.19136474925564,
- -0.026236229815916
+ -0.026236229815916,
),
(ConnectorPort.B, 8, BigDecimal("1"), 1.19033159643182, -0.029153206644635),
(
@@ -5878,295 +5878,295 @@ trait TransformerTestData extends TransformerTestGrid {
9,
BigDecimal("-1"),
1.23317648143091,
- 0.0297943364372745
+ 0.0297943364372745,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.9"),
1.23242253548239,
- 0.0268165934061628
+ 0.0268165934061628,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.8"),
1.23165335123863,
- 0.0238388501648581
+ 0.0238388501648581,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.7"),
1.23086887183479,
- 0.0208611067125756
+ 0.0208611067125756,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.6"),
1.23006903898355,
- 0.0178833630485118
+ 0.0178833630485118,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.5"),
1.22925379295153,
- 0.0149056191718423
+ 0.0149056191718423,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.4"),
1.228423072535,
- 0.0119278750817232
+ 0.0119278750817232,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.3"),
1.22757681503479,
- 0.00895013077728943
+ 0.00895013077728943,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.2"),
1.22671495623034,
- 0.00597238625765516
+ 0.00597238625765516,
),
(
ConnectorPort.B,
9,
BigDecimal("-0.1"),
1.22583743035288,
- 0.00299464152191339
+ 0.00299464152191339,
),
(
ConnectorPort.B,
9,
BigDecimal("0"),
1.22494417005775,
- 0.0000168965691349742
+ 0.0000168965691349742,
),
(
ConnectorPort.B,
9,
BigDecimal("0.1"),
1.22403510639586,
- -0.00296084860163105
+ -0.00296084860163105,
),
(
ConnectorPort.B,
9,
BigDecimal("0.2"),
1.22311016878405,
- -0.0059385939913583
+ -0.0059385939913583,
),
(
ConnectorPort.B,
9,
BigDecimal("0.3"),
1.22216928497466,
- -0.00891633960104338
+ -0.00891633960104338,
),
(
ConnectorPort.B,
9,
BigDecimal("0.4"),
1.22121238102396,
- -0.0118940854317062
+ -0.0118940854317062,
),
(
ConnectorPort.B,
9,
BigDecimal("0.5"),
1.22023938125956,
- -0.0148718314843905
+ -0.0148718314843905,
),
(
ConnectorPort.B,
9,
BigDecimal("0.6"),
1.2192502082468,
- -0.0178495777601646
+ -0.0178495777601646,
),
(
ConnectorPort.B,
9,
BigDecimal("0.7"),
1.2182447827539,
- -0.0208273242601213
+ -0.0208273242601213,
),
(
ConnectorPort.B,
9,
BigDecimal("0.8"),
1.2172230237161,
- -0.0238050709853787
+ -0.0238050709853787,
),
(
ConnectorPort.B,
9,
BigDecimal("0.9"),
1.21618484819846,
- -0.0267828179370809
+ -0.0267828179370809,
),
(
ConnectorPort.B,
9,
BigDecimal("1"),
1.21513017135749,
- -0.0297605651163982
+ -0.0297605651163982,
),
(
ConnectorPort.B,
10,
BigDecimal("-1"),
1.25834334839889,
- 0.0304023841196678
+ 0.0304023841196678,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.9"),
1.25757401579835,
- 0.0273638708226152
+ 0.0273638708226152,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.8"),
1.25678913391696,
- 0.0243253573110796
+ 0.0243253573110796,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.7"),
1.25598864472938,
- 0.0212868435842609
+ 0.0212868435842609,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.6"),
1.25517248875873,
- 0.0182483296413385
+ 0.0182483296413385,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.5"),
1.25434060505258,
- 0.0152098154814717
+ 0.0152098154814717,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.4"),
1.25349293115816,
- 0.0121713011037991
+ 0.0121713011037991,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.3"),
1.25262940309673,
- 0.00913278650743818
+ 0.00913278650743818,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.2"),
1.25174995533708,
- 0.00609427169148496
+ 0.00609427169148496,
),
(
ConnectorPort.B,
10,
BigDecimal("-0.1"),
1.25085452076824,
- 0.00305575665501359
+ 0.00305575665501359,
),
(
ConnectorPort.B,
10,
BigDecimal("0"),
1.24994303067118,
- 0.0000172413970765587
+ 0.0000172413970765587,
),
(
ConnectorPort.B,
10,
BigDecimal("0.1"),
1.24901541468965,
- -0.00302127408329697
+ -0.00302127408329697,
),
(
ConnectorPort.B,
10,
BigDecimal("0.2"),
1.24807160080005,
- -0.00605978978710035
+ -0.00605978978710035,
),
(
ConnectorPort.B,
10,
BigDecimal("0.3"),
1.24711151528027,
- -0.00909830571535039
+ -0.00909830571535039,
),
(
ConnectorPort.B,
10,
BigDecimal("0.4"),
1.24613508267751,
- -0.0121368218690879
+ -0.0121368218690879,
),
(
ConnectorPort.B,
10,
BigDecimal("0.5"),
1.24514222577506,
- -0.0151753382493781
+ -0.0151753382493781,
),
(
ConnectorPort.B,
10,
BigDecimal("0.6"),
1.24413286555796,
- -0.0182138548573108
+ -0.0182138548573108,
),
(
ConnectorPort.B,
10,
BigDecimal("0.7"),
1.24310692117745,
- -0.0212523716940014
+ -0.0212523716940014,
),
(
ConnectorPort.B,
10,
BigDecimal("0.8"),
1.24206430991439,
- -0.0242908887605906
+ -0.0242908887605906,
),
(
ConnectorPort.B,
10,
BigDecimal("0.9"),
1.24100494714129,
- -0.0273294060582459
+ -0.0273294060582459,
),
(
ConnectorPort.B,
10,
BigDecimal("1"),
1.23992874628315,
- -0.0303679235881615
- )
+ -0.0303679235881615,
+ ),
)
val tapDependentPortCurrents: TableFor9[
@@ -6178,7 +6178,7 @@ trait TransformerTestData extends TransformerTestGrid {
Double,
Double,
Double,
- Double
+ Double,
] = Table(
(
"tapSide",
@@ -6189,7 +6189,7 @@ trait TransformerTestData extends TransformerTestGrid {
"iAMag",
"iAAng",
"iBMag",
- "iBAng"
+ "iBAng",
),
(
ConnectorPort.A,
@@ -6200,7 +6200,7 @@ trait TransformerTestData extends TransformerTestGrid {
23.003954085118,
-178.834651058510,
431.294763182407,
- 0.781125259497
+ 0.781125259497,
),
(
ConnectorPort.A,
@@ -6211,7 +6211,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.525560223429,
-178.842119238276,
216.075065421917,
- 0.391722549446
+ 0.391722549446,
),
(
ConnectorPort.A,
@@ -6222,7 +6222,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.153959642453,
-89.999604838591,
0.000000000011,
- -63.434948822922
+ -63.434948822922,
),
(
ConnectorPort.A,
@@ -6233,7 +6233,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.625629756310,
-2.062621156246,
86.678612785391,
- -180.156031274941
+ -180.156031274941,
),
(
ConnectorPort.A,
@@ -6244,7 +6244,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.540232162149,
-1.103088525750,
347.601584970335,
- -180.628112995813
+ -180.628112995813,
),
(
ConnectorPort.A,
@@ -6255,7 +6255,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.971640550171,
-178.655670795668,
502.475593932476,
- 1.061459638084
+ 1.061459638084,
),
(
ConnectorPort.A,
@@ -6266,7 +6266,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.516767368719,
-178.903976192179,
251.905603222771,
- 0.532511914342
+ 0.532511914342,
),
(
ConnectorPort.A,
@@ -6277,7 +6277,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.113113206667,
-89.999604843751,
0.000000000777,
- -73.119669834476
+ -73.119669834476,
),
(
ConnectorPort.A,
@@ -6288,7 +6288,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.625851685250,
-1.613085600310,
101.155690258838,
- -180.212726208353
+ -180.212726208353,
),
(
ConnectorPort.A,
@@ -6299,7 +6299,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.563349939554,
-1.204609465931,
406.047287157229,
- -180.856312689835
+ -180.856312689835,
),
(
ConnectorPort.A,
@@ -6310,7 +6310,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.935405069071,
-178.398896556822,
573.354864130381,
- 1.384031350735
+ 1.384031350735,
),
(
ConnectorPort.A,
@@ -6321,7 +6321,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.507053462181,
-178.873309313946,
287.655034835151,
- 0.694718904051
+ 0.694718904051,
),
(
ConnectorPort.A,
@@ -6332,7 +6332,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.086602298880,
-89.999604838507,
0.000000000005,
- -90.000000000000
+ -90.000000000000,
),
(
ConnectorPort.A,
@@ -6343,7 +6343,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.626903589562,
-1.349856236502,
115.647100480956,
- -180.278184711145
+ -180.278184711145,
),
(
ConnectorPort.A,
@@ -6354,7 +6354,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.590741530558,
-1.386445189457,
464.742359900663,
- -181.120381398034
+ -181.120381398034,
),
(
ConnectorPort.A,
@@ -6365,7 +6365,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.919874893106,
-178.278100673998,
601.615721696539,
- 1.524814468172
+ 1.524814468172,
),
(
ConnectorPort.A,
@@ -6376,7 +6376,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.502886546947,
-178.842403214648,
301.929928522615,
- 0.765577315227
+ 0.765577315227,
),
(
ConnectorPort.A,
@@ -6387,7 +6387,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.078550816962,
-89.999609475535,
0.000000576609,
- -73.176741233690
+ -73.176741233690,
),
(
ConnectorPort.A,
@@ -6398,7 +6398,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.627473005039,
-1.278661221495,
121.448172109735,
- -180.306829295407
+ -180.306829295407,
),
(
ConnectorPort.A,
@@ -6409,7 +6409,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.602925214424,
-1.477216350318,
488.300222258160,
- -181.236130852830
+ -181.236130852830,
),
(
ConnectorPort.A,
@@ -6420,7 +6420,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.870025241112,
-177.861439802860,
686.067080644780,
- 1.987191673501
+ 1.987191673501,
),
(
ConnectorPort.A,
@@ -6431,7 +6431,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.489409416903,
-178.700787286039,
344.661815409481,
- 0.998555958661
+ 0.998555958661,
),
(
ConnectorPort.A,
@@ -6442,7 +6442,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.060140485118,
-89.999604900530,
0.000000006742,
- -73.172405921417
+ -73.172405921417,
),
(
ConnectorPort.A,
@@ -6453,7 +6453,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.629556005151,
-1.144721361043,
138.868669271316,
- -180.401205209677
+ -180.401205209677,
),
(
ConnectorPort.A,
@@ -6464,7 +6464,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.643871513939,
-1.802390457726,
559.287783590753,
- -181.618432259161
+ -181.618432259161,
),
(
ConnectorPort.B,
@@ -6475,7 +6475,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.935405088363,
-178.398896932957,
764.473152835885,
- 1.384030974747
+ 1.384030974747,
),
(
ConnectorPort.B,
@@ -6486,7 +6486,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.507053497806,
-178.873310168971,
383.540047677483,
- 0.694718050283
+ 0.694718050283,
),
(
ConnectorPort.B,
@@ -6497,7 +6497,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.086602095802,
-89.999645497660,
0.000007072753,
- -73.163406799512
+ -73.163406799512,
),
(
ConnectorPort.B,
@@ -6508,7 +6508,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.626903666866,
-1.349855603483,
154.196136582985,
- -180.278184095994
+ -180.278184095994,
),
(
ConnectorPort.B,
@@ -6519,7 +6519,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.590741530561,
-1.386445189467,
619.656479867641,
- -181.120381398044
+ -181.120381398044,
),
(
ConnectorPort.B,
@@ -6530,7 +6530,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.935405069060,
-178.398896556791,
655.262701863002,
- 1.384031350765
+ 1.384031350765,
),
(
ConnectorPort.B,
@@ -6541,7 +6541,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.507053462181,
-178.873309313955,
328.748611240179,
- 0.694718904041
+ 0.694718904041,
),
(
ConnectorPort.B,
@@ -6552,7 +6552,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.086602298877,
-89.999604839117,
0.000000000096,
- -73.739795291688
+ -73.739795291688,
),
(
ConnectorPort.B,
@@ -6563,7 +6563,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.626903745261,
-1.349858306013,
132.168119193982,
- -180.278186816939
+ -180.278186816939,
),
(
ConnectorPort.B,
@@ -6574,7 +6574,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.590741530557,
-1.386445189446,
531.134125600727,
- -181.120381398021
+ -181.120381398021,
),
(
ConnectorPort.B,
@@ -6585,7 +6585,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.935405069071,
-178.398896556822,
573.354864130381,
- 1.384031350735
+ 1.384031350735,
),
(
ConnectorPort.B,
@@ -6596,7 +6596,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.507053462181,
-178.873309313946,
287.655034835151,
- 0.694718904051
+ 0.694718904051,
),
(
ConnectorPort.B,
@@ -6607,7 +6607,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.086602298880,
-89.999604838507,
0.000000000005,
- -90.000000000000
+ -90.000000000000,
),
(
ConnectorPort.B,
@@ -6618,7 +6618,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.626903589562,
-1.349856236502,
115.647100480956,
- -180.278184711145
+ -180.278184711145,
),
(
ConnectorPort.B,
@@ -6629,7 +6629,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.590741530558,
-1.386445189457,
464.742359900663,
- -181.120381398034
+ -181.120381398034,
),
(
ConnectorPort.B,
@@ -6640,7 +6640,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.935405170891,
-178.398896948562,
546.052253991224,
- 1.384030959920
+ 1.384030959920,
),
(
ConnectorPort.B,
@@ -6651,7 +6651,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.507053496248,
-178.873309646778,
273.957176856597,
- 0.694718572467
+ 0.694718572467,
),
(
ConnectorPort.B,
@@ -6662,7 +6662,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.086602265146,
-89.999611600556,
0.000000839270,
- -73.144433757855
+ -73.144433757855,
),
(
ConnectorPort.B,
@@ -6673,7 +6673,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.626903738866,
-1.349857959049,
110.140099188552,
- -180.278186468467
+ -180.278186468467,
),
(
ConnectorPort.B,
@@ -6684,7 +6684,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.590741530489,
-1.386445115448,
442.611771334987,
- -181.120381324018
+ -181.120381324018,
),
(
ConnectorPort.B,
@@ -6695,7 +6695,7 @@ trait TransformerTestData extends TransformerTestGrid {
22.935405072746,
-178.398896583674,
477.795720186062,
- 1.384031323914
+ 1.384031323914,
),
(
ConnectorPort.B,
@@ -6706,7 +6706,7 @@ trait TransformerTestData extends TransformerTestGrid {
11.507053464629,
-178.873309352293,
239.712529081517,
- 0.694718865792
+ 0.694718865792,
),
(
ConnectorPort.B,
@@ -6717,7 +6717,7 @@ trait TransformerTestData extends TransformerTestGrid {
0.086602293201,
-89.999605976054,
0.000000123615,
- -73.155567956978
+ -73.155567956978,
),
(
ConnectorPort.B,
@@ -6728,7 +6728,7 @@ trait TransformerTestData extends TransformerTestGrid {
4.626903743678,
-1.349858243420,
96.372586881264,
- -180.278186753973
+ -180.278186753973,
),
(
ConnectorPort.B,
@@ -6739,7 +6739,7 @@ trait TransformerTestData extends TransformerTestGrid {
18.590741529536,
-1.386445177723,
387.285299896290,
- -181.120381386284
- )
+ -181.120381386284,
+ ),
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestGrid.scala b/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestGrid.scala
index 9f0fc77394..aff8b1f3a8 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestGrid.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/grid/TransformerTestGrid.scala
@@ -12,16 +12,16 @@ import edu.ie3.datamodel.models.input.connector.{
LineInput,
SwitchInput,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
RawGridElements,
- SubGridContainer
+ SubGridContainer,
}
import edu.ie3.datamodel.models.input.{
MeasurementUnitInput,
NodeInput,
- OperatorInput
+ OperatorInput,
}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.model.SystemComponent
@@ -60,7 +60,7 @@ trait TransformerTestGrid {
SystemComponent.determineOperationInterval(
defaultSimulationStart,
defaultSimulationEnd,
- defaultOperationTime
+ defaultOperationTime,
)
def mainRefSystem: RefSystem = {
@@ -85,7 +85,7 @@ trait TransformerTestGrid {
false,
0,
-10,
- 10
+ 10,
)
val transformerTypeTapLv = new Transformer2WTypeInput(
@@ -103,7 +103,7 @@ trait TransformerTestGrid {
true,
0,
-10,
- 10
+ 10,
)
val nodeA = new NodeInput(
@@ -115,7 +115,7 @@ trait TransformerTestGrid {
true,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.MV_10KV,
- 0
+ 0,
)
val nodeB = new NodeInput(
@@ -127,7 +127,7 @@ trait TransformerTestGrid {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.LV,
- 1
+ 1,
)
val transformerInputTapHv = new Transformer2WInput(
@@ -140,14 +140,14 @@ trait TransformerTestGrid {
1,
transformerTypeTapHv,
0,
- false
+ false,
)
val transformerModelTapHv: TransformerModel = TransformerModel(
transformerInputTapHv,
mainRefSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val transformerInputTapLv = new Transformer2WInput(
@@ -160,14 +160,14 @@ trait TransformerTestGrid {
1,
transformerTypeTapLv,
0,
- false
+ false,
)
val transformerModelTapLv: TransformerModel = TransformerModel(
transformerInputTapLv,
mainRefSystem,
defaultSimulationStart,
- defaultSimulationEnd
+ defaultSimulationEnd,
)
val gridTapHv: SubGridContainer = {
@@ -177,12 +177,12 @@ trait TransformerTestGrid {
Set(transformerInputTapHv).asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
TestGridFactory.createSubGrid(
gridName = "transformer_test_grid",
subgrid = 1,
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
)
}
@@ -193,12 +193,12 @@ trait TransformerTestGrid {
Set(transformerInputTapLv).asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
TestGridFactory.createSubGrid(
gridName = "transformer_test_grid",
subgrid = 1,
- rawGridElements = rawGridElements
+ rawGridElements = rawGridElements,
)
}
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/participant/HpTestData.scala b/src/test/scala/edu/ie3/simona/test/common/model/participant/HpTestData.scala
index 9dbed89e7b..60750607b4 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/participant/HpTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/participant/HpTestData.scala
@@ -15,7 +15,7 @@ import edu.ie3.datamodel.models.input.system.characteristic.ReactivePowerCharact
import edu.ie3.datamodel.models.input.thermal.{
ThermalBusInput,
ThermalHouseInput,
- ThermalStorageInput
+ ThermalStorageInput,
}
import edu.ie3.datamodel.models.voltagelevels.GermanVoltageLevelUtils
import edu.ie3.simona.test.common.DefaultTestData
@@ -34,12 +34,12 @@ trait HpTestData extends DefaultTestData {
false,
NodeInput.DEFAULT_GEO_POSITION,
GermanVoltageLevelUtils.LV,
- 2
+ 2,
)
protected val thermalBusInput = new ThermalBusInput(
UUID.fromString("48fa6e8d-c07f-45cd-9ad7-094a1f2a7489"),
- "thermal bus"
+ "thermal bus",
)
protected val typeInput = new HpTypeInput(
@@ -49,10 +49,10 @@ trait HpTestData extends DefaultTestData {
Quantities.getQuantity(0.0, StandardUnits.ENERGY_PRICE),
Quantities.getQuantity(15.0, StandardUnits.ACTIVE_POWER_IN),
0.97,
- Quantities.getQuantity(11.0, StandardUnits.ACTIVE_POWER_IN)
+ Quantities.getQuantity(11.0, StandardUnits.ACTIVE_POWER_IN),
)
- protected val inputModel = new HpInput(
+ protected val hpInputModel = new HpInput(
UUID.fromString("7832dea4-8703-4b37-8752-e67b86e957df"),
"test hp",
OperatorInput.NO_OPERATOR_ASSIGNED,
@@ -60,7 +60,7 @@ trait HpTestData extends DefaultTestData {
nodeInput,
thermalBusInput,
ReactivePowerCharacteristic.parse("cosPhiFixed:{(0.00,0.98)}"),
- typeInput
+ typeInput,
)
protected val thermalHouse = new ThermalHouseInput(
@@ -71,12 +71,12 @@ trait HpTestData extends DefaultTestData {
Quantities.getQuantity(75, StandardUnits.HEAT_CAPACITY),
Quantities.getQuantity(21.0, StandardUnits.TEMPERATURE),
Quantities.getQuantity(22.0, StandardUnits.TEMPERATURE),
- Quantities.getQuantity(20.0, StandardUnits.TEMPERATURE)
+ Quantities.getQuantity(20.0, StandardUnits.TEMPERATURE),
)
protected val thermalGrid = new ThermalGrid(
thermalBusInput,
Seq(thermalHouse).asJava,
- Seq.empty[ThermalStorageInput].asJava
+ Seq.empty[ThermalStorageInput].asJava,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/model/participant/LoadTestData.scala b/src/test/scala/edu/ie3/simona/test/common/model/participant/LoadTestData.scala
index 1aeb8adc5e..c0985612ba 100644
--- a/src/test/scala/edu/ie3/simona/test/common/model/participant/LoadTestData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/model/participant/LoadTestData.scala
@@ -30,6 +30,6 @@ trait LoadTestData extends LoadInputTestData {
SystemComponent.determineOperationInterval(
simulationStartDate,
simulationEndDate,
- operationTime
+ operationTime,
)
}
diff --git a/src/test/scala/edu/ie3/simona/test/common/result/PowerFlowResultData.scala b/src/test/scala/edu/ie3/simona/test/common/result/PowerFlowResultData.scala
index c04999be92..9b51cb3b86 100644
--- a/src/test/scala/edu/ie3/simona/test/common/result/PowerFlowResultData.scala
+++ b/src/test/scala/edu/ie3/simona/test/common/result/PowerFlowResultData.scala
@@ -13,7 +13,7 @@ import edu.ie3.datamodel.models.result.NodeResult
import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
- Transformer2WResult
+ Transformer2WResult,
}
import edu.ie3.datamodel.models.result.system.PvResult
import edu.ie3.util.TimeUtil
@@ -33,7 +33,7 @@ trait PowerFlowResultData {
dummyTime,
dummyInputModel,
Quantities.getQuantity(10, StandardUnits.ACTIVE_POWER_IN),
- Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN)
+ Quantities.getQuantity(10, StandardUnits.REACTIVE_POWER_IN),
)
val dummyPvResultDataString =
@@ -44,7 +44,7 @@ trait PowerFlowResultData {
dummyTime,
dummyInputModel,
Quantities.getQuantity(1.0, PowerSystemUnits.PU),
- Quantities.getQuantity(10, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(10, PowerSystemUnits.DEGREE_GEOM),
)
val dummyNodeResultString =
@@ -54,7 +54,7 @@ trait PowerFlowResultData {
UUID.fromString("647efb19-ec38-4e01-812b-0d751f0150e8"),
dummyTime,
dummyInputModel,
- true
+ true,
)
val dummySwitchResultString =
@@ -68,7 +68,7 @@ trait PowerFlowResultData {
Quantities.getQuantity(100, PowerSystemUnits.DEGREE_GEOM),
Quantities.getQuantity(100, Units.AMPERE),
Quantities.getQuantity(100, PowerSystemUnits.DEGREE_GEOM),
- 0
+ 0,
)
val dummyTrafo2wResultDataString =
@@ -81,7 +81,7 @@ trait PowerFlowResultData {
Quantities.getQuantity(100, Units.AMPERE),
Quantities.getQuantity(100, PowerSystemUnits.DEGREE_GEOM),
Quantities.getQuantity(100, Units.AMPERE),
- Quantities.getQuantity(100, PowerSystemUnits.DEGREE_GEOM)
+ Quantities.getQuantity(100, PowerSystemUnits.DEGREE_GEOM),
)
val dummyLineResultDataString =
diff --git a/src/test/scala/edu/ie3/simona/test/helper/TableDrivenHelper.scala b/src/test/scala/edu/ie3/simona/test/helper/TableDrivenHelper.scala
new file mode 100644
index 0000000000..f618b9106e
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/test/helper/TableDrivenHelper.scala
@@ -0,0 +1,19 @@
+/*
+ * © 2022. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.test.helper
+
+trait TableDrivenHelper {
+
+ /** Shortcut for Some type to make case tables more concise */
+ val S: Some.type = Some
+
+ /** Shortcut for None type to make case tables more concise */
+ val N: None.type = None
+
+ /** Shortcut for Seq type to make case tables more concise */
+ val L: Seq.type = Seq
+}
diff --git a/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchers.scala b/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchers.scala
index 1afb07811d..1cae268736 100644
--- a/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchers.scala
+++ b/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchers.scala
@@ -6,38 +6,65 @@
package edu.ie3.simona.test.matchers
+import edu.ie3.simona.test.matchers.QuantityMatchers.{
+ QuantityEqualityMatcher,
+ QuantityEquivalenceMatcher,
+}
import edu.ie3.util.quantities.QuantityUtil
+
import javax.measure.Quantity
import org.scalatest.matchers.{MatchResult, Matcher}
/** Trait, to simplify test coding, that is reliant on [[Quantity]] s
*/
trait QuantityMatchers {
- class QuantityMatcher[Q <: Quantity[Q]](right: Quantity[Q], tolerance: Double)
- extends Matcher[Quantity[Q]]
+ def equalWithTolerance[Q <: Quantity[Q]](
+ right: Quantity[Q],
+ tolerance: Double = 1e-10,
+ ) = new QuantityEqualityMatcher(right, tolerance)
+
+ def beEquivalentTo[Q <: Quantity[Q]](
+ right: Quantity[Q],
+ tolerance: Double = 1e-10,
+ ) = new QuantityEquivalenceMatcher(right, tolerance)
+
+}
+
+object QuantityMatchers {
+
+ class QuantityEqualityMatcher[Q <: Quantity[Q]](
+ right: Quantity[Q],
+ tolerance: Double,
+ ) extends Matcher[Quantity[Q]]
with QuantityMatchers {
override def apply(left: Quantity[Q]): MatchResult = MatchResult(
QuantityUtil.equals(left, right, tolerance),
QuantityMatchers.assembleRawFailureMessage(left, right, tolerance),
- QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance)
+ QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance),
)
}
- def equalWithTolerance[Q <: Quantity[Q]](
+ class QuantityEquivalenceMatcher[Q <: Quantity[Q]](
right: Quantity[Q],
- tolerance: Double = 1e-10
- ) = new QuantityMatcher(right, tolerance)
-}
+ tolerance: Double,
+ ) extends Matcher[Quantity[Q]]
+ with QuantityMatchers {
+ override def apply(left: Quantity[Q]): MatchResult = MatchResult(
+ QuantityUtil.isEquivalentAbs(left, right, tolerance),
+ QuantityMatchers.assembleRawFailureMessage(left, right, tolerance),
+ QuantityMatchers.assembleNegatedFailureMessage(left, right, tolerance),
+ )
+ }
-case object QuantityMatchers extends QuantityMatchers {
private def assembleRawFailureMessage[Q <: Quantity[Q]](
lhs: Quantity[Q],
rhs: Quantity[Q],
- tolerance: Double
+ tolerance: Double,
) = s"The quantities $lhs and $rhs differ more than $tolerance in value"
+
private def assembleNegatedFailureMessage[Q <: Quantity[Q]](
lhs: Quantity[Q],
rhs: Quantity[Q],
- tolerance: Double
+ tolerance: Double,
) = s"The quantities $lhs and $rhs differ less than $tolerance in value"
}
diff --git a/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchersSpec.scala b/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchersSpec.scala
new file mode 100644
index 0000000000..86f2d9ede8
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/test/matchers/QuantityMatchersSpec.scala
@@ -0,0 +1,43 @@
+/*
+ * © 2021. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.test.matchers
+
+import edu.ie3.simona.test.common.UnitSpec
+import tech.units.indriya.quantity.Quantities
+import tech.units.indriya.unit.Units
+
+class QuantityMatchersSpec extends UnitSpec {
+ "Testing quantities with custom quantity matchers" when {
+ val quant = Quantities.getQuantity(5d, Units.METRE)
+ val toleranceQuantity = Quantities.getQuantity(1e-10, Units.METRE)
+
+ val testTolerance = 1e-10
+
+ "testing for equality" should {
+ "pass if quantities are exactly the same" in {
+ quant should equalWithTolerance(quant, testTolerance)
+ }
+
+ "pass if quantities are approximately the same" in {
+ quant should equalWithTolerance(
+ quant.add(toleranceQuantity.multiply(0.9)),
+ testTolerance,
+ )
+ }
+
+ "detect mismatch on tolerance exceeding" in {
+ quant should not(
+ equalWithTolerance(
+ quant.add(toleranceQuantity.multiply(1.1)),
+ testTolerance,
+ )
+ )
+ }
+ }
+
+ }
+}
diff --git a/src/test/scala/edu/ie3/simona/test/matchers/SquantsMatchers.scala b/src/test/scala/edu/ie3/simona/test/matchers/SquantsMatchers.scala
new file mode 100644
index 0000000000..54cd678ed2
--- /dev/null
+++ b/src/test/scala/edu/ie3/simona/test/matchers/SquantsMatchers.scala
@@ -0,0 +1,25 @@
+/*
+ * © 2024. TU Dortmund University,
+ * Institute of Energy Systems, Energy Efficiency and Energy Economics,
+ * Research group Distribution grid planning and operation
+ */
+
+package edu.ie3.simona.test.matchers
+
+import org.scalatest.matchers.{MatchResult, Matcher}
+import squants.Quantity
+
+/** Trait, to simplify test coding, that is reliant on squants */
+trait SquantsMatchers {
+ class SquantsMatcher[Q <: Quantity[Q]](right: Q, implicit val tolerance: Q)
+ extends Matcher[Quantity[Q]] {
+ override def apply(left: Quantity[Q]): MatchResult = MatchResult(
+ left =~ right,
+ s"The quantities $left and $right differ more than $tolerance in value",
+ s"The quantities $left and $right differ less than $tolerance in value",
+ )
+ }
+
+ def approximate[Q <: Quantity[Q]](right: Q)(implicit tolerance: Q) =
+ new SquantsMatcher(right, tolerance)
+}
diff --git a/src/test/scala/edu/ie3/simona/util/ActorUtils.scala b/src/test/scala/edu/ie3/simona/util/ActorUtils.scala
index a7466c126a..986a7b6de7 100644
--- a/src/test/scala/edu/ie3/simona/util/ActorUtils.scala
+++ b/src/test/scala/edu/ie3/simona/util/ActorUtils.scala
@@ -20,7 +20,7 @@ object ActorUtils {
def expectActivationAndComplete(
scheduler: ActorRef[SchedulerMessage],
expectedTick: Long,
- newTick: Option[Long] = None
+ newTick: Option[Long] = None,
): Unit = {
val receivedTrigger =
triggeredActor.expectMessageType[Activation]
@@ -29,7 +29,7 @@ object ActorUtils {
scheduler ! Completion(
triggeredActor.ref,
- newTick
+ newTick,
)
}
diff --git a/src/test/scala/edu/ie3/simona/util/CollectionUtilsSpec.scala b/src/test/scala/edu/ie3/simona/util/CollectionUtilsSpec.scala
index 2553818567..d96c5da9af 100644
--- a/src/test/scala/edu/ie3/simona/util/CollectionUtilsSpec.scala
+++ b/src/test/scala/edu/ie3/simona/util/CollectionUtilsSpec.scala
@@ -29,7 +29,7 @@ class CollectionUtilsSpec extends UnitSpec {
List(
(Each(1d), Each(2d)),
(Each(2d), Each(4d)),
- (Each(3d), Each(8d))
+ (Each(3d), Each(8d)),
)
)
@@ -45,11 +45,11 @@ class CollectionUtilsSpec extends UnitSpec {
returnedSequence1 shouldBe Seq(
(Each(1d), Each(2d)),
- (Each(2d), Each(4d))
+ (Each(2d), Each(4d)),
)
returnedSequence2 shouldBe Seq(
(Each(2d), Each(4d)),
- (Each(3d), Each(8d))
+ (Each(3d), Each(8d)),
)
returnedSequence3 shouldBe Seq((Each(3d), Each(8d)))
}
diff --git a/src/test/scala/edu/ie3/simona/util/ConfigUtilSpec.scala b/src/test/scala/edu/ie3/simona/util/ConfigUtilSpec.scala
index 931ae996c5..c04877efeb 100644
--- a/src/test/scala/edu/ie3/simona/util/ConfigUtilSpec.scala
+++ b/src/test/scala/edu/ie3/simona/util/ConfigUtilSpec.scala
@@ -11,7 +11,7 @@ import edu.ie3.datamodel.models.result.connector.{
LineResult,
SwitchResult,
Transformer2WResult,
- Transformer3WResult
+ Transformer3WResult,
}
import edu.ie3.datamodel.models.result.system.{ChpResult, LoadResult}
import edu.ie3.datamodel.models.result.{NodeResult, ResultEntity}
@@ -25,7 +25,7 @@ import edu.ie3.simona.util.ConfigUtil.{
GridOutputConfigUtil,
NotifierIdentifier,
ParticipantConfigUtil,
- OutputConfigUtil
+ OutputConfigUtil,
}
import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor2}
@@ -67,7 +67,7 @@ class ConfigUtilSpec
scaling,
uuids,
modelBehaviour,
- reference
+ reference,
)
) =>
calculateMissingReactivePowerWithModel shouldBe false
@@ -126,7 +126,7 @@ class ConfigUtilSpec
scaling,
uuids,
modelBehaviour,
- reference
+ reference,
)
) =>
calculateMissingReactivePowerWithModel shouldBe false
@@ -242,7 +242,7 @@ class ConfigUtilSpec
1.3,
List("49f250fa-41ff-4434-a083-79c98d260a76"),
"profile",
- "power"
+ "power",
)
actual.getOrDefault[LoadRuntimeConfig](
UUID.fromString("fb8f1443-1843-4ecd-a94a-59be8148397f")
@@ -252,7 +252,7 @@ class ConfigUtilSpec
1.5,
List("fb8f1443-1843-4ecd-a94a-59be8148397f"),
"random",
- "energy"
+ "energy",
)
}
}
@@ -286,7 +286,7 @@ class ConfigUtilSpec
FixedFeedInRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
) =>
calculateMissingReactivePowerWithModel shouldBe false
@@ -337,7 +337,7 @@ class ConfigUtilSpec
FixedFeedInRuntimeConfig(
calculateMissingReactivePowerWithModel,
scaling,
- uuids
+ uuids,
)
) =>
calculateMissingReactivePowerWithModel shouldBe false
@@ -439,7 +439,7 @@ class ConfigUtilSpec
FixedFeedInRuntimeConfig(
calculateMissingReactivePowerWithModel = false,
1.3,
- List("49f250fa-41ff-4434-a083-79c98d260a76")
+ List("49f250fa-41ff-4434-a083-79c98d260a76"),
)
actual.getOrDefault[FixedFeedInRuntimeConfig](
UUID.fromString("fb8f1443-1843-4ecd-a94a-59be8148397f")
@@ -447,7 +447,7 @@ class ConfigUtilSpec
FixedFeedInRuntimeConfig(
calculateMissingReactivePowerWithModel = false,
1.5,
- List("fb8f1443-1843-4ecd-a94a-59be8148397f")
+ List("fb8f1443-1843-4ecd-a94a-59be8148397f"),
)
}
}
@@ -538,7 +538,7 @@ class ConfigUtilSpec
FixedFeedInRuntimeConfig(
calculateMissingReactivePowerWithModel = false,
1.0,
- List("default")
+ List("default"),
)
// return default if a request for load is done, but fixed feed is found
@@ -550,7 +550,7 @@ class ConfigUtilSpec
1.0,
List("default"),
"profile",
- "power"
+ "power",
)
// return default if a request for pv is done, but fixed feed is found
@@ -560,7 +560,7 @@ class ConfigUtilSpec
PvRuntimeConfig(
calculateMissingReactivePowerWithModel = false,
1.0,
- List("default")
+ List("default"),
)
}
}
@@ -572,27 +572,27 @@ class ConfigUtilSpec
("config", "expected"),
(
new GridOutputConfig(false, false, "grid", false, false, false),
- Set.empty[Class[_ <: ResultEntity]]
+ Set.empty[Class[_ <: ResultEntity]],
),
(
new GridOutputConfig(true, false, "grid", false, false, false),
- Set(classOf[LineResult])
+ Set(classOf[LineResult]),
),
(
new GridOutputConfig(false, true, "grid", false, false, false),
- Set(classOf[NodeResult])
+ Set(classOf[NodeResult]),
),
(
new GridOutputConfig(false, false, "grid", true, false, false),
- Set(classOf[SwitchResult])
+ Set(classOf[SwitchResult]),
),
(
new GridOutputConfig(false, false, "grid", false, true, false),
- Set(classOf[Transformer2WResult])
+ Set(classOf[Transformer2WResult]),
),
(
new GridOutputConfig(false, false, "grid", false, false, true),
- Set(classOf[Transformer3WResult])
+ Set(classOf[Transformer3WResult]),
),
(
new GridOutputConfig(true, true, "grid", true, true, true),
@@ -601,9 +601,9 @@ class ConfigUtilSpec
classOf[NodeResult],
classOf[SwitchResult],
classOf[Transformer2WResult],
- classOf[Transformer3WResult]
- )
- )
+ classOf[Transformer3WResult],
+ ),
+ ),
)
forAll(ddt) {
@@ -620,25 +620,29 @@ class ConfigUtilSpec
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "default",
powerRequestReply = false,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
List(
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = false,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "pv",
powerRequestReply = false,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "chp",
powerRequestReply = false,
- simulationResult = false
- )
- )
+ simulationResult = false,
+ flexResult = false,
+ ),
+ ),
)
"build the correct map on valid input" in {
@@ -646,21 +650,25 @@ class ConfigUtilSpec
inside(configUtil) { case OutputConfigUtil(default, configs) =>
default shouldBe NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
configs shouldBe Map(
Load -> NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
),
PvPlant -> NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
),
ChpPlant -> NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
- )
+ powerRequestReply = false,
+ flexResult = false,
+ ),
)
}
}
@@ -670,14 +678,16 @@ class ConfigUtilSpec
val actual = configUtil.getOrDefault(PvPlant)
actual shouldBe NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
}
"return default config, when the requested model type is not apparent" in {
configUtil.getOrDefault(Wec) shouldBe NotifierConfig(
simulationResultInfo = false,
- powerRequestReply = false
+ powerRequestReply = false,
+ flexResult = false,
)
}
@@ -686,25 +696,29 @@ class ConfigUtilSpec
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "default",
powerRequestReply = false,
- simulationResult = true
+ simulationResult = true,
+ flexResult = false,
),
List(
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = true,
- simulationResult = true
+ simulationResult = true,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "pv",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "chp",
powerRequestReply = true,
- simulationResult = true
- )
- )
+ simulationResult = true,
+ flexResult = false,
+ ),
+ ),
)
val configUtil = OutputConfigUtil(inputConfig)
val expectedResult: Set[Value] = NotifierIdentifier.values -- Vector(
@@ -719,25 +733,29 @@ class ConfigUtilSpec
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "default",
powerRequestReply = false,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
List(
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = true,
- simulationResult = true
+ simulationResult = true,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "pv",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "chp",
powerRequestReply = true,
- simulationResult = true
- )
- )
+ simulationResult = true,
+ flexResult = false,
+ ),
+ ),
)
val configUtil = OutputConfigUtil(inputConfig)
val expectedResult: Set[Value] =
@@ -751,25 +769,29 @@ class ConfigUtilSpec
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "default",
powerRequestReply = false,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
List(
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "load",
powerRequestReply = true,
- simulationResult = true
+ simulationResult = true,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "pv",
powerRequestReply = true,
- simulationResult = false
+ simulationResult = false,
+ flexResult = false,
),
SimonaConfig.ParticipantBaseOutputConfig(
notifier = "chp",
powerRequestReply = true,
- simulationResult = true
- )
- )
+ simulationResult = true,
+ flexResult = false,
+ ),
+ ),
)
val configUtil = OutputConfigUtil(inputConfig)
val expectedResult: Set[Class[_ <: ResultEntity]] =
@@ -788,9 +810,9 @@ class ConfigUtilSpec
0,
"-not-a-uuid-",
"https://reg:123",
- "topic"
+ "topic",
),
- Seq("topic")
+ Seq("topic"),
)
}.getMessage shouldBe "The UUID '-not-a-uuid-' cannot be parsed as it is invalid."
}
@@ -803,9 +825,9 @@ class ConfigUtilSpec
0,
"00000000-0000-0000-0000-000000000000",
"https://reg:123",
- "topic"
+ "topic",
),
- Seq("topic")
+ Seq("topic"),
)
}.getMessage shouldBe "Exception creating kafka client for broker not#a#server."
}
@@ -818,9 +840,9 @@ class ConfigUtilSpec
0,
"00000000-0000-0000-0000-000000000000",
"https://reg:123",
- "topic"
+ "topic",
),
- Seq("topic")
+ Seq("topic"),
)
}.getMessage shouldBe "Connection with kafka broker localhost:12345 failed."
}
diff --git a/src/test/scala/edu/ie3/simona/util/TestGridFactory.scala b/src/test/scala/edu/ie3/simona/util/TestGridFactory.scala
index 89f0cdf57e..df7be2d0a4 100644
--- a/src/test/scala/edu/ie3/simona/util/TestGridFactory.scala
+++ b/src/test/scala/edu/ie3/simona/util/TestGridFactory.scala
@@ -11,18 +11,18 @@ import edu.ie3.datamodel.models.input.connector.{
LineInput,
SwitchInput,
Transformer2WInput,
- Transformer3WInput
+ Transformer3WInput,
}
import edu.ie3.datamodel.models.input.container.{
GraphicElements,
JointGridContainer,
RawGridElements,
SubGridContainer,
- SystemParticipants
+ SystemParticipants,
}
import edu.ie3.datamodel.models.input.graphics.{
LineGraphicInput,
- NodeGraphicInput
+ NodeGraphicInput,
}
import edu.ie3.datamodel.models.input.system._
@@ -51,13 +51,13 @@ object TestGridFactory {
gridName: String = "TestGrid",
rawGridElements: RawGridElements = createEmptyRawGridElements(),
systemParticipants: SystemParticipants = createEmptySystemParticipants(),
- graphicElements: GraphicElements = createEmptyGraphicElements()
+ graphicElements: GraphicElements = createEmptyGraphicElements(),
): JointGridContainer =
new JointGridContainer(
gridName,
rawGridElements,
systemParticipants,
- graphicElements
+ graphicElements,
)
/** Creates a sub grid container for testing purposes.
@@ -84,14 +84,14 @@ object TestGridFactory {
subgrid: Int = 100,
rawGridElements: RawGridElements = createEmptyRawGridElements(),
systemParticipants: SystemParticipants = createEmptySystemParticipants(),
- graphicElements: GraphicElements = createEmptyGraphicElements()
+ graphicElements: GraphicElements = createEmptyGraphicElements(),
): SubGridContainer =
new SubGridContainer(
gridName,
subgrid,
rawGridElements,
systemParticipants,
- graphicElements
+ graphicElements,
)
def createEmptyRawGridElements(): RawGridElements =
@@ -101,7 +101,7 @@ object TestGridFactory {
Set.empty[Transformer2WInput].asJava,
Set.empty[Transformer3WInput].asJava,
Set.empty[SwitchInput].asJava,
- Set.empty[MeasurementUnitInput].asJava
+ Set.empty[MeasurementUnitInput].asJava,
)
def createEmptySystemParticipants(): SystemParticipants =
@@ -116,12 +116,12 @@ object TestGridFactory {
Set.empty[PvInput].asJava,
Set.empty[StorageInput].asJava,
Set.empty[WecInput].asJava,
- Set.empty[EmInput].asJava
+ Set.empty[EmInput].asJava,
)
def createEmptyGraphicElements(): GraphicElements =
new GraphicElements(
Set.empty[NodeGraphicInput].asJava,
- Set.empty[LineGraphicInput].asJava
+ Set.empty[LineGraphicInput].asJava,
)
}
diff --git a/src/test/scala/edu/ie3/util/quantities/IrradianceSpec.scala b/src/test/scala/edu/ie3/util/quantities/IrradianceSpec.scala
index 37759008e5..a9c9eabe7c 100644
--- a/src/test/scala/edu/ie3/util/quantities/IrradianceSpec.scala
+++ b/src/test/scala/edu/ie3/util/quantities/IrradianceSpec.scala
@@ -8,7 +8,7 @@ package edu.ie3.util.quantities
import edu.ie3.util.scala.quantities.{
WattHoursPerSquareMeter,
- WattsPerSquareMeter
+ WattsPerSquareMeter,
}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
diff --git a/src/test/scala/edu/ie3/util/quantities/IrradiationSpec.scala b/src/test/scala/edu/ie3/util/quantities/IrradiationSpec.scala
index a2e7663864..30a2e0bff6 100644
--- a/src/test/scala/edu/ie3/util/quantities/IrradiationSpec.scala
+++ b/src/test/scala/edu/ie3/util/quantities/IrradiationSpec.scala
@@ -8,7 +8,7 @@ package edu.ie3.util.quantities
import edu.ie3.util.scala.quantities.{
WattHoursPerSquareMeter,
- WattsPerSquareMeter
+ WattsPerSquareMeter,
}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
diff --git a/src/test/scala/edu/ie3/util/quantities/QuantityUtilSpec.scala b/src/test/scala/edu/ie3/util/quantities/QuantityUtilSpec.scala
index 549b76a826..1c1edb1f44 100644
--- a/src/test/scala/edu/ie3/util/quantities/QuantityUtilSpec.scala
+++ b/src/test/scala/edu/ie3/util/quantities/QuantityUtilSpec.scala
@@ -24,7 +24,7 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
2L -> unit(5d),
4L -> unit(15d),
6L -> unit(-5d),
- 8L -> unit(-10d)
+ 8L -> unit(-10d),
)
"Integrating over quantities" when {
@@ -36,7 +36,7 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
intercept[QuantityException] {
QuantityUtil invokePrivate startingValue(
Map.empty[Long, Power],
- 1L
+ 1L,
)
}.getMessage shouldBe "Unable to determine unit for dummy starting value."
}
@@ -44,7 +44,7 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
"bring default value, if there is nothing before window starts" in {
QuantityUtil invokePrivate startingValue(
values,
- 1L
+ 1L,
) should be
unit(0d)
@@ -53,7 +53,7 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
"bring correct value, if there is something before window starts" in {
QuantityUtil invokePrivate startingValue(
values,
- 2L
+ 2L,
) should be
unit(5d)
@@ -74,7 +74,7 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
QuantityUtil invokePrivate endingValue(values, 2L) match {
case (tick, value) =>
tick shouldBe 2L
- (value =~ unit(5d)) shouldBe true
+ value should approximate(unit(5d))
}
}
}
@@ -86,14 +86,14 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
(1L, 3L, integrationUnit(5d)),
(2L, 4L, integrationUnit(10d)),
(2L, 8L, integrationUnit(30d)),
- (0L, 12L, integrationUnit(-10d))
+ (0L, 12L, integrationUnit(-10d)),
)
forAll(cases) { (windowStart, windowEnd, expectedResult) =>
QuantityUtil.integrate[Power, Energy](
values,
windowStart,
- windowEnd
+ windowEnd,
) =~ expectedResult
}
}
@@ -106,14 +106,14 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
QuantityUtil.average[Power, Energy](
values,
0L,
- 0L
+ 0L,
) match {
case Failure(exception: IllegalArgumentException) =>
exception.getMessage shouldBe "Cannot average over trivial time window."
case Failure(exception) =>
fail(
"Averaging over values failed with wrong exception.",
- exception
+ exception,
)
case Success(_) =>
fail("Averaging with trivial window length should fail")
@@ -124,14 +124,14 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
QuantityUtil.average[Power, Energy](
values,
3L,
- 0L
+ 0L,
) match {
case Failure(exception: IllegalArgumentException) =>
exception.getMessage shouldBe "Window end is before window start."
case Failure(exception) =>
fail(
"Averaging over values failed with wrong exception.",
- exception
+ exception,
)
case Success(_) =>
fail("Averaging with flipped window start / end should fail")
@@ -146,21 +146,21 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
(1L, 3L, unit(2.5d)),
(2L, 4L, unit(5d)),
(2L, 8L, unit(5d)),
- (0L, 12L, unit(-0.8333333))
+ (0L, 12L, unit(-0.8333333)),
)
forAll(cases) { (windowStart, windowEnd, expectedResult) =>
QuantityUtil.average[Power, Energy](
values,
windowStart,
- windowEnd
+ windowEnd,
) match {
case Success(result) =>
- result =~ expectedResult
+ result should approximate(expectedResult)
case Failure(exception) =>
fail(
"Averaging with fine input should pass, but failed.",
- exception
+ exception,
)
}
}
diff --git a/src/test/scala/edu/ie3/util/quantities/SpecificHeatCapacitySpec.scala b/src/test/scala/edu/ie3/util/quantities/SpecificHeatCapacitySpec.scala
index d2a5aef2f8..adf15f89b6 100644
--- a/src/test/scala/edu/ie3/util/quantities/SpecificHeatCapacitySpec.scala
+++ b/src/test/scala/edu/ie3/util/quantities/SpecificHeatCapacitySpec.scala
@@ -39,7 +39,7 @@ class SpecificHeatCapacitySpec extends AnyFlatSpec with Matchers {
KilowattHoursPerKelvinCubicMeters(1000).calcEnergy(
Kelvin(10),
Kelvin(20),
- CubicMeters(5)
+ CubicMeters(5),
) should be(KilowattHours(50000.0))
}
@@ -47,7 +47,7 @@ class SpecificHeatCapacitySpec extends AnyFlatSpec with Matchers {
KilowattHoursPerKelvinCubicMeters(1000).calcEnergy(
Celsius(100),
Celsius(101),
- CubicMeters(5)
+ CubicMeters(5),
) should be(KilowattHours(5000))
}
}
diff --git a/src/test/scala/edu/ie3/util/quantities/ThermalConductanceSpec.scala b/src/test/scala/edu/ie3/util/quantities/ThermalConductanceSpec.scala
index fd2e290f16..7b71137d69 100644
--- a/src/test/scala/edu/ie3/util/quantities/ThermalConductanceSpec.scala
+++ b/src/test/scala/edu/ie3/util/quantities/ThermalConductanceSpec.scala
@@ -36,7 +36,7 @@ class ThermalConductanceSpec extends AnyFlatSpec with Matchers {
WattsPerKelvin(1000).thermalConductanceToEnergy(
Kelvin(10),
Kelvin(0),
- Hours(5)
+ Hours(5),
) should be(KilowattHours(50d))
}
@@ -44,7 +44,7 @@ class ThermalConductanceSpec extends AnyFlatSpec with Matchers {
WattsPerKelvin(1000).thermalConductanceToEnergy(
Celsius(10),
Celsius(0),
- Hours(5)
+ Hours(5),
) should be(KilowattHours(50d))
}
}