summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt18
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimPsu.kt58
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimPsuTest.kt102
3 files changed, 155 insertions, 23 deletions
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt
index 45d15692..7f416010 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimBareMetalMachine.kt
@@ -36,12 +36,14 @@ import org.opendc.simulator.resources.SimResourceInterpreter
* @param interpreter The [SimResourceInterpreter] to drive the simulation.
* @param model The machine model to simulate.
* @param powerDriver The power driver to use.
+ * @param psu The power supply of the machine.
* @param parent The parent simulation system.
*/
public class SimBareMetalMachine(
interpreter: SimResourceInterpreter,
model: SimMachineModel,
powerDriver: PowerDriver,
+ public val psu: SimPsu = SimPsu(500.0, mapOf(1.0 to 1.0)),
parent: SimResourceSystem? = null,
) : SimAbstractMachine(interpreter, parent, model) {
/**
@@ -51,23 +53,15 @@ public class SimBareMetalMachine(
Cpu(SimResourceSource(cpu.frequency, interpreter, this@SimBareMetalMachine), cpu)
}
- /**
- * The power supply of this bare-metal machine.
- */
- public val psu: SimPsu = object : SimPsu() {
- /**
- * The logic for the CPU power driver.
- */
- private val cpuLogic = powerDriver.createLogic(this@SimBareMetalMachine, cpus)
-
- override fun computePower(): Double = cpuLogic.computePower()
- }
-
override fun updateUsage(usage: Double) {
super.updateUsage(usage)
psu.update()
}
+ init {
+ psu.connect(powerDriver.createLogic(this, cpus))
+ }
+
/**
* A [SimProcessingUnit] of a bare-metal machine.
*/
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimPsu.kt b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimPsu.kt
index 8837eff3..4ddad1c9 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimPsu.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/main/kotlin/org/opendc/simulator/compute/SimPsu.kt
@@ -22,16 +22,24 @@
package org.opendc.simulator.compute
+import org.opendc.simulator.compute.power.PowerDriver
import org.opendc.simulator.power.SimPowerInlet
import org.opendc.simulator.resources.SimResourceCommand
import org.opendc.simulator.resources.SimResourceConsumer
import org.opendc.simulator.resources.SimResourceContext
import org.opendc.simulator.resources.SimResourceEvent
+import java.util.*
/**
* A power supply of a [SimBareMetalMachine].
+ *
+ * @param ratedOutputPower The rated output power of the PSU.
+ * @param energyEfficiency The energy efficiency of the PSU for various power draws.
*/
-public abstract class SimPsu : SimPowerInlet() {
+public class SimPsu(
+ private val ratedOutputPower: Double,
+ energyEfficiency: Map<Double, Double>,
+) : SimPowerInlet() {
/**
* The power draw of the machine at this instant.
*/
@@ -40,13 +48,44 @@ public abstract class SimPsu : SimPowerInlet() {
private var _powerDraw = 0.0
/**
+ * The energy efficiency of the PSU at various power draws.
+ */
+ private val energyEfficiency = TreeMap(energyEfficiency)
+
+ /**
* The consumer context.
*/
private var _ctx: SimResourceContext? = null
+ /**
+ * The driver that is connected to the PSU.
+ */
+ private var _driver: PowerDriver.Logic? = null
+
+ init {
+ require(energyEfficiency.isNotEmpty()) { "Must specify at least one entry for energy efficiency of PSU" }
+ }
+
+ /**
+ * Update the power draw of the PSU.
+ */
+ public fun update() {
+ _ctx?.interrupt()
+ }
+
+ /**
+ * Connect the specified [PowerDriver.Logic] to this PSU.
+ */
+ public fun connect(driver: PowerDriver.Logic) {
+ check(_driver == null) { "PSU already connected" }
+ _driver = driver
+ update()
+ }
+
override fun createConsumer(): SimResourceConsumer = object : SimResourceConsumer {
override fun onNext(ctx: SimResourceContext): SimResourceCommand {
- val powerDraw = _powerDraw
+ val powerDraw = computePowerDraw(_driver?.computePower() ?: 0.0)
+
return if (powerDraw > 0.0)
SimResourceCommand.Consume(Double.POSITIVE_INFINITY, powerDraw, Long.MAX_VALUE)
else
@@ -56,6 +95,7 @@ public abstract class SimPsu : SimPowerInlet() {
override fun onEvent(ctx: SimResourceContext, event: SimResourceEvent) {
when (event) {
SimResourceEvent.Start -> _ctx = ctx
+ SimResourceEvent.Run -> _powerDraw = ctx.speed
SimResourceEvent.Exit -> _ctx = null
else -> {}
}
@@ -63,17 +103,13 @@ public abstract class SimPsu : SimPowerInlet() {
}
/**
- * Update the power draw of the PSU.
+ * Compute the power draw of the PSU including the power loss.
*/
- public fun update() {
- _powerDraw = computePower()
- _ctx?.interrupt()
+ private fun computePowerDraw(load: Double): Double {
+ val loadPercentage = (load / ratedOutputPower).coerceIn(0.0, 1.0)
+ val efficiency = energyEfficiency.ceilingEntry(loadPercentage)?.value ?: 1.0
+ return load / efficiency
}
- /**
- * Compute the power draw of the PSU.
- */
- protected abstract fun computePower(): Double
-
override fun toString(): String = "SimPsu[draw=$_powerDraw]"
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimPsuTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimPsuTest.kt
new file mode 100644
index 00000000..e0ebdb73
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimPsuTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2021 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.simulator.compute
+
+import io.mockk.every
+import io.mockk.mockk
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import org.opendc.simulator.compute.power.PowerDriver
+import org.opendc.simulator.core.runBlockingSimulation
+import org.opendc.simulator.power.SimPowerSource
+import org.opendc.simulator.resources.SimResourceInterpreter
+
+/**
+ * Test suite for [SimPsu]
+ */
+internal class SimPsuTest {
+
+ @Test
+ fun testInvalidInput() {
+ assertThrows<IllegalArgumentException> { SimPsu(1.0, emptyMap()) }
+ }
+
+ @Test
+ fun testDoubleConnect() {
+ val psu = SimPsu(1.0, mapOf(0.0 to 1.0))
+ val cpuLogic = mockk<PowerDriver.Logic>()
+ psu.connect(cpuLogic)
+ assertThrows<IllegalStateException> { psu.connect(mockk()) }
+ }
+
+ @Test
+ fun testPsuIdle() = runBlockingSimulation {
+ val ratedOutputPower = 240.0
+ val energyEfficiency = mapOf(0.0 to 1.0)
+
+ val interpreter = SimResourceInterpreter(coroutineContext, clock)
+ val source = SimPowerSource(interpreter, capacity = ratedOutputPower)
+
+ val cpuLogic = mockk<PowerDriver.Logic>()
+ every { cpuLogic.computePower() } returns 0.0
+
+ val psu = SimPsu(ratedOutputPower, energyEfficiency)
+ psu.connect(cpuLogic)
+ source.connect(psu)
+
+ assertEquals(0.0, source.powerDraw, 0.01)
+ }
+
+ @Test
+ fun testPsuPowerLoss() = runBlockingSimulation {
+ val ratedOutputPower = 240.0
+ // Efficiency of 80 Plus Titanium PSU
+ val energyEfficiency = sortedMapOf(
+ 0.3 to 0.9,
+ 0.7 to 0.92,
+ 1.0 to 0.94,
+ )
+
+ val interpreter = SimResourceInterpreter(coroutineContext, clock)
+ val source = SimPowerSource(interpreter, capacity = ratedOutputPower)
+
+ val cpuLogic = mockk<PowerDriver.Logic>()
+ every { cpuLogic.computePower() } returnsMany listOf(50.0, 100.0, 150.0, 200.0)
+
+ val psu = SimPsu(ratedOutputPower, energyEfficiency)
+ psu.connect(cpuLogic)
+ source.connect(psu)
+
+ assertEquals(55.55, source.powerDraw, 0.01)
+
+ psu.update()
+ assertEquals(108.695, source.powerDraw, 0.01)
+
+ psu.update()
+ assertEquals(163.043, source.powerDraw, 0.01)
+
+ psu.update()
+ assertEquals(212.765, source.powerDraw, 0.01)
+ }
+}