Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Re-organizing test resources into their respective packages [#105](https://github.com/ie3-institute/simona/issues/105)
- BREAKING: Using snapshot version of PSDM
- Simplified PrimaryServiceProxy due to changes in PSDM [#120](https://github.com/ie3-institute/simona/issues/120)
- Improved handling of weights and their sum in determination of weather data [#173](https://github.com/ie3-institute/simona/issues/173)

### Fixed
- Location of `vn_simona` test grid (was partially in Berlin and Dortmund)
- Let `ParticipantAgent` die after failed registration with secondary services (prevents stuck simulation)
- Fix default resolution of weather source wrapper [#78](https://github.com/ie3-institute/simona/issues/78)

[Unreleased]: https://github.com/ie3-institute/simona/compare/a14a093239f58fca9b2b974712686b33e5e5f939...HEAD
6 changes: 3 additions & 3 deletions docs/uml/main/ParticipantModelling.puml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ package edu.ie3.edu.ie3.simona {
}
DateTime --|> SecondaryData

Class Weather{
+ diffRad: Quantity[Irradiation]
+ dirRad: Quantity[Irradiation]
Class WeatherData{
+ diffIrr: Quantity[Irradiation]
+ dirIrr: Quantity[Irradiation]
+ temp: Quantity[Temperature]
+ windVel: Quantity[Speed]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ protected trait PVAgentFundamentals
PVRelevantData(
dateTime,
tickInterval,
weatherData.diffRad,
weatherData.dirRad
weatherData.diffIrr,
weatherData.dirIrr
)

val power = pvModel.calculatePower(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,21 @@ object WeatherMessage {
) extends WeatherMessage
with ProvisionMessage[WeatherData]

/** Hold entire weather result together
/** Container class for the entirety of weather information at a certain point
* in time and at a certain coordinate
*
* @param diffIrr
* Diffuse irradiance on the horizontal pane
* @param dirIrr
* Direct irradiance on the horizontal pane
* @param temp
* Temperature
* @param windVel
* Wind velocity
*/
final case class WeatherData(
diffRad: ComparableQuantity[Irradiance],
dirRad: ComparableQuantity[Irradiance],
diffIrr: ComparableQuantity[Irradiance],
dirIrr: ComparableQuantity[Irradiance],
temp: ComparableQuantity[Temperature],
windVel: ComparableQuantity[Speed]
) extends SecondaryData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ trait WeatherSource {
}
}

/** Determine the weights of each coordinate
/** Determine the weights of each coordinate. It is ensured, that the entirety
* of weights sum up to 1.0
*
* @param nearestCoordinates
* Collection of nearest coordinates with their distances
Expand Down Expand Up @@ -522,9 +523,9 @@ object WeatherSource {
): WeatherData = {
WeatherData(
weatherValue.getSolarIrradiance.getDiffuseIrradiance
.orElse(EMPTY_WEATHER_DATA.diffRad),
.orElse(EMPTY_WEATHER_DATA.diffIrr),
weatherValue.getSolarIrradiance.getDirectIrradiance
.orElse(EMPTY_WEATHER_DATA.dirRad),
.orElse(EMPTY_WEATHER_DATA.dirIrr),
weatherValue.getTemperature.getTemperature
.orElse(EMPTY_WEATHER_DATA.temp),
weatherValue.getWind.getVelocity.orElse(EMPTY_WEATHER_DATA.windVel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import edu.ie3.datamodel.io.connectors.{
SqlConnector
}
import edu.ie3.datamodel.io.factory.timeseries.{
IconTimeBasedWeatherValueFactory,
CosmoTimeBasedWeatherValueFactory
CosmoTimeBasedWeatherValueFactory,
IconTimeBasedWeatherValueFactory
}
import edu.ie3.datamodel.io.naming.FileNamingStrategy
import edu.ie3.datamodel.io.source.couchbase.CouchbaseWeatherSource
Expand Down Expand Up @@ -45,7 +45,9 @@ import edu.ie3.simona.util.TickUtil
import edu.ie3.simona.util.TickUtil.TickLong
import edu.ie3.util.exceptions.EmptyQuantityException
import edu.ie3.util.interval.ClosedInterval
import edu.ie3.util.scala.DoubleUtils.ImplicitDouble
import tech.units.indriya.quantity.Quantities
import tech.units.indriya.unit.Units

import java.time.ZonedDateTime
import javax.measure.Quantity
Expand Down Expand Up @@ -140,21 +142,21 @@ private[weather] final case class WeatherSourceWrapper private (
)

/* Determine actual weights and contributions */
val (diffRadContrib, diffRadWeight) = currentWeather.diffRad match {
case EMPTY_WEATHER_DATA.diffRad => (EMPTY_WEATHER_DATA.diffRad, 0d)
case nonEmptyDiffRad =>
val (diffIrrContrib, diffIrrWeight) = currentWeather.diffIrr match {
case EMPTY_WEATHER_DATA.diffIrr => (EMPTY_WEATHER_DATA.diffIrr, 0d)
case nonEmptyDiffIrr =>
calculateContrib(
nonEmptyDiffRad,
nonEmptyDiffIrr,
weight,
StandardUnits.SOLAR_IRRADIANCE,
s"Diffuse solar irradiance not available at $point."
)
}
val (dirRadContrib, dirRadWeight) = currentWeather.dirRad match {
case EMPTY_WEATHER_DATA.dirRad => (EMPTY_WEATHER_DATA.dirRad, 0d)
case nonEmptyDirRad =>
val (dirIrrContrib, dirIrrWeight) = currentWeather.dirIrr match {
case EMPTY_WEATHER_DATA.`dirIrr` => (EMPTY_WEATHER_DATA.dirIrr, 0d)
case nonEmptyDirIrr =>
calculateContrib(
nonEmptyDirRad,
nonEmptyDirIrr,
weight,
StandardUnits.SOLAR_IRRADIANCE,
s"Direct solar irradiance not available at $point."
Expand All @@ -164,7 +166,7 @@ private[weather] final case class WeatherSourceWrapper private (
case EMPTY_WEATHER_DATA.temp => (EMPTY_WEATHER_DATA.temp, 0d)
case nonEmptyTemp =>
calculateContrib(
nonEmptyTemp,
nonEmptyTemp.to(Units.KELVIN),
weight,
StandardUnits.TEMPERATURE,
s"Temperature not available at $point."
Expand All @@ -184,29 +186,22 @@ private[weather] final case class WeatherSourceWrapper private (
/* Sum up weight and contributions */
(
WeatherData(
averagedWeather.diffRad.add(diffRadContrib),
averagedWeather.dirRad.add(dirRadContrib),
averagedWeather.diffIrr.add(diffIrrContrib),
averagedWeather.dirIrr.add(dirIrrContrib),
averagedWeather.temp.add(tempContrib),
averagedWeather.windVel.add(windVelContrib)
),
currentWeightSum.add(
diffRadWeight,
dirRadWeight,
diffIrrWeight,
dirIrrWeight,
tempWeight,
windVelWeight
)
)
} match {
case (weatherData: WeatherData, weightSum: WeightSum) =>
/* Divide by weight sum to correctly account for missing data. Change temperature scale back to absolute*/
WeatherData(
weatherData.diffRad.divide(weightSum.diffRad),
weatherData.dirRad.divide(weightSum.dirRad),
weatherData.temp.divide(weightSum.temp),
weatherData.windVel.divide(weightSum.windVel)
)
weightSum.scale(weatherData)
}

}

/** Determine an Array with all ticks between the request frame's start and
Expand All @@ -227,7 +222,7 @@ private[weather] final case class WeatherSourceWrapper private (
}

private[weather] object WeatherSourceWrapper extends LazyLogging {
private val DEFAULT_RESOLUTION = 360L
private val DEFAULT_RESOLUTION = 3600L

def apply(
csvSep: String,
Expand Down Expand Up @@ -382,26 +377,64 @@ private[weather] object WeatherSourceWrapper extends LazyLogging {
)
}

/** Simple container class to allow for accumulating determination of the sum
* of weights for different weather properties for different locations
* surrounding a given coordinate of interest
*
* @param diffIrr
* Sum of weight for diffuse irradiance
* @param dirIrr
* Sum of weight for direct irradiance
* @param temp
* Sum of weight for temperature
* @param windVel
* Sum of weight for wind velocity
*/
final case class WeightSum(
diffRad: Double,
dirRad: Double,
diffIrr: Double,
dirIrr: Double,
temp: Double,
windVel: Double
) {
def add(
diffRad: Double,
dirRad: Double,
diffIrr: Double,
dirIrr: Double,
temp: Double,
windVel: Double
): WeightSum =
WeightSum(
this.diffRad + diffRad,
this.dirRad + dirRad,
this.diffIrr + diffIrr,
this.dirIrr + dirIrr,
this.temp + temp,
this.windVel + windVel
)

/** Scale the given [[WeatherData]] by dividing by the sum of weights per
* attribute of the weather data. If one of the weight sums is empty (and
* thus a division by zero would happen) the defined "empty" information
* for this attribute is returned.
*
* @param weatherData
* Weighted and accumulated weather information
* @return
* Weighted weather information, which are divided by the sum of weights
*/
def scale(weatherData: WeatherData): WeatherData = weatherData match {
case WeatherData(diffIrr, dirIrr, temp, windVel) =>
implicit val precision: Double = 1e-3
WeatherData(
if (this.diffIrr !~= 0d) diffIrr.divide(this.diffIrr)
else EMPTY_WEATHER_DATA.diffIrr,
if (this.dirIrr !~= 0d) dirIrr.divide(this.dirIrr)
else EMPTY_WEATHER_DATA.dirIrr,
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
)
}
}
case object WeightSum {
object WeightSum {
val EMPTY_WEIGHT_SUM: WeightSum = WeightSum(0d, 0d, 0d, 0d)
}

Expand Down
17 changes: 17 additions & 0 deletions src/main/scala/edu/ie3/util/scala/DoubleUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* © 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class PVModelIT extends Specification implements PVModelITHelper {

"build the needed data"
WeatherMessage.WeatherData weather = modelToWeatherMap.get(modelId)
PVModel.PVRelevantData neededData = new PVModel.PVRelevantData(dateTime,3600L, weather.diffRad() as ComparableQuantity<Irradiance>, weather.dirRad() as ComparableQuantity<Irradiance>)
PVModel.PVRelevantData neededData = new PVModel.PVRelevantData(dateTime,3600L, weather.diffIrr() as ComparableQuantity<Irradiance>, weather.dirIrr() as ComparableQuantity<Irradiance>)
ComparableQuantity<Dimensionless> voltage = getQuantity(1.414213562, PU)

"collect the results and calculate the difference between the provided results and the calculated ones"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,8 +585,8 @@ class PVAgentModelCalculationSpec
0L -> PVRelevantData(
0L.toDateTime,
3600L,
weatherData.diffRad,
weatherData.dirRad
weatherData.diffIrr,
weatherData.dirIrr
)
)
}
Expand Down Expand Up @@ -737,8 +737,8 @@ class PVAgentModelCalculationSpec
0L -> PVRelevantData(
0L.toDateTime,
3600L,
weatherData.diffRad,
weatherData.dirRad
weatherData.diffIrr,
weatherData.dirIrr
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ class SampleWeatherSourceSpec
val actual = source invokePrivate getWeatherPrivate(tick)

/* Units meet expectation */
actual.diffRad.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
actual.dirRad.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
actual.diffIrr.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
actual.dirIrr.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
actual.temp.getUnit shouldBe StandardUnits.TEMPERATURE
actual.windVel.getUnit shouldBe StandardUnits.WIND_VELOCITY

/* Values meet expectations */
actual.diffRad should equalWithTolerance(
actual.diffIrr should equalWithTolerance(
Quantities.getQuantity(72.7656, StandardUnits.SOLAR_IRRADIANCE)
)
actual.dirRad should equalWithTolerance(
actual.dirIrr should equalWithTolerance(
Quantities.getQuantity(80.1172, StandardUnits.SOLAR_IRRADIANCE)
)
actual.windVel should equalWithTolerance(
Expand All @@ -108,14 +108,14 @@ class SampleWeatherSourceSpec
WeightedCoordinates(Map(NodeInput.DEFAULT_GEO_POSITION -> 1d))

source.getWeather(tick, weightedCoordinates) match {
case WeatherData(diffRad, dirRad, temp, windVel) =>
diffRad.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
diffRad should equalWithTolerance(
case WeatherData(diffIrr, dirIrr, temp, windVel) =>
diffIrr.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
diffIrr should equalWithTolerance(
Quantities.getQuantity(72.7656, StandardUnits.SOLAR_IRRADIANCE)
)

dirRad.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
dirRad should equalWithTolerance(
dirIrr.getUnit shouldBe StandardUnits.SOLAR_IRRADIANCE
dirIrr should equalWithTolerance(
Quantities.getQuantity(80.1172, StandardUnits.SOLAR_IRRADIANCE)
)

Expand Down
Loading