Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test validations #173

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
38 changes: 26 additions & 12 deletions zummon/src/commonMain/kotlin/companysurvey/Electricity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,41 @@ data class Electricity (
return hasConnection ?: false
}

/**
* Contracted capacity for delivery of electricity from grid to company.
*/
fun getContractedConnectionCapacityKw(): Double? {
when(kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> return grootverbruik?.contractedConnectionDeliveryCapacity_kW?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> return kleinverbruik?.connectionCapacity?.toKw()
else -> return kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.contractedConnectionDeliveryCapacity_kW?.toDouble()
return when (kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> grootverbruik?.contractedConnectionDeliveryCapacity_kW?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> kleinverbruik?.connectionCapacity?.toKw()
else -> kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.contractedConnectionDeliveryCapacity_kW?.toDouble()
}
}

fun getPhysicalConnectionCapacityKw(): Double? {
when(kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> return grootverbruik?.physicalCapacityKw?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> return kleinverbruik?.connectionCapacity?.toKw()
else -> return kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.physicalCapacityKw?.toDouble()
return when (kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> grootverbruik?.physicalCapacityKw?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> kleinverbruik?.connectionCapacity?.toKw()
else -> kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.physicalCapacityKw?.toDouble()
}
}

fun getContractedDeliveryCapacityKw(): Double? {
return when (kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> grootverbruik?.contractedConnectionDeliveryCapacity_kW?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> kleinverbruik?.connectionCapacity?.toKw()
else -> kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.contractedConnectionDeliveryCapacity_kW?.toDouble()
}
}

/**
* Contracted capacity for feed-in of electricity from company to grid.
*/
fun getContractedFeedInCapacityKw(): Double? {
when (kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> return grootverbruik?.contractedConnectionFeedInCapacity_kW?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> return kleinverbruik?.connectionCapacity?.toKw()
else -> return kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.contractedConnectionFeedInCapacity_kW?.toDouble()
return when (kleinverbruikOrGrootverbruik) {
KleinverbruikOrGrootverbruik.GROOTVERBRUIK -> grootverbruik?.contractedConnectionFeedInCapacity_kW?.toDouble()
KleinverbruikOrGrootverbruik.KLEINVERBRUIK -> kleinverbruik?.connectionCapacity?.toKw()
else -> kleinverbruik?.connectionCapacity?.toKw() ?: grootverbruik?.contractedConnectionFeedInCapacity_kW?.toDouble()
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions zummon/src/commonMain/kotlin/companysurvey/TimeSeries.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.zenmo.zummon.companysurvey
import kotlinx.serialization.Serializable
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.zenmo.zummon.BenasherUuidSerializer
Expand Down Expand Up @@ -76,6 +77,7 @@ data class TimeSeries (
return maxNullSequence
}

fun getPeak(): DataPoint = DataPoint(values.max(), unit, timeStep)

/**
* Get a full calendar year of data if it is present.
Expand Down Expand Up @@ -166,3 +168,29 @@ enum class TimeSeriesType {
ELECTRICITY_PRODUCTION,
GAS_DELIVERY,
}

/**
* Represents a single point within the time series.
* Improvement: add timestamp
*/
data class DataPoint (
val value: Float,
val unit: TimeSeriesUnit,
val timeStep: kotlin.time.Duration,
) {
fun kWh(): Double {
if (this.unit != TimeSeriesUnit.KWH) {
throw UnsupportedOperationException("Can only get the kWh from a kWh data point")
}

return value.toDouble()
}

fun kW(): Double {
if (this.unit != TimeSeriesUnit.KWH) {
throw UnsupportedOperationException("Can only get the kW from a kWh data point")
}

return value * (1.hours / this.timeStep)
}
}
90 changes: 68 additions & 22 deletions zummon/src/commonMain/kotlin/companysurvey/Validation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class GridConnectionValidator : Validator<GridConnection> {
val batteryPower = (gridConnection.storage.batteryPowerKw ?: 0.0).toFloat()

return if (totalPowerChargePoints < (contractedCapacity + batteryPower)) {
listOf(ValidationResult(Status.VALID, translate("gridConnection.totalPowerChargePoints")))
listOf(ValidationResult(Status.VALID, translate("gridConnection.totalPowerChargePointsValid")))
} else {
listOf(ValidationResult(Status.INVALID, translate("gridConnection.totalPowerChargePointsInvalid", totalPowerChargePoints, contractedCapacity + batteryPower)))
}
Expand Down Expand Up @@ -289,7 +289,12 @@ class ElectricityValidator : Validator<Electricity> {
} ?: false

if (isCloseEnough) {
ValidationResult(Status.VALID, translate("electricity.annualFeedInValid", electricity.annualElectricityFeedIn_kWh, totalQuarterHourlyFeedIn))
ValidationResult(
Status.VALID, message(
nl = "Jaarwaarde teruglevering ${electricity.annualElectricityFeedIn_kWh} kWh komt overeen met het totaal van de kwartierwaarden ${totalQuarterHourlyFeedIn} kWh",
en = "Annual feed-in of ${electricity.annualElectricityFeedIn_kWh} kWh matches total of quarter-hourly feed-in ${totalQuarterHourlyFeedIn} kWh",
)
)
} else {
ValidationResult(Status.INVALID, translate("electricity.annualFeedInMismatch", electricity.annualElectricityFeedIn_kWh, totalQuarterHourlyFeedIn))
}
Expand Down Expand Up @@ -339,25 +344,69 @@ class ElectricityValidator : Validator<Electricity> {
}
}

//peak of delivery should be less than contracted capacity
/**
* peak of delivery should be less than contracted capacity
*/
fun quarterHourlyDeliveryLowContractedCapacity(electricity: Electricity): ValidationResult {
val contractedCapacity = (electricity.getContractedConnectionCapacityKw() ?: 0.0).toFloat()
val pickDelivery = electricity.quarterHourlyDelivery_kWh?.values?.maxOrNull() ?: Float.MIN_VALUE
return if ( pickDelivery <= contractedCapacity) {
ValidationResult(Status.VALID, translate("electricity.quarterHourlyDeliveryLowContractedCapacityKw", contractedCapacity))
val contractedCapacity_kW = electricity.getContractedConnectionCapacityKw()
if (contractedCapacity_kW == null) {
return ValidationResult(Status.MISSING_DATA, message(
en = "Gecontracteerd vermogen levering ontbreek",
nl = "Contracted delivery capacity missing",
))
}

if (electricity.quarterHourlyDelivery_kWh == null) {
return ValidationResult(Status.MISSING_DATA, message(
en = "Kwartierwaarden levering ontbreek",
nl = "Quarter-hourly delivery missing",
))
}

val peakDelivery = electricity.quarterHourlyDelivery_kWh.getPeak()

return if ( peakDelivery.kW() <= contractedCapacity_kW) {
ValidationResult(Status.VALID, message(
en = "Piek van kwartierwaarden levering ${peakDelivery.kWh()} kWh valt binnen gecontracteerd vermogen levering ${contractedCapacity_kW} kW",
nl = "Peak of quarter-hourly delivery ${peakDelivery.kWh()} kWh does not exceed contracted capacity ${contractedCapacity_kW} kW",
))
} else {
ValidationResult(Status.INVALID, translate("electricity.quarterHourlyDeliveryHighContractedCapacityKw", contractedCapacity))
ValidationResult(Status.INVALID, message(
nl = "Piek van kwartierwaarden levering ${peakDelivery.kWh()} kWh mag niet hoger zijn dan gecontracteerd vermogen levering $contractedCapacity_kW kW",
en = "Peak of quarter-hourly delivery ${peakDelivery.kWh()} kWh should be below contracted capacity $contractedCapacity_kW Kw",
))
}
}

//peak of feed-in should be less than contracted capacity
fun quarterFeedInLowContractedCapacity(electricity: Electricity): ValidationResult {
val contractedFeedInCapacity = (electricity.getContractedFeedInCapacityKw() ?: 0.0).toFloat()
val pickFeedIn = electricity.quarterHourlyFeedIn_kWh?.values?.maxOrNull() ?: Float.MIN_VALUE
return if (pickFeedIn < contractedFeedInCapacity) {
ValidationResult(Status.VALID, translate("electricity.quarterHourlyDeliveryLowContractedCapacityKw", contractedFeedInCapacity))
val contractedFeedInCapacity_kW = (electricity.getContractedFeedInCapacityKw() ?: 0.0).toFloat()
if (contractedFeedInCapacity_kW == null) {
return ValidationResult(Status.MISSING_DATA, message(
en = "Gecontracteerd vermogen teruglevering ontbreek",
nl = "Contracted feed-in capacity missing",
))
}

if (electricity.quarterHourlyFeedIn_kWh == null) {
// Or not applicable if no solar panels
return ValidationResult(Status.MISSING_DATA, message(
en = "Kwartierwaarden teruglevering ontbreek",
nl = "Quarter-hourly feed-in missing",
))
}

val peakFeedIn = electricity.quarterHourlyFeedIn_kWh.getPeak()
return if (peakFeedIn.kW() < contractedFeedInCapacity_kW) {
ValidationResult(Status.VALID, message(
en = "Piek van kwartierwaarden teruglevering ${peakFeedIn.kWh()} kWh valt binnen gecontracteerd vermogen levering $contractedFeedInCapacity_kW kW",
nl = "Peak of quarter-hourly feed-in ${peakFeedIn.kWh()} kWh does not exceed contracted capacity $contractedFeedInCapacity_kW kW",
))
} else {
ValidationResult(Status.INVALID, translate("electricity.quarterHourlyDeliveryHighContractedCapacityKw", contractedFeedInCapacity))
ValidationResult(Status.INVALID, message(
nl = "Piek van kwartierwaarden teruglevering ${peakFeedIn.kWh()} kWh mag niet hoger zijn dan gecontracteerd vermogen levering $contractedFeedInCapacity_kW kW",
en = "Peak of quarter-hourly feed-in ${peakFeedIn.kWh()} kWh delivery should be below contracted capacity $contractedFeedInCapacity_kW Kw",
))
}
}

Expand Down Expand Up @@ -528,7 +577,12 @@ class TransportValidator {
fun validateTotalElectricVans(transport: Transport): ValidationResult {
return when {
((transport.vans.numElectricVans ?: 0) > (transport.vans.numVans ?: 0)) -> ValidationResult(Status.INVALID, translate("transport.electricVansInvalid", transport.vans.numElectricVans, transport.vans.numVans))
else -> ValidationResult(Status.VALID, translate("transport.electricVansValid"))
else -> ValidationResult(
Status.VALID, message(
en = "Number of electric vans does not exceed the total number of Vans",
nl = "Aantal elektrische bestelwagens valt binnen het totale aantal bestelwagens"
)
)
}
}
}
Expand Down Expand Up @@ -595,10 +649,6 @@ val translations: Map<Language, Map<String, Map<String, String>>> = mapOf(
"notEnoughValues" to "Not enough values for year: needed %d got %d",
"annualFeedInMismatch" to "Annual feed in (%d) mismatch the total quarter hourly feed in (%d)",
"annualFeedInMismatch" to "Annual feed in (%d) matches the total quarter hourly feed in (%d)",

"quarterHourlyDeliveryLowContractedCapacityKw" to "Quarter-hourly stays lower than the Contracted CapacityKw (%d)",
"quarterHourlyDeliveryHighContractedCapacityKw" to "Quarter-hourly shouldn't go higher than the Contracted CapacityKw (%d)"

),
"grootverbruik" to mapOf(
"notProvided" to "Large consumption data is not provided",
Expand Down Expand Up @@ -661,11 +711,7 @@ val translations: Map<Language, Map<String, Map<String, String>>> = mapOf(
"electricTrucksValid" to "Number of Electric Trucks is lower than the total of Trucks",
"electricTrucksInvalid" to "Number of electric trucks %d exceeds the total number of trucks %d",

"electricVansValid" to "Number of Electric Vans is lower than the total of Vans",
"electricVansInvalid" to "Number of electric vans %d exceeds the total number of vans %d",

"quarterHourlyDeliveryLowContractedCapacityKw" to "Kwartuur levering blijft lager dan de Contractuele CapaciteitKw (%d)",
"quarterHourlyDeliveryHighContractedCapacityKw" to "Kwartuur levering mag niet hoger zijn dan de Contractuele CapaciteitKw (%d)",
),
),
Language.nl to mapOf(
Expand Down
13 changes: 13 additions & 0 deletions zummon/src/commonTest/kotlin/companysurvey/TimeSeriesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,17 @@ class TimeSeriesTest {
assertEquals(300f, yearValues.first())
assertEquals(200f, yearValues.last())
}

@Test
fun testGetPeakKw() {
val timeSeries = TimeSeries(
type = TimeSeriesType.ELECTRICITY_DELIVERY,
start = Instant.parse("2024-01-01T00:00:00+01:00"),
values = floatArrayOf(1f, 2f, 1f)
)

val peak = timeSeries.getPeak()
assertEquals(2.0, peak.kWh())
assertEquals(8.0, peak.kW())
}
}
Loading