Skip to content

Commit

Permalink
Merge pull request #79 from ie3-institute/ck/#78-weatherSourceDefault…
Browse files Browse the repository at this point in the history
…Resolution

Adapt default resolution of weather source
  • Loading branch information
danielfeismann authored Apr 7, 2022
2 parents ad824fc + 47530b3 commit f10db48
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 66 deletions.
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 @@ -363,26 +358,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

0 comments on commit f10db48

Please sign in to comment.