diff --git a/CHANGELOG.md b/CHANGELOG.md index 178aa25fc0..d488ed71e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed +- Harmonized active power output of `WecInput` and `PvInput` [#135](https://github.com/ie3-institute/simona/issues/135) - Improving code readability in EvcsAgent by moving FreeLotsRequest to separate methods ### Fixed diff --git a/src/main/scala/edu/ie3/simona/model/participant/PVModel.scala b/src/main/scala/edu/ie3/simona/model/participant/PVModel.scala index 235f8a3981..99feb17cf2 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/PVModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/PVModel.scala @@ -740,8 +740,7 @@ final case class PVModel private ( .multiply(genCorr * tempCorr) /* Calculate the foreseen active power output without boundary condition adaptions */ - val proposal = sRated - .multiply(-1) + val activePower = sRated .multiply( `yield` .divide(irraditionSTC) @@ -752,19 +751,12 @@ final case class PVModel private ( .asType(classOf[Power]) .to(MEGAWATT) // MW. - /* Do sanity check, if the proposed feed in is above the estimated maximum to be apparent active power of the plant */ - if (proposal.isLessThan(pMax)) - logger.warn( - "The fed in active power is higher than the estimated maximum active power of this plant ({} < {}). " + - "Did you provide wrong weather input data?", - proposal, - pMax - ) + /* If the output is marginally small, suppress the output, as it's likely nighttime where there shouldn't be any output */ + if (activePower.compareTo(activationThreshold) > 0) + return Quantities.getQuantity(0d, MEGAWATT) - /* If the output is marginally small, suppress the output, as we are likely to be in night and then only produce incorrect output */ - if (proposal.compareTo(activationThreshold) > 0) - Quantities.getQuantity(0d, MEGAWATT) - else proposal + /* Do sanity check, if the proposed feed in is above the estimated maximum active power of the plant */ + limitActivePower(activePower, pMax).multiply(-1) } } diff --git a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala index 3331b2280d..03a5817278 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/SystemParticipant.scala @@ -188,5 +188,30 @@ abstract class SystemParticipant[CD <: CalcRelevantData]( } } + /** Limits active power with respect to the maximum permissible power defined + * for the system participant. + * + * @param p + * the calculated active power + * @param pMax + * the maximum permissible power + * @return + */ + protected def limitActivePower( + p: ComparableQuantity[Power], + pMax: ComparableQuantity[Power] + ): ComparableQuantity[Power] = { + if (p.isGreaterThan(pMax)) { + logger.warn( + "The calculated active power of plant {} is higher than its estimated maximum active power ({} > {}). Will be limited to the maximum permissible output.", + uuid, + p, + pMax + ) + pMax + } else + p + } + def getUuid: UUID = this.uuid } diff --git a/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala b/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala index 66868b7dcb..7f912582a4 100644 --- a/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala +++ b/src/main/scala/edu/ie3/simona/model/participant/WecModel.scala @@ -112,17 +112,7 @@ final case class WecModel( val activePower = determinePower(wecData).to(MEGAWATT) val pMax = sMax.multiply(cosPhiRated).to(MEGAWATT) - (if (activePower.isGreaterThan(pMax)) { - logger.warn( - "The fed in active power is higher than the estimated maximum active power of this plant ({} > {}). " + - "Did you provide wrong weather input data?", - activePower, - pMax - ) - pMax - } else { - activePower - }).multiply(-1) + limitActivePower(activePower, pMax).multiply(-1) } /** Determine the turbine output power with the air density ρ, the wind diff --git a/src/test/groovy/edu/ie3/simona/model/participant/SystemParticipantTest.groovy b/src/test/groovy/edu/ie3/simona/model/participant/SystemParticipantTest.groovy index 1c643cb4cc..bb5f181bad 100644 --- a/src/test/groovy/edu/ie3/simona/model/participant/SystemParticipantTest.groovy +++ b/src/test/groovy/edu/ie3/simona/model/participant/SystemParticipantTest.groovy @@ -287,4 +287,37 @@ class SystemParticipantTest extends Specification { 1.07 || 44.440972086578 1.1 || 44.440972086578 } + + def "Limits active power to the maximum permissible power specified"() { + given: "a calculated power value and the maximum permissible power" + + Quantity pQuant = Quantities.getQuantity(p, KILOWATT) + Quantity pMaxQuant = Quantities.getQuantity(pMax, KILOWATT) + + def loadMock = new SystemParticipant( + UUID.fromString("d8461624-d142-4360-8e02-c21965ec555e"), + "System participant calculateQ Test", + OperationInterval.apply(0L, 86400L), + 1d, + QControl.apply(new QV("qV:{(0.93,-1),(0.97,0),(1,0),(1.03,0),(1.07,1)}")), + Quantities.getQuantity(200, KILOWATT), + 1d) { + @Override + ComparableQuantity calculateActivePower(CalcRelevantData data) { + return Quantities.getQuantity(0, MEGAWATT) + } + } + + when: + Quantity limited = loadMock.limitActivePower(pQuant, pMaxQuant) + + then: + if (p <= pMax) limited.isEquivalentTo(pQuant) else limited.isEquivalentTo(pMaxQuant) + + where: + p || pMax + 10 || 100 + 100 || 10 + 10 || 10 + } }