16 package emlab.gen.role;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
26 import org.apache.commons.math.optimization.GoalType;
27 import org.apache.commons.math.optimization.OptimizationException;
28 import org.apache.commons.math.optimization.RealPointValuePair;
29 import org.apache.commons.math.optimization.linear.LinearConstraint;
30 import org.apache.commons.math.optimization.linear.LinearObjectiveFunction;
31 import org.apache.commons.math.optimization.linear.Relationship;
32 import org.apache.commons.math.optimization.linear.SimplexSolver;
33 import org.apache.commons.math.stat.regression.SimpleRegression;
34 import org.springframework.beans.factory.annotation.Autowired;
36 import agentspring.role.AbstractRole;
37 import agentspring.trend.GeometricTrend;
51 public abstract class AbstractEnergyProducerRole<T
extends EnergyProducer> extends AbstractRole<T> {
56 public double calculateMarginalCO2Cost(
PowerPlant powerPlant,
long tick,
boolean forecast) {
59 mc += calculateCO2TaxMarginalCost(powerPlant, tick);
60 mc += calculateCO2MarketMarginalCost(powerPlant, tick, forecast);
61 logger.info(
"Margincal cost for plant {} is {}", powerPlant.getName(), mc);
65 public double calculateMarginalCostExclCO2MarketCost(
PowerPlant powerPlant,
long clearingTick) {
68 mc += calculateMarginalFuelCost(powerPlant, clearingTick);
69 mc += calculateCO2TaxMarginalCost(powerPlant, clearingTick);
70 logger.info(
"Margincal cost excluding CO2 auction/market cost for plant {} is {}", powerPlant.getName(), mc);
74 public double calculateExpectedMarginalCostExclCO2MarketCost(
PowerPlant powerPlant,
75 Map<Substance, Double> forecastedFuelPrices,
long tick) {
77 mc += calculateExpectedMarginalFuelCost(powerPlant, forecastedFuelPrices);
78 mc += calculateCO2TaxMarginalCost(powerPlant, tick);
79 logger.info(
"Margincal cost excluding CO2 auction/market cost for plant {} is {}", powerPlant.getName(), mc);
83 public double calculateMarginalFuelCost(
PowerPlant powerPlant,
long clearingTick) {
88 double amount = mix.getShare();
89 logger.info(
"Calculating need for fuel: {} units of {}", mix.getShare(), mix.getSubstance().getName());
90 double fuelPrice = findLastKnownPriceForSubstance(mix.getSubstance(), clearingTick);
91 fc += amount * fuelPrice;
92 logger.info(
"Calculating marginal cost and found a fuel price which is {} per unit of fuel", fuelPrice);
98 public double calculateExpectedMarginalFuelCost(
PowerPlant powerPlant, Map<Substance, Double> forecastedFuelPrices) {
103 double amount = mix.getShare();
104 logger.info(
"Calculating need for fuel: {} units of {}", mix.getShare(), mix.getSubstance().getName());
105 double fuelPrice = forecastedFuelPrices.get(mix.getSubstance());
106 fc += amount * fuelPrice;
107 logger.info(
"Calculating marginal cost and found a fuel price which is {} per unit of fuel", fuelPrice);
122 Double average = calculateAverageMarketPriceBasedOnClearingPoints(reps.clearingPointRepositoryOld
123 .findClearingPointsForMarketAndTime(market, clearingTick,
false));
124 Substance substance = market.getSubstance();
126 if (average != null) {
127 logger.info(
"Average price found on market for this tick for {}", substance.getName());
131 average = calculateAverageMarketPriceBasedOnClearingPoints(reps.clearingPointRepositoryOld
132 .findClearingPointsForMarketAndTime(
133 market, clearingTick - 1,
false));
134 if (average != null) {
135 logger.info(
"Average price found on market for previous tick for {}", substance.getName());
139 if (market.getReferencePrice() > 0) {
140 logger.info(
"Found a reference price found for market for {}", substance.getName());
141 return market.getReferencePrice();
145 if (supplier.getSubstance().equals(substance)) {
147 return supplier.getPriceOfCommodity().getValue(clearingTick);
151 logger.info(
"No price has been found for {}", substance.getName());
166 if (market == null) {
167 logger.warn(
"No market found for {} so no price can be found", substance.getName());
170 return findLastKnownPriceOnMarket(market, clearingTick);
181 private Double calculateAverageMarketPriceBasedOnClearingPoints(Iterable<ClearingPoint> clearingPoints) {
182 double priceTimesVolume = 0d;
186 priceTimesVolume += point.getPrice() * point.getVolume();
187 volume += point.getVolume();
190 return priceTimesVolume / volume;
195 public double calculateCO2MarketMarginalCost(PowerPlant powerPlant,
long clearingTick,
boolean forecast) {
196 double co2Intensity = powerPlant.calculateEmissionIntensity();
197 CO2Auction auction = reps.genericRepository.findFirst(CO2Auction.class);
200 co2Price = reps.clearingPointRepository.findClearingPointForMarketAndTime(auction, clearingTick, forecast)
202 }
catch (Exception e) {
203 logger.warn(
"Couldn't find clearing point for tick {} and market {}", clearingTick, auction);
204 co2Price = findLastKnownCO2Price(clearingTick);
208 return co2Intensity * co2Price;
211 public double calculateCO2MarketCost(PowerPlant powerPlant,
boolean forecast,
long clearingTick) {
212 double co2Intensity = powerPlant.calculateEmissionIntensity();
213 CO2Auction auction = reps.genericRepository.findFirst(CO2Auction.class);
214 double co2Price = findLastKnownPriceOnMarket(auction, clearingTick);
215 double electricityOutput = powerPlant.calculateElectricityOutputAtTime(clearingTick, forecast);
216 return co2Intensity * co2Price * electricityOutput;
227 double co2Intensity = powerPlant.calculateEmissionIntensity();
228 CO2Auction auction = reps.genericRepository.findFirst(CO2Auction.class);
229 double co2Price = findLastKnownPriceOnMarket(auction, clearingTick);
230 double electricityOutput = powerPlant.calculateElectricityOutputAtTime(getCurrentTick(), forecast);
231 double nationalMinCo2price = reps.nationalGovernmentRepository.findNationalGovernmentByPowerPlant(powerPlant)
232 .getMinNationalCo2PriceTrend().getValue(getCurrentTick());
233 double paymentEffectivePartOfNationalCO2;
234 if (nationalMinCo2price > co2Price)
235 paymentEffectivePartOfNationalCO2 = nationalMinCo2price - co2Price;
237 paymentEffectivePartOfNationalCO2 = 0;
238 return co2Intensity * paymentEffectivePartOfNationalCO2 * electricityOutput;
241 public double calculateCO2TaxMarginalCost(
PowerPlant powerPlant,
long tick) {
242 double co2Intensity = powerPlant.calculateEmissionIntensity();
243 Government government = reps.genericRepository.findFirst(Government.class);
244 double co2Tax = government.getCO2Tax(tick);
245 return co2Intensity * co2Tax;
248 public double findLastKnownCO2Price(
long clearingTick) {
249 Government government = reps.genericRepository.findFirst(Government.class);
250 CO2Auction auction = reps.genericRepository.findFirst(CO2Auction.class);
251 double co2Price = findLastKnownPriceOnMarket(auction, clearingTick);
252 double co2Tax = government.getCO2Tax(clearingTick);
253 return co2Price + co2Tax;
256 public double calculateCO2Tax(PowerPlant powerPlant,
boolean forecast,
long clearingTick) {
257 double co2Intensity = powerPlant.calculateEmissionIntensity();
258 double electricityOutput = powerPlant.calculateElectricityOutputAtTime(clearingTick, forecast);
259 Government government = reps.genericRepository.findFirst(Government.class);
260 double co2Tax = government.getCO2Tax(clearingTick);
261 double taxToPay = (co2Intensity * electricityOutput) * co2Tax;
266 public double calculateFixedOperatingCost(PowerPlant powerPlant,
long clearingTick) {
268 double norm = powerPlant.getActualFixedOperatingCost();
269 long timeConstructed = powerPlant.getConstructionStartTime() + powerPlant.calculateActualLeadtime();
270 double mod = powerPlant.getTechnology().getFixedOperatingCostModifierAfterLifetime();
271 long lifetime = powerPlant.calculateActualLifetime();
273 GeometricTrend trend =
new GeometricTrend();
274 trend.setGrowthRate(mod);
275 trend.setStart(norm);
277 double currentCost = trend.getValue(clearingTick - (timeConstructed + lifetime));
281 public double calculateAveragePastOperatingProfit(PowerPlant pp,
long horizon) {
283 double averageFractionInMerit = 0d;
284 for (
long i = -horizon; i <= 0; i++) {
285 averageFractionInMerit += calculatePastOperatingProfitInclFixedOMCost(pp, getCurrentTick() + i) / i;
287 return averageFractionInMerit;
290 public double calculatePastOperatingProfitInclFixedOMCost(PowerPlant plant,
long clearingTick) {
296 pastOP += calculateFixedOperatingCost(plant, clearingTick);
318 double efficiency = plant.getActualEfficiency();
320 Set<SubstanceShareInFuelMix> fuelMix = (plant.getFuelMix() == null) ?
new HashSet<SubstanceShareInFuelMix>() : plant.getFuelMix();
322 int numberOfFuels = substancePriceMap.size();
323 if (numberOfFuels == 0) {
324 logger.info(
"No fuels, so no operation mode is set. Empty fuel mix is returned");
325 return new HashSet<SubstanceShareInFuelMix>();
326 }
else if (numberOfFuels == 1) {
328 if (!fuelMix.isEmpty()) {
329 ssifm = fuelMix.iterator().next();
335 Substance substance = substancePriceMap.keySet().iterator().next();
337 ssifm.setShare(calculateFuelConsumptionWhenOnlyOneFuelIsUsed(substance, efficiency));
338 ssifm.setSubstance(substance);
339 logger.info(
"Setting fuel consumption for {} to {}", ssifm.getSubstance().getName(), ssifm.getShare());
344 double minimumFuelMixQuality = plant.getTechnology().getMinimumFuelQuality();
346 double[] fuelAndCO2Costs =
new double[numberOfFuels];
347 double[] fuelDensities =
new double[numberOfFuels];
348 double[] fuelQuality =
new double[numberOfFuels];
351 for (
Substance substance : substancePriceMap.keySet()) {
352 fuelAndCO2Costs[i] = substancePriceMap.get(substance) + substance.getCo2Density() * (co2Price);
353 fuelDensities[i] = substance.getEnergyDensity();
354 fuelQuality[i] = (substance.getQuality() - minimumFuelMixQuality) * fuelDensities[i];
358 logger.info(
"Fuel prices: {}", fuelAndCO2Costs);
359 logger.info(
"Fuel densities: {}", fuelDensities);
360 logger.info(
"Fuel purities: {}", fuelQuality);
365 LinearObjectiveFunction
function =
new LinearObjectiveFunction(fuelAndCO2Costs, 0d);
367 List<LinearConstraint> constraints =
new ArrayList<LinearConstraint>();
371 constraints.add(
new LinearConstraint(fuelDensities, Relationship.EQ, (1 / efficiency)));
377 constraints.add(
new LinearConstraint(fuelQuality, Relationship.GEQ, 0));
380 SimplexSolver solver =
new SimplexSolver();
381 RealPointValuePair solution = solver.optimize(
function, constraints, GoalType.MINIMIZE,
true);
383 logger.info(
"Succesfully solved a linear optimization for fuel mix");
386 Iterator<SubstanceShareInFuelMix> iterator = plant.getFuelMix().iterator();
387 for (
Substance substance : substancePriceMap.keySet()) {
388 double share = solution.getPoint()[f];
391 if (iterator.hasNext()) {
392 ssifm = iterator.next();
398 double fuelConsumptionPerMWhElectricityProduced = convertFuelShareToMassVolume(share);
399 logger.info(
"Setting fuel consumption for {} to {}", substance.getName(), fuelConsumptionPerMWhElectricityProduced);
400 ssifm.setShare(fuelConsumptionPerMWhElectricityProduced);
401 ssifm.setSubstance(substance);
405 logger.info(
"If single fired, it would have been: {}",
406 calculateFuelConsumptionWhenOnlyOneFuelIsUsed(substancePriceMap.keySet().iterator().next(), efficiency));
408 }
catch (OptimizationException e) {
410 "Failed to determine the correct fuel mix. Adding only fuel number 1 in fuel mix out of {} substances and minimum quality of {}",
411 substancePriceMap.size(), minimumFuelMixQuality);
412 logger.info(
"The fuel added is: {}", substancePriceMap.keySet().iterator().next().getName());
415 fuelMix =
new HashSet<SubstanceShareInFuelMix>();
417 Substance substance = substancePriceMap.keySet().iterator().next();
419 ssifm.setShare(calculateFuelConsumptionWhenOnlyOneFuelIsUsed(substance, efficiency));
420 ssifm.setSubstance(substance);
421 logger.info(
"Setting fuel consumption for {} to {}", ssifm.getSubstance().getName(), ssifm.getShare());
428 public double convertFuelShareToMassVolume(
double share) {
432 public double calculateFuelConsumptionWhenOnlyOneFuelIsUsed(Substance substance,
double efficiency) {
434 double fuelConsumptionPerMWhElectricityProduced = convertFuelShareToMassVolume(1 / (efficiency * substance.getEnergyDensity()));
436 return fuelConsumptionPerMWhElectricityProduced;
457 double q = 1 + interestRate;
458 double annuity = totalLoan * (Math.pow(q, payBackTime) * (q - 1)) / (Math.pow(q, payBackTime) - 1);
471 long yearsLookingBackForRegression,
long clearingTick) {
472 return determineExpectedCO2PriceInclTax(futureTimePoint, yearsLookingBackForRegression, 0, clearingTick);
484 long yearsLookingBackForRegression,
int adjustmentForDetermineFuelMix,
long clearingTick) {
485 HashMap<ElectricitySpotMarket, Double> co2Prices =
new HashMap<ElectricitySpotMarket, Double>();
486 CO2Auction co2Auction = reps.marketRepository.findCO2Auction();
488 Iterable<ClearingPoint> cps = reps.clearingPointRepository.findAllClearingPointsForMarketAndTimeRange(
489 co2Auction, clearingTick - yearsLookingBackForRegression + 1 - adjustmentForDetermineFuelMix,
490 clearingTick - adjustmentForDetermineFuelMix,
false);
492 SimpleRegression sr =
new SimpleRegression();
493 Government government = reps.template.findAll(Government.class).iterator().next();
494 double lastPrice = 0;
495 double averagePrice = 0;
498 sr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
499 lastPrice = clearingPoint.getPrice();
500 averagePrice += lastPrice;
503 averagePrice = averagePrice / i;
504 double expectedCO2Price;
506 expectedCO2Price = sr.predict(futureTimePoint);
507 expectedCO2Price = Math.max(0, expectedCO2Price);
508 expectedCO2Price = Math.min(expectedCO2Price, government.getCo2Penalty(futureTimePoint));
510 expectedCO2Price = lastPrice;
513 expectedCO2Price = (expectedCO2Price + averagePrice) / 2;
515 double nationalCo2MinPriceinFutureTick = reps.nationalGovernmentRepository.findNationalGovernmentByElectricitySpotMarket(esm)
516 .getMinNationalCo2PriceTrend().getValue(futureTimePoint);
517 double co2PriceInCountry = 0d;
518 if (expectedCO2Price > nationalCo2MinPriceinFutureTick) {
519 co2PriceInCountry = expectedCO2Price;
521 co2PriceInCountry = nationalCo2MinPriceinFutureTick;
523 co2PriceInCountry += reps.genericRepository.findFirst(Government.class).getCO2Tax(futureTimePoint);
524 co2Prices.put(esm, Double.valueOf(co2PriceInCountry));
538 public Map<Substance, Double>
predictFuelPrices(
long numberOfYearsBacklookingForForecasting,
long futureTimePoint,
541 Map<Substance, Double> expectedFuelPrices =
new HashMap<Substance, Double>();
542 for (
Substance substance : reps.substanceRepository.findAllSubstancesTradedOnCommodityMarkets()) {
545 Iterable<ClearingPoint> cps = reps.clearingPointRepository
546 .findAllClearingPointsForSubstanceTradedOnCommodityMarkesAndTimeRange(substance, getCurrentTick()
547 - (numberOfYearsBacklookingForForecasting - 1), getCurrentTick(),
false);
556 gtr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
558 double forecast = gtr.predict(futureTimePoint);
559 if (Double.isNaN(forecast)) {
560 expectedFuelPrices.put(
562 reps.clearingPointRepositoryOld.findClearingPointForMarketAndTime(
563 reps.marketRepository.findFirstMarketBySubstance(substance), getCurrentTick(),
false)
566 expectedFuelPrices.put(substance, forecast);
572 return expectedFuelPrices;
Set< SubstanceShareInFuelMix > calculateFuelMix(PowerPlant plant, Map< Substance, Double > substancePriceMap, double co2Price)
double calculatePaymentEffictiveCO2NationalMinimumPriceCost(PowerPlant powerPlant, boolean forecast, long clearingTick)
double findLastKnownPriceForSubstance(Substance substance, long clearingTick)
Map< Substance, Double > predictFuelPrices(long numberOfYearsBacklookingForForecasting, long futureTimePoint, long clearingTick)
double determineLoanAnnuities(double totalLoan, double payBackTime, double interestRate)
double findLastKnownPriceOnMarket(DecarbonizationMarket market, long clearingTick)
HashMap< ElectricitySpotMarket, Double > determineExpectedCO2PriceInclTax(long futureTimePoint, long yearsLookingBackForRegression, long clearingTick)
HashMap< ElectricitySpotMarket, Double > determineExpectedCO2PriceInclTax(long futureTimePoint, long yearsLookingBackForRegression, int adjustmentForDetermineFuelMix, long clearingTick)