EMlab-generation Documentation  1.0
Documentation of the EMLab-Generation model.
AbstractClearElectricitySpotMarketRole.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.market;
17 
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 
22 import org.apache.commons.math.stat.regression.SimpleRegression;
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.data.neo4j.support.Neo4jTemplate;
25 import org.springframework.transaction.annotation.Transactional;
26 
27 import agentspring.role.AbstractRole;
45 
55 public abstract class AbstractClearElectricitySpotMarketRole<T extends DecarbonizationModel> extends AbstractRole<T> {
56 
57  @Autowired
58  private Reps reps;
59 
60  @Autowired
61  private Neo4jTemplate template;
62 
63  protected final double epsilon = 1e-3;
64 
65  class MarketSegmentClearingOutcome {
66  HashMap<ElectricitySpotMarket, Double> loads = new HashMap<ElectricitySpotMarket, Double>();
67  HashMap<ElectricitySpotMarket, Double> prices = new HashMap<ElectricitySpotMarket, Double>();
68  HashMap<ElectricitySpotMarket, Double> supplies = new HashMap<ElectricitySpotMarket, Double>();
69 
70  @Override
71  public String toString() {
72  return new String("Market outcome: loads " + loads + " prices: " + prices + " supplies: " + supplies);
73  }
74  }
75 
76  class GlobalSegmentClearingOutcome {
77  Map<ElectricitySpotMarket, Double> loads;
78  Map<ElectricitySpotMarket, Double> supplies = new HashMap<ElectricitySpotMarket, Double>();
79  double globalLoad;
80  double globalPrice;
81  double globalSupply;
82 
83  @Override
84  public String toString() {
85  return "Global Data; loads: " + loads + ", supplies: " + supplies + " globalLoad: " + globalLoad + ", globalSupply: "
86  + globalSupply;
87  }
88  }
89 
90  public class CO2Iteration {
91  public boolean stable;
92  public double co2Price;
93  public double co2Emissions;
94 
95  }
96 
97  public class CO2PriceStability extends CO2Iteration {
98 
99  public boolean positive;
100  public double iterationSpeedFactor;
101  public double changeInDeviationFromLastStep;
102 
103  }
104 
105  public class CO2SecantSearch extends CO2Iteration {
106  public boolean twoPricesExistWithBelowAboveEmissions;
107  public double higherCO2Price;
108  public int iteration = 0;
109  public PriceEmissionPair tooLowEmissionsPair;
110  public PriceEmissionPair tooHighEmissionsPair;
111  public double bankingEffectiveMinimumPrice;
112 
113  /*
114  * (non-Javadoc)
115  *
116  * @see java.lang.Object#toString()
117  */
118  @Override
119  public String toString() {
120  // TODO Auto-generated method stub
121  return "Stable: " + this.stable + ", co2Price: " + this.co2Price + ", co2Emissions: " + this.co2Emissions;
122  }
123 
124  }
125 
126  class PriceEmissionPair {
127  public double price;
128  public double emission;
129  }
130 
131  CO2SecantSearch co2PriceSecantSearchUpdate(CO2SecantSearch co2SecantSearch, DecarbonizationModel model,
132  Government government, boolean forecast, long clearingTick, double co2CapAdjustment) {
133 
134  co2SecantSearch.stable = false;
135  double capDeviationCriterion = model.getCapDeviationCriterion();
136  double co2Cap = government.getCo2Cap(clearingTick) + co2CapAdjustment;
137  co2SecantSearch.co2Emissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(forecast, clearingTick);
138 
139  double deviation = (co2SecantSearch.co2Emissions - co2Cap) / co2Cap;
140 
141  // Check if current price leads to emissions close to the cap.
142  if (Math.abs(deviation) < capDeviationCriterion) {
143  // logger.warn("Deviation is less than capDeviationCriterion");
144  co2SecantSearch.stable = true;
145  return co2SecantSearch;
146  }
147 
148  // Check and update the twoPricesExistWithBelowAboveEmissions
149 
150  if (co2SecantSearch.tooHighEmissionsPair != null && co2SecantSearch.tooLowEmissionsPair != null) {
151  co2SecantSearch.twoPricesExistWithBelowAboveEmissions = true;
152  } else if (co2SecantSearch.co2Price == government.getMinCo2Price(clearingTick)
153  && co2SecantSearch.co2Emissions < co2Cap) {
154  // logger.warn("Deviation CO2 price has reached minimum");
155  // check if stable enough --> 2. Cap is met with a co2Price
156  // equal to the minimum co2 price
157  co2SecantSearch.stable = true;
158  return co2SecantSearch;
159  } else if (co2SecantSearch.co2Price >= government.getCo2Penalty(clearingTick)
160  && co2SecantSearch.co2Emissions >= co2Cap) {
161  // Only if above the cap...
162  // logger.warn("CO2 price ceiling reached {}", co2SecantSearch.co2Price);
163  co2SecantSearch.co2Price = government.getCo2Penalty(clearingTick);
164  co2SecantSearch.stable = true;
165  return co2SecantSearch;
166  }
167 
168  // Check whether we know two pairs, one with EmissionsAboveCap, one with EmissionsBelowCap
169  // in case of yes: calculate new CO2 price via secant calculation. In case of no: Take last known
170  // price above or below, or halve/double the price.
171  if (co2SecantSearch.twoPricesExistWithBelowAboveEmissions) {
172 
173  // Update the emission pairs
174  if (deviation > 0) {
175  co2SecantSearch.tooHighEmissionsPair.price = co2SecantSearch.co2Price;
176  co2SecantSearch.tooHighEmissionsPair.emission = co2SecantSearch.co2Emissions;
177  } else {
178  co2SecantSearch.tooLowEmissionsPair.price = co2SecantSearch.co2Price;
179  co2SecantSearch.tooLowEmissionsPair.emission = co2SecantSearch.co2Emissions;
180  }
181 
182  double p2 = co2SecantSearch.tooHighEmissionsPair.price;
183  double p1 = co2SecantSearch.tooLowEmissionsPair.price;
184  double e2 = co2SecantSearch.tooHighEmissionsPair.emission - co2Cap;
185  double e1 = co2SecantSearch.tooLowEmissionsPair.emission - co2Cap;
186 
187  // Interrupts long iterations by making a binary search step.
188  if (co2SecantSearch.iteration < 5) {
189  co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
190  co2SecantSearch.iteration++;
191  // logger.warn("New CO2 Secant price {}", co2SecantSearch.co2Price);
192  } else {
193  co2SecantSearch.co2Price = (p1 + p2) / 2;
194  co2SecantSearch.iteration = 0;
195  // logger.warn("New CO2 Binary price {}", co2SecantSearch.co2Price);
196  }
197 
198  } else {
199 
200  if (deviation > 0) {
201  if (co2SecantSearch.tooHighEmissionsPair == null)
202  co2SecantSearch.tooHighEmissionsPair = new PriceEmissionPair();
203 
204  co2SecantSearch.tooHighEmissionsPair.price = co2SecantSearch.co2Price;
205  co2SecantSearch.tooHighEmissionsPair.emission = co2SecantSearch.co2Emissions;
206 
207  if (co2SecantSearch.tooLowEmissionsPair == null) {
208  co2SecantSearch.co2Price = (co2SecantSearch.co2Price != 0d) ? ((co2SecantSearch.co2Price * 2 < government
209  .getCo2Penalty(clearingTick)) ? (co2SecantSearch.co2Price * 2) : government
210  .getCo2Penalty(clearingTick)) : 5d;
211  // logger.warn("New doubled CO2 search price {}", co2SecantSearch.co2Price);
212  } else {
213  double p2 = co2SecantSearch.tooHighEmissionsPair.price;
214  double p1 = co2SecantSearch.tooLowEmissionsPair.price;
215  double e2 = co2SecantSearch.tooHighEmissionsPair.emission - co2Cap;
216  double e1 = co2SecantSearch.tooLowEmissionsPair.emission - co2Cap;
217 
218  co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
219  co2SecantSearch.iteration++;
220  // logger.warn("New CO2 Secant price {}", co2SecantSearch.co2Price);
221  }
222 
223  } else {
224 
225  if (co2SecantSearch.tooLowEmissionsPair == null)
226  co2SecantSearch.tooLowEmissionsPair = new PriceEmissionPair();
227 
228  co2SecantSearch.tooLowEmissionsPair.price = co2SecantSearch.co2Price;
229  co2SecantSearch.tooLowEmissionsPair.emission = co2SecantSearch.co2Emissions;
230 
231  if (co2SecantSearch.tooHighEmissionsPair == null) {
232  co2SecantSearch.co2Price = (co2SecantSearch.co2Price / 2);
233  // logger.warn("New halved CO2 search price {}", co2SecantSearch.co2Price);
234  } else {
235  double p2 = co2SecantSearch.tooHighEmissionsPair.price;
236  double p1 = co2SecantSearch.tooLowEmissionsPair.price;
237  double e2 = co2SecantSearch.tooHighEmissionsPair.emission - co2Cap;
238  double e1 = co2SecantSearch.tooLowEmissionsPair.emission - co2Cap;
239 
240  co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
241  // logger.warn("New CO2 Secant price {}", co2SecantSearch.co2Price);
242  co2SecantSearch.iteration++;
243 
244  }
245 
246  if (co2SecantSearch.co2Price < 0.5
247  || co2SecantSearch.co2Price - government.getMinCo2Price(clearingTick) < 0.5) {
248  co2SecantSearch.co2Price = government.getMinCo2Price(clearingTick);
249  co2SecantSearch.stable = true;
250  }
251 
252  }
253 
254  }
255 
256  return co2SecantSearch;
257 
258  }
259 
269  double clearGlobalMarketWithNoCapacityConstraints(Segment segment, GlobalSegmentClearingOutcome globalOutcome,
270  boolean forecast, long clearingTick) {
271 
272  double marginalPlantMarginalCost = Double.MAX_VALUE;
273 
274  for (PowerPlantDispatchPlan plan : reps.powerPlantDispatchPlanRepository.findSortedPowerPlantDispatchPlansForSegmentForTime(
275  segment, clearingTick, forecast)) {
276  ElectricitySpotMarket myMarket = (ElectricitySpotMarket) plan.getBiddingMarket();
277 
278  // Make it produce as long as there is load.
279  double plantSupply = determineProductionOnSpotMarket(plan, globalOutcome.globalSupply, globalOutcome.globalLoad);
280 
281  if (plantSupply > 0) {
282  // Plant is producing, store the information to determine price
283  // and so on.
284  marginalPlantMarginalCost = plan.getPrice();
285  globalOutcome.supplies.put(myMarket, globalOutcome.supplies.get(myMarket) + plantSupply);
286  globalOutcome.globalSupply = +globalOutcome.globalSupply + plantSupply;
287  // logger.warn("Storing price: {} for plant {} in market " +
288  // myMarket, plantCost.getValue(), plant);
289 
290  }
291  }
292 
293  return marginalPlantMarginalCost;
294  }
295 
306  @Transactional
307  void determineCommitmentOfPowerPlantsOnTheBasisOfLongTermContracts(List<Segment> segments, boolean forecast) {
308 
309  for (EnergyProducer producer : reps.genericRepository.findAll(EnergyProducer.class)) {
310 
311  for (Segment segment : segments) {
312 
313  // How much capacity is contracted by long term contracts in
314  // this segment?
315  double contractedCapacityInSegment = 0;
316 
317  for (LongTermContract ltc : reps.contractRepository.findLongTermContractsForEnergyProducerForSegmentActiveAtTime(producer,
318  segment, getCurrentTick())) {
319  contractedCapacityInSegment += ltc.getCapacity();
320  }
321 
322  // for all power plants in the sorted marginal cost map
323  for (PowerPlantDispatchPlan plan : reps.powerPlantDispatchPlanRepository
324  .findAllPowerPlantDispatchPlansForEnergyProducerForTimeAndSegment(segment, producer,
325  getCurrentTick(), forecast)) {
326  PowerPlant plant = plan.getPowerPlant();
327 
328  double availableCapacity = plant.getAvailableCapacity(getCurrentTick(), segment, segments.size());
329 
330  // logger.warn("Capacity of plant " + plant.toString() +
331  // " is " + availableCapacity);
332 
333  if (plant.getTechnology().isApplicableForLongTermContract()) {
334 
335  if (contractedCapacityInSegment - availableCapacity > 0) {
336 
337  // the whole plant has to be used for long term
338  // contract
339  plan.setCapacityLongTermContract(availableCapacity);
340  } else {
341  // use the contractedCapacity left for long term
342  // contract
343  plan.setCapacityLongTermContract(contractedCapacityInSegment);
344  }
345  } else {
346  // Not applicable for LTC, so save 0 capacity for
347  // it.
348  plan.setCapacityLongTermContract(0);
349  }
350 
351  // Update the capacity available for the spot market,
352  // which is the total capacity available
353  // minus committed to LTC's.
354  plan.setAmount(availableCapacity - plan.getCapacityLongTermContract());
355  contractedCapacityInSegment -= plan.getCapacityLongTermContract();
356 
357  }
358  }
359  }
360  }
361 
370  Map<ElectricitySpotMarket, Double> determineActualDemandForSpotMarkets(Segment segment, Map<ElectricitySpotMarket,Double> demandGrowthMap) {
371 
372  if (demandGrowthMap == null) {
373  demandGrowthMap = new HashMap<ElectricitySpotMarket, Double>();
374  for (ElectricitySpotMarket market : reps.marketRepository.findAllElectricitySpotMarkets()) {
375  demandGrowthMap.put(market, market.getDemandGrowthTrend().getValue(getCurrentTick()));
376  }
377  }
378 
379  Map<ElectricitySpotMarket, Double> loadInMarkets = new HashMap<ElectricitySpotMarket, Double>();
380 
381  for (ElectricitySpotMarket market : reps.marketRepository.findAllElectricitySpotMarkets()) {
382  double baseLoad = reps.segmentLoadRepository.returnSegmentBaseLoadBySegmentAndMarket(segment, market);
383  double load;
384  load = baseLoad * demandGrowthMap.get(market);
385 
386  // Load may be covered by long term contracts.
387  double loadCoveredByLTC = 0d;
388 
389  // for each energy consumer
390  for (EnergyConsumer consumer : reps.genericRepository.findAll(EnergyConsumer.class)) {
391  // for each active LTC
392  for (LongTermContract ltc : reps.contractRepository.findLongTermContractsForEnergyConsumerForSegmentForZoneActiveAtTime(
393  consumer, segment, market.getZone(), getCurrentTick())) {
394  // add tot the total
395  loadCoveredByLTC += ltc.getCapacity();
396  }
397  }
398 
399  // Part of the load may be covered by long term contracts. We
400  // subtract that.
401  loadInMarkets.put(market, load - loadCoveredByLTC);
402  }
403 
404  return loadInMarkets;
405  }
406 
414  double determineTotalLoadFromLoadMap(Map<ElectricitySpotMarket, Double> loadInMarkets) {
415  double totalLoad = 0d;
416  for (ElectricitySpotMarket market : loadInMarkets.keySet()) {
417  totalLoad += loadInMarkets.get(market);
418  }
419  return totalLoad;
420  }
421 
432  double determineProductionOnSpotMarket(PowerPlantDispatchPlan plan, double supplySoFar, double load) {
433 
434  double plantCapacity = plan.getAmount();
435  double plantSupply = 0d;
436 
437  // if after adding the supply of this extra plant demand
438  // is not yet met
439  if ((supplySoFar + plantCapacity) < load) {
440 
441  // Plant will be supplying completely
442  plantSupply = plantCapacity;
443  plan.setStatus(Bid.ACCEPTED);
444  } else {
445 
446  // Plant will by partly supplying and this is the
447  // final plant or is not supplying at all
448  plantSupply = load - supplySoFar;
449  if (plantSupply - epsilon > 0) {
450  plan.setStatus(Bid.PARTLY_ACCEPTED);
451  } else {
452  plan.setStatus(Bid.FAILED);
453  plantSupply = 0d;
454  }
455  }
456 
457  plan.setAcceptedAmount(plantSupply);
458 
459  return plantSupply;
460  }
461 
467  double determineTotalEmissionsBasedOnPowerPlantDispatchPlan(boolean forecast, long clearingTick) {
468  double totalEmissions = 0d;
469  //int counter = 0;
470  for (PowerPlantDispatchPlan plan : reps.powerPlantDispatchPlanRepository.findAllPowerPlantDispatchPlansForTime(
471  clearingTick, forecast)) {
472  double operationalCapacity = plan.getCapacityLongTermContract() + plan.getAcceptedAmount();
473  double emissionIntensity = plan.getPowerPlant().calculateEmissionIntensity();
474  double hours = plan.getSegment().getLengthInHours();
475  totalEmissions += operationalCapacity * emissionIntensity * hours;
476  // counter++;
477  }
478  // logger.warn("Total emissions: {} based on {} power plant dispatch plans", totalEmissions, counter);
479  return totalEmissions;
480  }
481 
488  double determineTotalEmissionsBasedOnPowerPlantDispatchPlanForEnergyProducer(boolean forecast, long clearingTick,
489  EnergyProducer producer) {
490  double totalEmissions = 0d;
491  // int counter = 0;
492  for (PowerPlantDispatchPlan plan : reps.powerPlantDispatchPlanRepository
493  .findAllAcceptedPowerPlantDispatchPlansForEnergyProducerForTime(producer, clearingTick, forecast)) {
494  double operationalCapacity = plan.getCapacityLongTermContract() + plan.getAcceptedAmount();
495  double emissionIntensity = plan.getPowerPlant().calculateEmissionIntensity();
496  double hours = plan.getSegment().getLengthInHours();
497  totalEmissions += operationalCapacity * emissionIntensity * hours;
498  // counter++;
499  }
500  // logger.warn("Total emissions: {} based on {} power plant dispatch plans",
501  // totalEmissions, counter);
502  return totalEmissions;
503  }
504 
516  CO2PriceStability determineStabilityOfCO2andElectricityPricesAndAdjustIfNecessary(CO2PriceStability co2PriceStability,
517  DecarbonizationModel model, Government government, boolean forecast,
518  long clearingTick) {
519 
520  double co2Cap = government.getCo2Cap(clearingTick);
521  double minimumCo2Price = government.getMinCo2Price(clearingTick);
522  double co2Penalty = government.getCo2Penalty(clearingTick);
523  double iterationSpeedCriterion = model.getIterationSpeedCriterion();
524  double capDeviationCriterion = model.getCapDeviationCriterion();
525 
526  co2PriceStability.co2Emissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(forecast, clearingTick);
527  double deviation = (co2PriceStability.co2Emissions - co2Cap) / co2Cap;
528 
529  // Determine the deviation from the cap.
530  logger.warn("Cap {} (euro/ton) vs emissions {} (euro/ton)", co2Cap, co2PriceStability.co2Emissions);
531  logger.warn("Tick {} Deviation: {} %", clearingTick, deviation * 100);
532 
533  // check if the deviation is smaller then the criterion --> 1.
534  // Close to the cap or almost stopped moving
535  if (Math.abs(deviation) < capDeviationCriterion) {
536  logger.warn("Deviation is less than capDeviationCriterion");
537  co2PriceStability.stable = true;
538  } else if (co2PriceStability.iterationSpeedFactor < iterationSpeedCriterion) {
539  logger.warn("Deviation iterationSpeedFactor is less than iterationSpeedCriterion");
540  co2PriceStability.stable = true;
541  } else if (co2PriceStability.co2Price == minimumCo2Price && co2PriceStability.co2Emissions < co2Cap) {
542  logger.warn("Deviation CO2 price has reached minimum");
543  // check if stable enough --> 2. Cap is met with a co2Price
544  // equal to the minimum co2 price
545  co2PriceStability.stable = true;
546 
547  } else if (co2PriceStability.co2Price >= co2Penalty && co2PriceStability.co2Emissions >= co2Cap) {
548  // Only if above the cap...
549  logger.warn("CO2 price ceiling reached {}", co2PriceStability.co2Price);
550  co2PriceStability.stable = true;
551  } else {
552  co2PriceStability.co2Price = co2PriceStability.co2Price * (1 + deviation * co2PriceStability.iterationSpeedFactor);
553  logger.warn("Deviation updated CO2 price to {}", co2PriceStability.co2Price);
554  }
555 
556  // if price is 0, but the cap is not met, we have to
557  // change it, otherwise, you could never get out of 0.
558  // In that case assume stability and assume a price of 2.
559  if (co2PriceStability.co2Price == 0 && co2PriceStability.co2Emissions >= co2Cap) {
560  logger.warn("Deviation resetting CO2 price to 2");
561  co2PriceStability.co2Price = 2;
562  co2PriceStability.stable = true;
563  }
564 
565  // make the speed smaller if we passed by the target
566  if ((co2PriceStability.positive && deviation < 0) || (!co2PriceStability.positive && deviation > 0)) {
567  co2PriceStability.iterationSpeedFactor = co2PriceStability.iterationSpeedFactor / 2;
568  logger.warn("Deviation speed factor decreased {}", co2PriceStability.iterationSpeedFactor);
569  }
570 
571  // If we are below the cap and close to or below the minimum
572  // CO2
573  // price set the price to the minimum co2
574  // price.
575  if ((co2PriceStability.co2Price < (0.1 + minimumCo2Price)) && (co2PriceStability.co2Emissions < co2Cap)) {
576  logger.warn("Deviation reseting CO2 price to minimum");
577  co2PriceStability.co2Price = minimumCo2Price;
578  }
579 
580  // record whether the last change was positive or not
581  if (deviation < 0) {
582  co2PriceStability.positive = false;
583  } else {
584  co2PriceStability.positive = true;
585  }
586 
587  return co2PriceStability;
588  }
589 
598  double findLastKnownPriceForSubstance(Substance substance) {
599 
600  DecarbonizationMarket market = reps.marketRepository.findFirstMarketBySubstance(substance);
601  if (market == null) {
602  logger.warn("No market found for {} so no price can be found", substance.getName());
603  return 0d;
604  } else {
605  return findLastKnownPriceOnMarket(market);
606  }
607  }
608 
617  double findLastKnownPriceOnMarket(DecarbonizationMarket market) {
618  Double average = calculateAverageMarketPriceBasedOnClearingPoints(reps.clearingPointRepositoryOld
619  .findClearingPointsForMarketAndTime(market, getCurrentTick(), false));
620  Substance substance = market.getSubstance();
621 
622  if (average != null) {
623  logger.info("Average price found on market for this tick for {}", substance.getName());
624  return average;
625  }
626 
627  average = calculateAverageMarketPriceBasedOnClearingPoints(reps.clearingPointRepositoryOld.findClearingPointsForMarketAndTime(
628  market, getCurrentTick() - 1, false));
629  if (average != null) {
630  logger.info("Average price found on market for previous tick for {}", substance.getName());
631  return average;
632  }
633 
634  if (market.getReferencePrice() > 0) {
635  logger.info("Found a reference price found for market for {}", substance.getName());
636  return market.getReferencePrice();
637  }
638 
639  for (CommoditySupplier supplier : reps.genericRepository.findAll(CommoditySupplier.class)) {
640  if (supplier.getSubstance().equals(substance)) {
641 
642  logger.info("Price found for {} by asking the supplier {} directly", substance.getName(), supplier.getName());
643  return supplier.getPriceOfCommodity().getValue(getCurrentTick());
644  }
645  }
646 
647  logger.info("No price has been found for {}", substance.getName());
648  return 0d;
649  }
650 
658  private Double calculateAverageMarketPriceBasedOnClearingPoints(Iterable<ClearingPoint> clearingPoints) {
659  double priceTimesVolume = 0d;
660  double volume = 0d;
661 
662  for (ClearingPoint point : clearingPoints) {
663  priceTimesVolume += point.getPrice() * point.getVolume();
664  volume += point.getVolume();
665  }
666  if (volume > 0) {
667  return priceTimesVolume / volume;
668  }
669  return null;
670  }
671 
672  public Reps getReps() {
673  return reps;
674  }
675 
676  public Map<Substance, Double> predictFuelPrices(long numberOfYearsBacklookingForForecasting, long futureTimePoint) {
677  // Fuel Prices
678  Map<Substance, Double> expectedFuelPrices = new HashMap<Substance, Double>();
679  for (Substance substance : reps.substanceRepository.findAllSubstancesTradedOnCommodityMarkets()) {
680 
681  Iterable<ClearingPoint> cps = reps.clearingPointRepository
682  .findAllClearingPointsForSubstanceTradedOnCommodityMarkesAndTimeRange(substance, getCurrentTick()
683  - (numberOfYearsBacklookingForForecasting - 1), getCurrentTick() - 1,
684  false);
685 
686  SimpleRegression gtr = new SimpleRegression();
687  for (ClearingPoint clearingPoint : cps) {
688 
689  gtr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
690  }
691  gtr.addData(getCurrentTick(), findLastKnownPriceForSubstance(substance));
692  double forecast = gtr.predict(futureTimePoint);
693  if (Double.isNaN(forecast)) {
694  expectedFuelPrices.put(substance, findLastKnownPriceForSubstance(substance));
695  } else {
696  expectedFuelPrices.put(substance, forecast);
697  }
698  }
699  return expectedFuelPrices;
700  }
701 
702  public Map<ElectricitySpotMarket, Double> predictDemand(long numberOfYearsBacklookingForForecasting,
703  long futureTimePoint) {
704  Map<ElectricitySpotMarket, Double> expectedDemand = new HashMap<ElectricitySpotMarket, Double>();
705  for (ElectricitySpotMarket elm : reps.template.findAll(ElectricitySpotMarket.class)) {
706  GeometricTrendRegression gtr = new GeometricTrendRegression();
707  for (long time = getCurrentTick(); time > getCurrentTick() - numberOfYearsBacklookingForForecasting
708  && time >= 0; time = time - 1) {
709  gtr.addData(time, elm.getDemandGrowthTrend().getValue(time));
710  }
711  double forecast = gtr.predict(futureTimePoint);
712  if (Double.isNaN(forecast))
713  forecast = elm.getDemandGrowthTrend().getValue(getCurrentTick());
714  expectedDemand.put(elm, forecast);
715  }
716  return expectedDemand;
717  }
718 
719  @Transactional
720  void updatePowerPlanDispatchPlansWithNewCO2Prices(double co2Price,
721  Map<ElectricitySpotMarket, Double> nationalMinCo2Prices, long clearingTick, boolean forecast) {
722  for (PowerPlantDispatchPlan plan : reps.powerPlantDispatchPlanRepository.findAllPowerPlantDispatchPlansForTime(
723  clearingTick, forecast)) {
724  if (nationalMinCo2Prices.get(plan.getBiddingMarket()) > co2Price) {
725  plan.setPrice(plan.getBidWithoutCO2()
726  + (nationalMinCo2Prices.get(plan.getBiddingMarket()) * plan.getPowerPlant().calculateEmissionIntensity()));
727  } else {
728  plan.setPrice(plan.getBidWithoutCO2() + (co2Price * plan.getPowerPlant().calculateEmissionIntensity()));
729  }
730  }
731  }
732 
733 }