Skip to content

Commit

Permalink
Adding some test.
Browse files Browse the repository at this point in the history
  • Loading branch information
staudtMarius committed Jul 29, 2024
1 parent a0028b6 commit 17b1223
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ import org.locationtech.jts.geom.Coordinate
import tech.units.indriya.ComparableQuantity
import tech.units.indriya.quantity.Quantities

import java.time.ZonedDateTime
import javax.measure.quantity.{Angle, Dimensionless, Power}
import scala.jdk.CollectionConverters._
import scala.jdk.OptionConverters._

object PvProfileConverter {

Expand Down Expand Up @@ -95,7 +93,7 @@ object PvProfileConverter {
* @param azimuth
* of the [[PvInput]]
* @return
* the azimuth and the elevation angle in radians
* the elevation angle in radians
*/
def calculateElevationAngle(
maxFeedIn: Option[TimeBasedValue[PValue]],
Expand All @@ -105,7 +103,7 @@ object PvProfileConverter {
): ComparableQuantity[Angle] = {
val position: Coordinate = ResProfileConverter.getCoordinate(profile)

// TODO: Adjust angle with power value and rated power
// TODO: Adjust angle with power value, rated power and azimuth
// angle for horizontal irradiance
val angleInDegrees = position.getY - 15
Quantities.getQuantity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,33 @@ import scala.jdk.OptionConverters.RichOptional

object ResProfileConverter {
// default coordinates for two locations
private val hanoverCoordinate: Coordinate =
val hanoverCoordinate: Coordinate =
GeoUtils.buildCoordinate(52.366667, 9.733333)
private val luebeckCoordinate: Coordinate =
val luebeckCoordinate: Coordinate =
GeoUtils.buildCoordinate(53.866667, 10.683333)

/** Determines the maximum feed in of the time series.
* @param timeSeries
* given time series
* @return
* time based value with the maximum feed in
*/
def findMaxFeedIn(
timeSeries: IndividualTimeSeries[PValue]
): Option[TimeBasedValue[PValue]] =
timeSeries.getEntries.asScala.minByOption { value =>
value.getValue.getP.toScala.getOrElse(0.asKiloWatt).getValue.doubleValue()
}
timeSeries.getEntries.asScala
.filter { value =>
value.getValue.getP.toScala
.getOrElse(1.asKiloWatt)
.getValue
.doubleValue() < 0
}
.minByOption { value =>
value.getValue.getP.toScala
.getOrElse(0.asKiloWatt)
.getValue
.doubleValue()
}

/** Determines the [[Coordinate]] of a
* [[edu.ie3.simbench.model.datamodel.RES]] based on its [[ResProfileType]]
Expand All @@ -48,7 +64,7 @@ object ResProfileConverter {
case ResProfileType.PV8 => hanoverCoordinate // hanover (second place)
case other =>
throw ConversionException(
s"There are no coordinates for the profile type $other."
s"There is no coordinate for the profile type $other."
)
}

Expand All @@ -68,7 +84,7 @@ object ResProfileConverter {
case ResProfileType.PV7 | ResProfileType.PV8 => CompassDirection.West
case other =>
throw ConversionException(
s"There are no coordinates for the profile type $other."
s"There is no compass direction for the profile type $other."
)
}

Expand Down
64 changes: 62 additions & 2 deletions src/test/scala/edu/ie3/simbench/convert/ResConverterSpec.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package edu.ie3.simbench.convert

import edu.ie3.datamodel.models.StandardUnits
import edu.ie3.datamodel.models.{OperationTime, StandardUnits}
import edu.ie3.datamodel.models.input.OperatorInput
import edu.ie3.datamodel.models.input.system.characteristic.CosPhiFixed

import java.util.Objects
import java.util.{Locale, Objects}
import edu.ie3.simbench.model.datamodel.profiles.{ResProfile, ResProfileType}
import edu.ie3.test.common.{ConverterTestData, TestTimeUtils, UnitSpec}
import edu.ie3.test.matchers.QuantityMatchers
import edu.ie3.util.quantities.QuantityUtils.RichQuantityDouble
import tech.units.indriya.quantity.Quantities

import scala.jdk.OptionConverters.RichOptional
Expand Down Expand Up @@ -86,5 +89,62 @@ class ResConverterSpec
}
}
}

"convert pv plants correctly" in {

val pvPlant = pvRes
val pvNode = getNodePair("LV4.101 Bus 19")._2

val pvProfile: ResProfile = ResProfile(
"PV profile",
ResProfileType.PV3,
Map(
TestTimeUtils.simbench.toZonedDateTime(
"01.01.1990 00:00"
) -> BigDecimal(
"0.75"
),
TestTimeUtils.simbench.toZonedDateTime(
"01.01.1990 00:15"
) -> BigDecimal(
"0.55"
),
TestTimeUtils.simbench.toZonedDateTime(
"01.01.1990 00:30"
) -> BigDecimal(
"0.35"
),
TestTimeUtils.simbench.toZonedDateTime(
"01.01.1990 00:45"
) -> BigDecimal(
"0.15"
)
)
)

val result = ResConverter.convertPv(
pvPlant,
pvNode,
pvProfile
)

result.getId shouldBe "LV4.101 SGen 1_pv"
result.getOperator shouldBe OperatorInput.NO_OPERATOR_ASSIGNED
result.getOperationTime shouldBe OperationTime.notLimited()
result.getNode shouldBe pvNode
result.getqCharacteristics() shouldBe new CosPhiFixed(
"cosPhiFixed:{(0.0,1.0)}"
)
result.getControllingEm.toScala shouldBe None
result.getAlbedo shouldBe 0.20000000298023224
result.getAzimuth shouldBe 0.asDegreeGeom
result.getEtaConv shouldBe 97.asPercent
result.getElevationAngle shouldBe 0.652171369646312.asDegreeGeom
result.getkG() shouldBe 0.8999999761581421
result.getkT() shouldBe 1.0
result.isMarketReaction shouldBe false
result.getsRated() shouldBe 6.48.asKiloVoltAmpere
result.getCosPhiRated shouldBe 1.0
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package edu.ie3.simbench.convert.profiles

import edu.ie3.datamodel.models.timeseries.individual.{
IndividualTimeSeries,
TimeBasedValue
}
import edu.ie3.datamodel.models.value.PValue
import edu.ie3.simbench.model.datamodel.profiles.ResProfileType
import edu.ie3.test.common.UnitSpec
import edu.ie3.util.quantities.QuantityUtils._
import org.scalatest.prop.TableDrivenPropertyChecks

import java.time.ZonedDateTime
import scala.jdk.CollectionConverters._
import scala.jdk.OptionConverters.RichOptional

class PvProfileConverterSpec extends UnitSpec with TableDrivenPropertyChecks {

"The pv profile converter" should {

"calculate the power before the converter correctly" in {
val time = ZonedDateTime.now()

val timeSeries = new IndividualTimeSeries(
Set(
new TimeBasedValue(time, new PValue((-1).asKiloWatt)),
new TimeBasedValue(time.plusHours(1), new PValue((-3).asKiloWatt)),
new TimeBasedValue(time.plusHours(2), new PValue((-2).asKiloWatt))
).asJava
)

val efficiency = 97.asPercent
val kG: Double = 0.8999999761581421
val kT: Double = 1.0

val result = PvProfileConverter.calculatePowerBeforeConverter(
timeSeries,
efficiency,
kG,
kT
)

val entries = result.getEntries.asScala.toList.map(value =>
value.getValue.getP.toScala
.getOrElse(fail("This test should not fail!"))
)

entries(0).getValue.doubleValue() shouldBe -1.1454754026242313
entries(1).getValue.doubleValue() shouldBe -3.436426207872694
entries(2).getValue.doubleValue() shouldBe -2.2909508052484626
}

"return the correct azimuth for a given pv profile type" in {
val cases = Table(
("profileType", "expectedAzimuth"),
(ResProfileType.PV1, (-90).asDegreeGeom),
(ResProfileType.PV2, (-90).asDegreeGeom),
(ResProfileType.PV3, 0.asDegreeGeom),
(ResProfileType.PV4, 0.asDegreeGeom),
(ResProfileType.PV5, 0.asDegreeGeom),
(ResProfileType.PV6, 0.asDegreeGeom),
(ResProfileType.PV7, 90.asDegreeGeom),
(ResProfileType.PV8, 90.asDegreeGeom)
)

forAll(cases) { (profileType, expectedAzimuth) =>
PvProfileConverter.getAzimuth(profileType) shouldBe expectedAzimuth
}
}

"calculate the elevation angle correctly" in {
val result = PvProfileConverter.calculateElevationAngle(
None,
5.asKiloVoltAmpere,
ResProfileType.PV3,
0.asDegreeGeom
)

result shouldBe 0.652171369646312.asDegreeGeom
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package edu.ie3.simbench.convert.profiles

import edu.ie3.datamodel.models.timeseries.individual.{
IndividualTimeSeries,
TimeBasedValue
}
import edu.ie3.datamodel.models.value.PValue
import edu.ie3.simbench.convert.profiles.ResProfileConverter.{
CompassDirection,
findMaxFeedIn,
hanoverCoordinate,
luebeckCoordinate
}
import edu.ie3.simbench.model.datamodel.profiles.ResProfileType
import edu.ie3.test.common.UnitSpec
import edu.ie3.util.quantities.QuantityUtils._
import org.scalatest.prop.TableDrivenPropertyChecks

import java.time.ZonedDateTime
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Success, Try}

class ResProfileConverterSpec extends UnitSpec with TableDrivenPropertyChecks {

"The res profile converter" should {
"find the maximum feed in of a time series" in {
val time = ZonedDateTime.now()

val value0 = new TimeBasedValue(time, new PValue(1.asKiloWatt))
val value1 =
new TimeBasedValue(time.plusHours(1), new PValue((-1).asKiloWatt))
val value2 = new TimeBasedValue(time.plusHours(2), new PValue(null))
val value3 =
new TimeBasedValue(time.plusHours(3), new PValue((-3).asKiloWatt))
val value4 =
new TimeBasedValue(time.plusHours(4), new PValue((-2).asKiloWatt))

val cases = Table(
("timeSeries", "expectedValue"),
(new IndividualTimeSeries(Set(value0).asJava), None),
(new IndividualTimeSeries(Set(value0, value2).asJava), None),
(
new IndividualTimeSeries(Set(value0, value1, value2).asJava),
Some(value1)
),
(
new IndividualTimeSeries(Set(value1, value2, value4).asJava),
Some(value4)
),
(
new IndividualTimeSeries(Set(value1, value2, value3, value4).asJava),
Some(value3)
)
)

forAll(cases) { (timeSeries, expectedValue) =>
val maxFeedIn = findMaxFeedIn(timeSeries)
maxFeedIn shouldBe expectedValue
}

}

"return the correct coordinate" in {
val cases = Table(
("profileType", "expectedCoordinate"),
(ResProfileType.PV1, hanoverCoordinate),
(ResProfileType.PV2, luebeckCoordinate),
(ResProfileType.PV3, hanoverCoordinate),
(ResProfileType.PV4, hanoverCoordinate),
(ResProfileType.PV5, luebeckCoordinate),
(ResProfileType.PV6, luebeckCoordinate),
(ResProfileType.PV7, hanoverCoordinate),
(ResProfileType.PV8, hanoverCoordinate)
)

forAll(cases) { (profileType, expectedCoordinate) =>
ResProfileConverter.getCoordinate(
profileType
) shouldBe expectedCoordinate
}
}

"throw an exception if unknown profile type is given for coordinate" in {
Try(ResProfileConverter.getCoordinate(ResProfileType.WP1)) match {
case Success(_) => fail("This test should not pass!")
case Failure(exception) =>
exception.getMessage shouldBe s"There is no coordinate for the profile type WP1."
}
}

"return the correct compass direction" in {
val cases = Table(
("profileType", "expectedDirection"),
(ResProfileType.PV1, CompassDirection.East),
(ResProfileType.PV2, CompassDirection.East),
(ResProfileType.PV3, CompassDirection.South),
(ResProfileType.PV4, CompassDirection.South),
(ResProfileType.PV5, CompassDirection.South),
(ResProfileType.PV6, CompassDirection.South),
(ResProfileType.PV7, CompassDirection.West),
(ResProfileType.PV8, CompassDirection.West)
)

forAll(cases) { (profileType, expectedDirection) =>
ResProfileConverter.getCompassDirection(
profileType
) shouldBe expectedDirection
}
}

"throw an exception if unknown profile type is given for compass direction" in {
Try(ResProfileConverter.getCompassDirection(ResProfileType.WP1)) match {
case Success(_) => fail("This test should not pass!")
case Failure(exception) =>
exception.getMessage shouldBe s"There is no compass direction for the profile type WP1."
}
}

}
}
Loading

0 comments on commit 17b1223

Please sign in to comment.