Skip to content

Commit

Permalink
Add voltage and current units
Browse files Browse the repository at this point in the history
  • Loading branch information
gmulders committed Oct 22, 2024
1 parent 2734c78 commit ec84dd9
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 39 deletions.
12 changes: 10 additions & 2 deletions measure/src/main/kotlin/com/alliander/open/measure/Measure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,16 @@ data class Measure<U : Units>(val amount: BigDecimal, val units: U) : Comparable
val absoluteFactor = (factor `in` base).abs()
return dividend.roundToMultiple(absoluteFactor, roundingMode) * base
}

}

private fun BigDecimal.roundToMultiple(factor: BigDecimal, roundingMode: RoundingMode): BigDecimal =
this.divide(factor, 0, roundingMode) * factor
this.divide(factor, 0, roundingMode) * factor

operator fun <U : Units> BigDecimal.times(m: Measure<U>): Measure<U> =
Measure(amount = m.amount.times(this), units = m.units)

operator fun <U : Units> Int.times(m: Measure<U>): Measure<U> =
BigDecimal.valueOf(this.toLong()) * m

operator fun <U : Units> Long.times(m: Measure<U>): Measure<U> =
BigDecimal.valueOf(this) * m
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,60 @@

package com.alliander.open.measure

import com.alliander.open.measure.Current.Companion.ampere
import com.alliander.open.measure.Energy.Companion.joule
import com.alliander.open.measure.Power.Companion.watt
import com.alliander.open.measure.Time.Companion.seconds
import com.alliander.open.measure.Voltage.Companion.volt
import java.math.BigDecimal

@JvmName("powerTimesTime")
operator fun Measure<Power>.times(duration: Measure<Time>): Measure<Energy> {
val p = this `as` watt
val dt = duration `as` seconds
val p = this `in` watt
val dt = duration `in` seconds
return Measure(p * dt, joule)
}

@JvmName("currentTimesVoltage")
operator fun Measure<Current>.times(voltage: Measure<Voltage>): Measure<Power> {
val i = this `in` ampere
val v = voltage `in` volt
return Measure(i * v, watt)
}

@JvmName("voltageTimesCurrent")
operator fun Measure<Voltage>.times(current: Measure<Current>): Measure<Power> {
val v = this `in` volt
val i = current `in` ampere
return Measure(i * v, watt)
}

@JvmName("powerDivVoltage")
operator fun Measure<Power>.div(voltage: Measure<Voltage>): Measure<Current> {
val p = this `in` watt
val v = voltage `in` volt
return Measure(p / v, ampere)
}

@JvmName("powerDivCurrent")
operator fun Measure<Power>.div(current: Measure<Current>): Measure<Voltage> {
val p = this `in` watt
val i = current `in` ampere
return Measure(p / i, volt)
}

class Voltage(suffix: String, ratio: BigDecimal = BigDecimal.ONE) : Units(suffix, ratio) {
companion object {
val volt = Voltage("V")
val kiloVolt = Voltage("kV", 1_000.toBigDecimal())
}
}

return Measure(p.amount * dt.amount, joule)
class Current(suffix: String, ratio: BigDecimal = BigDecimal.ONE) : Units(suffix, ratio) {
companion object {
val ampere = Current("A")
val kiloAmpere = Current("kA", 1_000.toBigDecimal())
}
}

class Power(suffix: String, ratio: BigDecimal = BigDecimal.ONE) : Units(suffix, ratio) {
Expand All @@ -30,7 +74,7 @@ class Energy(suffix: String, ratio: BigDecimal = BigDecimal.ONE) : Units(suffix,
val kiloJoule = Energy("kJ", 1_000.toBigDecimal())
val megaJoule = Energy("MJ", 1_000_000.toBigDecimal())
val kiloWattHour = Energy("kWh", 3_600_000.toBigDecimal())
val megaWattHour = Energy("mWh", 3_600_000_000.toBigDecimal())
val megaWattHour = Energy("MWh", 3_600_000_000.toBigDecimal())
}
}

Expand All @@ -41,4 +85,3 @@ class Time(suffix: String, ratio: BigDecimal = BigDecimal.ONE) : Units(suffix, r
val hours = Time("h", 3_600.toBigDecimal())
}
}

32 changes: 0 additions & 32 deletions measure/src/test/kotlin/com/alliander/open/measure/MeasureTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,21 @@ import com.alliander.open.measure.Energy.Companion.joule
import com.alliander.open.measure.Energy.Companion.kiloJoule
import com.alliander.open.measure.Energy.Companion.kiloWattHour
import com.alliander.open.measure.Energy.Companion.megaJoule
import com.alliander.open.measure.Energy.Companion.megaWattHour
import com.alliander.open.measure.Power.Companion.kiloWatt
import com.alliander.open.measure.Power.Companion.megaWatt
import com.alliander.open.measure.Power.Companion.watt
import com.alliander.open.measure.Time.Companion.hours
import com.alliander.open.measure.Time.Companion.minutes
import com.alliander.open.measure.Time.Companion.seconds
import com.alliander.open.measure.extension.sum
import io.kotest.core.spec.style.StringSpec
import io.kotest.data.headers
import io.kotest.data.row
import io.kotest.data.table
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.property.checkAll
import java.math.BigDecimal
import java.math.RoundingMode.UP

class MeasureTest : StringSpec({
"Joule, Watt and seconds are different units" {
joule shouldNotBe watt
joule shouldNotBe seconds

watt shouldNotBe joule
watt shouldNotBe seconds

seconds shouldNotBe joule
seconds shouldNotBe watt
}

"joules and kiloJoules can be added" {
val left = 1000 * joule
val right = 1 * kiloJoule
Expand All @@ -47,15 +32,6 @@ class MeasureTest : StringSpec({
sum shouldBe 2000 * joule
}

"power times time is energy" {
val power = 10 * kiloWatt
val duration = 15 * minutes

val energy = power * duration

energy `as` megaJoule shouldBe 9 * megaJoule
}

"joules can be added" {
checkAll { leftQuantity: Int, rightQuantity: Int ->
val left = leftQuantity * joule
Expand Down Expand Up @@ -123,14 +99,6 @@ class MeasureTest : StringSpec({
}
}

"energySum returns correct amount of energy when given a list of measures of energy" {
val list = listOf(100 * joule, 1 * megaWattHour, 1 * megaJoule)
val result = list.sum()

val expectedResult = 3601000100 * joule
result shouldBe expectedResult
}

"roundUpToNextMultiple processes different units correctly" {
io.kotest.data.forAll(
table(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2021-2022 Alliander N.V.
//
// SPDX-License-Identifier: MPL-2.0

package com.alliander.open.measure

import com.alliander.open.measure.Current.Companion.ampere
import com.alliander.open.measure.Current.Companion.kiloAmpere
import com.alliander.open.measure.Energy.Companion.joule
import com.alliander.open.measure.Energy.Companion.kiloJoule
import com.alliander.open.measure.Energy.Companion.kiloWattHour
import com.alliander.open.measure.Energy.Companion.megaJoule
import com.alliander.open.measure.Energy.Companion.megaWattHour
import com.alliander.open.measure.Power.Companion.kiloWatt
import com.alliander.open.measure.Power.Companion.megaWatt
import com.alliander.open.measure.Power.Companion.watt
import com.alliander.open.measure.Time.Companion.hours
import com.alliander.open.measure.Time.Companion.minutes
import com.alliander.open.measure.Time.Companion.seconds
import com.alliander.open.measure.Voltage.Companion.kiloVolt
import com.alliander.open.measure.Voltage.Companion.volt
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.should
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.beInstanceOf

class SpecificUnitsTest : StringSpec({
"The units have the correct measure" {
joule should beInstanceOf<Energy>()
kiloJoule should beInstanceOf<Energy>()
megaJoule should beInstanceOf<Energy>()
kiloWattHour should beInstanceOf<Energy>()
megaWattHour should beInstanceOf<Energy>()

watt should beInstanceOf<Power>()
kiloWatt should beInstanceOf<Power>()
megaWatt should beInstanceOf<Power>()

seconds should beInstanceOf<Time>()
minutes should beInstanceOf<Time>()
hours should beInstanceOf<Time>()

volt should beInstanceOf<Voltage>()
kiloVolt should beInstanceOf<Voltage>()

ampere should beInstanceOf<Current>()
kiloAmpere should beInstanceOf<Current>()
}

"power times time is energy" {
val power = 10 * kiloWatt
val duration = 15 * minutes

val energy = power * duration

energy `as` megaJoule shouldBe 9 * megaJoule
}

"current times voltage is power" {
val current = 10 * ampere
val voltage = 15 * volt

val power = current * voltage

power `as` watt shouldBe 150 * watt
}

"voltage times current is power" {
val current = 10 * ampere
val voltage = 15 * volt

val power = voltage * current

power `as` watt shouldBe 150 * watt
}

"power divided by voltage is current" {
val power = 150 * watt
val voltage = 15 * volt

val current = power / voltage

current `as` ampere shouldBe 10 * ampere
}

"power divided by current is voltage" {
val power = 150 * watt
val current = 10 * ampere

val voltage = power / current

voltage `as` volt shouldBe 15 * volt
}
})

0 comments on commit ec84dd9

Please sign in to comment.