EMlab-generation Documentation  1.0
Documentation of the EMLab-Generation model.
AbstractEnergyProducerRole.java
1 /*******************************************************************************
2  * Copyright 2012 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 package emlab.gen.role;
17 
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;
23 import java.util.Map;
24 import java.util.Set;
25 
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;
35 
36 import agentspring.role.AbstractRole;
37 import agentspring.trend.GeometricTrend;
50 
51 public abstract class AbstractEnergyProducerRole<T extends EnergyProducer> extends AbstractRole<T> {
52 
53  @Autowired
54  Reps reps;
55 
56  public double calculateMarginalCO2Cost(PowerPlant powerPlant, long tick, boolean forecast) {
57  double mc = 0d;
58  // fuel cost
59  mc += calculateCO2TaxMarginalCost(powerPlant, tick);
60  mc += calculateCO2MarketMarginalCost(powerPlant, tick, forecast);
61  logger.info("Margincal cost for plant {} is {}", powerPlant.getName(), mc);
62  return mc;
63  }
64 
65  public double calculateMarginalCostExclCO2MarketCost(PowerPlant powerPlant, long clearingTick) {
66  double mc = 0d;
67  // fuel cost
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);
71  return mc;
72  }
73 
74  public double calculateExpectedMarginalCostExclCO2MarketCost(PowerPlant powerPlant,
75  Map<Substance, Double> forecastedFuelPrices, long tick) {
76  double mc = 0d;
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);
80  return mc;
81  }
82 
83  public double calculateMarginalFuelCost(PowerPlant powerPlant, long clearingTick) {
84  double fc = 0d;
85  // fuel cost for each fuel
86  for (SubstanceShareInFuelMix mix : powerPlant.getFuelMix()) {
87 
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);
93  }
94 
95  return fc;
96  }
97 
98  public double calculateExpectedMarginalFuelCost(PowerPlant powerPlant, Map<Substance, Double> forecastedFuelPrices) {
99  double fc = 0d;
100  // fuel cost for each fuel
101  for (SubstanceShareInFuelMix mix : powerPlant.getFuelMix()) {
102 
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);
108  }
109 
110  return fc;
111  }
112 
121  public double findLastKnownPriceOnMarket(DecarbonizationMarket market, long clearingTick) {
122  Double average = calculateAverageMarketPriceBasedOnClearingPoints(reps.clearingPointRepositoryOld
123  .findClearingPointsForMarketAndTime(market, clearingTick, false));
124  Substance substance = market.getSubstance();
125 
126  if (average != null) {
127  logger.info("Average price found on market for this tick for {}", substance.getName());
128  return average;
129  }
130 
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());
136  return average;
137  }
138 
139  if (market.getReferencePrice() > 0) {
140  logger.info("Found a reference price found for market for {}", substance.getName());
141  return market.getReferencePrice();
142  }
143 
144  for (CommoditySupplier supplier : reps.genericRepository.findAll(CommoditySupplier.class)) {
145  if (supplier.getSubstance().equals(substance)) {
146 
147  return supplier.getPriceOfCommodity().getValue(clearingTick);
148  }
149  }
150 
151  logger.info("No price has been found for {}", substance.getName());
152  return 0d;
153  }
154 
163  public double findLastKnownPriceForSubstance(Substance substance, long clearingTick) {
164 
165  DecarbonizationMarket market = reps.marketRepository.findFirstMarketBySubstance(substance);
166  if (market == null) {
167  logger.warn("No market found for {} so no price can be found", substance.getName());
168  return 0d;
169  } else {
170  return findLastKnownPriceOnMarket(market, clearingTick);
171  }
172  }
173 
181  private Double calculateAverageMarketPriceBasedOnClearingPoints(Iterable<ClearingPoint> clearingPoints) {
182  double priceTimesVolume = 0d;
183  double volume = 0d;
184 
185  for (ClearingPoint point : clearingPoints) {
186  priceTimesVolume += point.getPrice() * point.getVolume();
187  volume += point.getVolume();
188  }
189  if (volume > 0) {
190  return priceTimesVolume / volume;
191  }
192  return null;
193  }
194 
195  public double calculateCO2MarketMarginalCost(PowerPlant powerPlant, long clearingTick, boolean forecast) {
196  double co2Intensity = powerPlant.calculateEmissionIntensity();
197  CO2Auction auction = reps.genericRepository.findFirst(CO2Auction.class);
198  double co2Price;
199  try{
200  co2Price = reps.clearingPointRepository.findClearingPointForMarketAndTime(auction, clearingTick, forecast)
201  .getPrice();
202  } catch (Exception e) {
203  logger.warn("Couldn't find clearing point for tick {} and market {}", clearingTick, auction);
204  co2Price = findLastKnownCO2Price(clearingTick);
205  }
206 
207 
208  return co2Intensity * co2Price;
209  }
210 
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;
217  }
218 
225  public double calculatePaymentEffictiveCO2NationalMinimumPriceCost(PowerPlant powerPlant, boolean forecast,
226  long clearingTick) {
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;
236  else
237  paymentEffectivePartOfNationalCO2 = 0;
238  return co2Intensity * paymentEffectivePartOfNationalCO2 * electricityOutput;
239  }
240 
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;
246  }
247 
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;
254  }
255 
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;
262  return taxToPay;
263  }
264 
265  // TODO: needs to be updated and used somewhere
266  public double calculateFixedOperatingCost(PowerPlant powerPlant, long clearingTick) {
267 
268  double norm = powerPlant.getActualFixedOperatingCost();
269  long timeConstructed = powerPlant.getConstructionStartTime() + powerPlant.calculateActualLeadtime();
270  double mod = powerPlant.getTechnology().getFixedOperatingCostModifierAfterLifetime();
271  long lifetime = powerPlant.calculateActualLifetime();
272 
273  GeometricTrend trend = new GeometricTrend();
274  trend.setGrowthRate(mod);
275  trend.setStart(norm);
276 
277  double currentCost = trend.getValue(clearingTick - (timeConstructed + lifetime));
278  return currentCost;
279  }
280 
281  public double calculateAveragePastOperatingProfit(PowerPlant pp, long horizon) {
282 
283  double averageFractionInMerit = 0d;
284  for (long i = -horizon; i <= 0; i++) {
285  averageFractionInMerit += calculatePastOperatingProfitInclFixedOMCost(pp, getCurrentTick() + i) / i;
286  }
287  return averageFractionInMerit;
288  }
289 
290  public double calculatePastOperatingProfitInclFixedOMCost(PowerPlant plant, long clearingTick) {
291  double pastOP = 0d;
292  // TODO get all accepted supply bids and calculate income
293  // TODO get all accepted demand bids and calculate costs
294  // TODO get the CO2 cost
295  // TODO get the fixed cost
296  pastOP += calculateFixedOperatingCost(plant, clearingTick);
297  return pastOP;
298  }
299 
300 
316  public Set<SubstanceShareInFuelMix> calculateFuelMix(PowerPlant plant, Map<Substance, Double> substancePriceMap, double co2Price) {
317 
318  double efficiency = plant.getActualEfficiency();
319 
320  Set<SubstanceShareInFuelMix> fuelMix = (plant.getFuelMix() == null) ? new HashSet<SubstanceShareInFuelMix>() : plant.getFuelMix();
321 
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) {
327  SubstanceShareInFuelMix ssifm = null;
328  if (!fuelMix.isEmpty()) {
329  ssifm = fuelMix.iterator().next();
330  } else {
331  ssifm = new SubstanceShareInFuelMix().persist();
332  fuelMix.add(ssifm);
333  }
334 
335  Substance substance = substancePriceMap.keySet().iterator().next();
336 
337  ssifm.setShare(calculateFuelConsumptionWhenOnlyOneFuelIsUsed(substance, efficiency));
338  ssifm.setSubstance(substance);
339  logger.info("Setting fuel consumption for {} to {}", ssifm.getSubstance().getName(), ssifm.getShare());
340 
341  return fuelMix;
342  } else {
343 
344  double minimumFuelMixQuality = plant.getTechnology().getMinimumFuelQuality();
345 
346  double[] fuelAndCO2Costs = new double[numberOfFuels];
347  double[] fuelDensities = new double[numberOfFuels];
348  double[] fuelQuality = new double[numberOfFuels];
349 
350  int i = 0;
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];
355  i++;
356  }
357 
358  logger.info("Fuel prices: {}", fuelAndCO2Costs);
359  logger.info("Fuel densities: {}", fuelDensities);
360  logger.info("Fuel purities: {}", fuelQuality);
361 
362  // Objective function = minimize fuel cost (fuel
363  // consumption*fuelprices
364  // + CO2 intensity*co2 price/tax)
365  LinearObjectiveFunction function = new LinearObjectiveFunction(fuelAndCO2Costs, 0d);
366 
367  List<LinearConstraint> constraints = new ArrayList<LinearConstraint>();
368 
369  // Constraint 1: total fuel density * fuel consumption should match
370  // required energy input
371  constraints.add(new LinearConstraint(fuelDensities, Relationship.EQ, (1 / efficiency)));
372 
373  // Constraint 2&3: minimum fuel quality (times fuel consumption)
374  // required
375  // The equation is derived from (example for 2 fuels): q1 * x1 / (x1+x2) + q2 * x2 / (x1+x2) >= qmin
376  // so that the fuelquality weighted by the mass percentages is greater than the minimum fuel quality.
377  constraints.add(new LinearConstraint(fuelQuality, Relationship.GEQ, 0));
378 
379  try {
380  SimplexSolver solver = new SimplexSolver();
381  RealPointValuePair solution = solver.optimize(function, constraints, GoalType.MINIMIZE, true);
382 
383  logger.info("Succesfully solved a linear optimization for fuel mix");
384 
385  int f = 0;
386  Iterator<SubstanceShareInFuelMix> iterator = plant.getFuelMix().iterator();
387  for (Substance substance : substancePriceMap.keySet()) {
388  double share = solution.getPoint()[f];
389 
391  if (iterator.hasNext()) {
392  ssifm = iterator.next();
393  } else {
394  ssifm = new SubstanceShareInFuelMix().persist();
395  fuelMix.add(ssifm);
396  }
397 
398  double fuelConsumptionPerMWhElectricityProduced = convertFuelShareToMassVolume(share);
399  logger.info("Setting fuel consumption for {} to {}", substance.getName(), fuelConsumptionPerMWhElectricityProduced);
400  ssifm.setShare(fuelConsumptionPerMWhElectricityProduced);
401  ssifm.setSubstance(substance);
402  f++;
403  }
404 
405  logger.info("If single fired, it would have been: {}",
406  calculateFuelConsumptionWhenOnlyOneFuelIsUsed(substancePriceMap.keySet().iterator().next(), efficiency));
407  return fuelMix;
408  } catch (OptimizationException e) {
409  logger.warn(
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());
413 
414  // Override the old one
415  fuelMix = new HashSet<SubstanceShareInFuelMix>();
416  SubstanceShareInFuelMix ssifm = new SubstanceShareInFuelMix().persist();
417  Substance substance = substancePriceMap.keySet().iterator().next();
418 
419  ssifm.setShare(calculateFuelConsumptionWhenOnlyOneFuelIsUsed(substance, efficiency));
420  ssifm.setSubstance(substance);
421  logger.info("Setting fuel consumption for {} to {}", ssifm.getSubstance().getName(), ssifm.getShare());
422  fuelMix.add(ssifm);
423  return fuelMix;
424  }
425  }
426  }
427 
428  public double convertFuelShareToMassVolume(double share) {
429  return share * 3600;
430  }
431 
432  public double calculateFuelConsumptionWhenOnlyOneFuelIsUsed(Substance substance, double efficiency) {
433 
434  double fuelConsumptionPerMWhElectricityProduced = convertFuelShareToMassVolume(1 / (efficiency * substance.getEnergyDensity()));
435 
436  return fuelConsumptionPerMWhElectricityProduced;
437 
438  }
439 
446  /*
447  * public double determineAnnuitizedInvestmentCost(PowerPlant powerPlant, long time) {
448  *
449  * double invNorm = powerPlant.getTechnology().getAnnuitizedInvestmentCost(); double modifierExo = calculateExogenousModifier(powerPlant.getTechnology(). getInvestmentCostModifierExogenous(),
450  * time);
451  *
452  * double annuitizedInvestmentCost = invNorm * modifierExo; logger.info("Investment cost of plant{} is {}", powerPlant, annuitizedInvestmentCost); return annuitizedInvestmentCost; }
453  */
454 
455  public double determineLoanAnnuities(double totalLoan, double payBackTime, double interestRate) {
456 
457  double q = 1 + interestRate;
458  double annuity = totalLoan * (Math.pow(q, payBackTime) * (q - 1)) / (Math.pow(q, payBackTime) - 1);
459 
460  return annuity;
461  }
462 
463 
470  protected HashMap<ElectricitySpotMarket, Double> determineExpectedCO2PriceInclTax(long futureTimePoint,
471  long yearsLookingBackForRegression, long clearingTick) {
472  return determineExpectedCO2PriceInclTax(futureTimePoint, yearsLookingBackForRegression, 0, clearingTick);
473  }
474 
483  protected HashMap<ElectricitySpotMarket, Double> determineExpectedCO2PriceInclTax(long futureTimePoint,
484  long yearsLookingBackForRegression, int adjustmentForDetermineFuelMix, long clearingTick) {
485  HashMap<ElectricitySpotMarket, Double> co2Prices = new HashMap<ElectricitySpotMarket, Double>();
486  CO2Auction co2Auction = reps.marketRepository.findCO2Auction();
487  //Find Clearing Points for the last 5 years (counting current year as one of the last 5 years).
488  Iterable<ClearingPoint> cps = reps.clearingPointRepository.findAllClearingPointsForMarketAndTimeRange(
489  co2Auction, clearingTick - yearsLookingBackForRegression + 1 - adjustmentForDetermineFuelMix,
490  clearingTick - adjustmentForDetermineFuelMix, false);
491  // Create regression object and calculate average
492  SimpleRegression sr = new SimpleRegression();
493  Government government = reps.template.findAll(Government.class).iterator().next();
494  double lastPrice = 0;
495  double averagePrice = 0;
496  int i = 0;
497  for (ClearingPoint clearingPoint : cps) {
498  sr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
499  lastPrice = clearingPoint.getPrice();
500  averagePrice += lastPrice;
501  i++;
502  }
503  averagePrice = averagePrice / i;
504  double expectedCO2Price;
505  if(i>1){
506  expectedCO2Price = sr.predict(futureTimePoint);
507  expectedCO2Price = Math.max(0, expectedCO2Price);
508  expectedCO2Price = Math.min(expectedCO2Price, government.getCo2Penalty(futureTimePoint));
509  }else{
510  expectedCO2Price = lastPrice;
511  }
512  // Calculate average of regression and past average:
513  expectedCO2Price = (expectedCO2Price + averagePrice) / 2;
514  for (ElectricitySpotMarket esm : reps.marketRepository.findAllElectricitySpotMarkets()) {
515  double nationalCo2MinPriceinFutureTick = reps.nationalGovernmentRepository.findNationalGovernmentByElectricitySpotMarket(esm)
516  .getMinNationalCo2PriceTrend().getValue(futureTimePoint);
517  double co2PriceInCountry = 0d;
518  if (expectedCO2Price > nationalCo2MinPriceinFutureTick) {
519  co2PriceInCountry = expectedCO2Price;
520  } else {
521  co2PriceInCountry = nationalCo2MinPriceinFutureTick;
522  }
523  co2PriceInCountry += reps.genericRepository.findFirst(Government.class).getCO2Tax(futureTimePoint);
524  co2Prices.put(esm, Double.valueOf(co2PriceInCountry));
525  }
526  return co2Prices;
527  }
528 
538  public Map<Substance, Double> predictFuelPrices(long numberOfYearsBacklookingForForecasting, long futureTimePoint,
539  long clearingTick) {
540  // Fuel Prices
541  Map<Substance, Double> expectedFuelPrices = new HashMap<Substance, Double>();
542  for (Substance substance : reps.substanceRepository.findAllSubstancesTradedOnCommodityMarkets()) {
543  // Find Clearing Points for the last 5 years (counting current year
544  // as one of the last 5 years).
545  Iterable<ClearingPoint> cps = reps.clearingPointRepository
546  .findAllClearingPointsForSubstanceTradedOnCommodityMarkesAndTimeRange(substance, getCurrentTick()
547  - (numberOfYearsBacklookingForForecasting - 1), getCurrentTick(), false);
548  // logger.warn("{}, {}",
549  // getCurrentTick()-(agent.getNumberOfYearsBacklookingForForecasting()-1),
550  // getCurrentTick());
551  // Create regression object
553  for (ClearingPoint clearingPoint : cps) {
554  // logger.warn("CP {}: {} , in" + clearingPoint.getTime(),
555  // substance.getName(), clearingPoint.getPrice());
556  gtr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
557  }
558  double forecast = gtr.predict(futureTimePoint);
559  if (Double.isNaN(forecast)) {
560  expectedFuelPrices.put(
561  substance,
562  reps.clearingPointRepositoryOld.findClearingPointForMarketAndTime(
563  reps.marketRepository.findFirstMarketBySubstance(substance), getCurrentTick(), false)
564  .getPrice());
565  } else {
566  expectedFuelPrices.put(substance, forecast);
567  }
568 
569  // logger.warn("Forecast {}: {}, in Step " + futureTimePoint,
570  // substance, expectedFuelPrices.get(substance));
571  }
572  return expectedFuelPrices;
573  }
574 
575 }
Set< SubstanceShareInFuelMix > calculateFuelMix(PowerPlant plant, Map< Substance, Double > substancePriceMap, double co2Price)
double calculatePaymentEffictiveCO2NationalMinimumPriceCost(PowerPlant powerPlant, boolean forecast, 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)