Skip to content

Commit

Permalink
Merge branch 'dev' into df/#965-compression-at-resultEventListener
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
  • Loading branch information
danielfeismann committed Oct 1, 2024
2 parents cf6d3e4 + 6c75d5c commit f52bf82
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 134 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplifying ThermalHouse [#940](https://github.com/ie3-institute/simona/issues/940)
- Prepare ThermalStorageTestData for Storage without storageVolumeLvlMin [#894](https://github.com/ie3-institute/simona/issues/894)
- Renamed `ActivityStartTrigger`, `ScheduleTriggerMessage`, `CompletionMessage` in UML Diagrams[#675](https://github.com/ie3-institute/simona/issues/675)
- Simplifying quantity integration in QuantityUtil [#973](https://github.com/ie3-institute/simona/issues/973)
- Move compression of output files into `ResultEventListener`[#965](https://github.com/ie3-institute/simona/issues/965)

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ ext {
jtsVersion = '1.20.0'
confluentKafkaVersion = '7.4.0'
tscfgVersion = '1.1.3'
scapegoatVersion = '3.0.2'
scapegoatVersion = '3.0.3'

testContainerVersion = '0.41.4'

Expand Down Expand Up @@ -103,7 +103,7 @@ dependencies {
/* 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.13.0' // mocking framework
testImplementation 'org.mockito:mockito-core:5.14.1' // mocking framework
testImplementation "org.scalatest:scalatest_${scalaVersion}:3.2.19"
testRuntimeOnly 'com.vladsch.flexmark:flexmark-all:0.64.8' //scalatest html output
testImplementation group: 'org.pegdown', name: 'pegdown', version: '1.6.0'
Expand Down
106 changes: 27 additions & 79 deletions src/main/scala/edu/ie3/util/scala/quantities/QuantityUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
package edu.ie3.util.scala.quantities

import edu.ie3.simona.exceptions.QuantityException
import edu.ie3.util.quantities.{QuantityUtil => PSQuantityUtil}
import squants.time.{Hours, TimeDerivative, TimeIntegral}
import squants.{Quantity, Seconds, UnitOfMeasure}
import tech.units.indriya.ComparableQuantity
import tech.units.indriya.function.Calculus
import tech.units.indriya.quantity.Quantities

import scala.collection.mutable
import scala.collection.immutable.SortedMap
import scala.util.{Failure, Try}

object QuantityUtil {
Expand Down Expand Up @@ -120,27 +119,39 @@ object QuantityUtil {
lastValue: Q,
)

/* Determine the starting and ending value for the integral */
val startValue = startingValue(values, windowStart)
val (lastTick, lastValue) = endingValue(values, windowEnd)
val valuesWithinWindow = mutable.LinkedHashMap.newBuilder
.addAll(
(values filter { case (tick, _) =>
tick >= windowStart && tick <= windowEnd
}).toSeq
.sortBy(_._1)
val sortedValues = SortedMap.from(values)

/* Determine the unit from the first best value */
val unit = sortedValues.values.headOption
.map(_.unit)
.getOrElse(
throw new QuantityException(
"Unable to determine unit for dummy starting value."
)
)
.result()
val zeroValue = unit(0d)

/* the first relevant value for integration is placed before or at windowStart */
val startValue = sortedValues
.rangeUntil(windowStart + 1)
.lastOption
.map { case (_, value) =>
value
}
.getOrElse(zeroValue)

/* We need a value at the window end, so if the last value is not exactly there, replicate it at that point */
if (windowEnd > lastTick)
valuesWithinWindow.addOne(windowEnd -> lastValue)
/* Filtering out values outside the specified time window.
Excluding the value at first tick because the fold below starts with it.
At the end, we add a dummy value (we only care about the ending tick).
*/
val valuesWithinWindow = sortedValues.range(windowStart + 1, windowEnd) +
(windowEnd -> zeroValue)

/* Actually determining the integral, but sweeping over values and summing up everything */
valuesWithinWindow
.foldLeft(
IntegrationState(
startValue * Hours(0),
zeroValue * Hours(0),
windowStart,
startValue,
)
Expand All @@ -158,67 +169,4 @@ object QuantityUtil {
.currentIntegral
}

/** Determine the starting value for the integration
*
* @param values
* Mapping of ticks to values
* @param windowStart
* Tick, where the integration window starts
* @tparam Q
* Type of quantity to account for
* @return
* Either the first value <b>before</b> the window starts or 0, if not
* apparent
*/
private def startingValue[Q <: squants.Quantity[Q]](
values: Map[Long, Q],
windowStart: Long,
): Q = {
values
.filter { case (tick, _) =>
tick <= windowStart
}
.maxOption[(Long, Q)](Ordering.by(_._1)) match {
case Some((_, value)) => value
case None =>
val unit = values.headOption
.map(_._2.unit)
.getOrElse(
throw new QuantityException(
"Unable to determine unit for dummy starting value."
)
)
unit(0d)
}
}

/** Determine the last value for the integration
*
* @param values
* Mapping of ticks to values
* @param windowEnd
* Tick, where the integration window ends
* @tparam Q
* Type of quantity to account for
* @return
* Last entry before the integration window ends and it's corresponding
* tick
*/
private def endingValue[Q <: Quantity[Q]](
values: Map[Long, Q],
windowEnd: Long,
): (Long, Q) = {
values
.filter { case (tick, _) =>
tick <= windowEnd
}
.maxOption[(Long, Q)](Ordering.by(_._1)) match {
case Some(tickToValue) => tickToValue
case None =>
throw new QuantityException(
"Cannot integrate over an empty set of values."
)
}
}

}
54 changes: 1 addition & 53 deletions src/test/scala/edu/ie3/util/quantities/QuantityUtilSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

package edu.ie3.util.quantities

import edu.ie3.simona.exceptions.QuantityException
import edu.ie3.simona.test.common.UnitSpec
import edu.ie3.util.scala.quantities.QuantityUtil
import org.scalatest.prop.TableDrivenPropertyChecks
Expand All @@ -28,57 +27,6 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
)

"Integrating over quantities" when {
"determining the start value" should {
val startingValue =
PrivateMethod[Power](Symbol("startingValue"))

"throw an exception, if values are empty and unit of \"empty\" quantity cannot be determined" in {
intercept[QuantityException] {
QuantityUtil invokePrivate startingValue(
Map.empty[Long, Power],
1L,
)
}.getMessage shouldBe "Unable to determine unit for dummy starting value."
}

"bring default value, if there is nothing before window starts" in {
QuantityUtil invokePrivate startingValue(
values,
1L,
) should be
unit(0d)

}

"bring correct value, if there is something before window starts" in {
QuantityUtil invokePrivate startingValue(
values,
2L,
) should be
unit(5d)

}
}

"determining the end value" should {
val endingValue =
PrivateMethod[(Long, Power)](Symbol("endingValue"))

"throw and exception, if there is no value before the window ends" in {
intercept[QuantityException] {
QuantityUtil invokePrivate endingValue(values, 1L)
}.getMessage shouldBe "Cannot integrate over an empty set of values."
}

"bring correct value, if there is something before window ends" in {
QuantityUtil invokePrivate endingValue(values, 2L) match {
case (tick, value) =>
tick shouldBe 2L
value should approximate(unit(5d))
}
}
}

"actually integrating" should {
"lead to correct values" in {
val cases = Table(
Expand All @@ -94,7 +42,7 @@ class QuantityUtilSpec extends UnitSpec with TableDrivenPropertyChecks {
values,
windowStart,
windowEnd,
) =~ expectedResult
) should approximate(expectedResult)
}
}
}
Expand Down

0 comments on commit f52bf82

Please sign in to comment.