EMlab-generation Documentation  1.0
Documentation of the EMLab-Generation model.
ClearIterativeCO2AndElectricitySpotMarketTwoCountryRole.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.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 
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.Role;
28 import agentspring.role.RoleComponent;
46 
61 @RoleComponent
63 AbstractClearElectricitySpotMarketRole<DecarbonizationModel> implements Role<DecarbonizationModel> {
64 
65  @Autowired
66  private Reps reps;
67 
68  @Autowired
69  SubmitOffersToElectricitySpotMarketRole submitOffersToElectricitySpotMarketRole;
70 
71  @Autowired
72  DetermineFuelMixRole determineFuelMixRole;
73 
74  @Autowired
75  MarketStabilityReserveRole marketStabilityReserveRole;
76 
77  @Autowired
78  RenewableAdaptiveCO2CapRole renewableAdaptiveCO2CapRole;
79 
80  @Autowired
81  Neo4jTemplate template;
82 
83  @Override
84  @Transactional
85  public void act(DecarbonizationModel model) {
86  Map<Substance, Double> fuelPriceMap = new HashMap<Substance, Double>();
87  for (Substance substance : template.findAll(Substance.class)) {
88  fuelPriceMap.put(substance, findLastKnownPriceForSubstance(substance));
89  }
90 
91  if (!model.isCo2BankingIsImplemented() || !model.isCo2TradingImplemented())
92  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, false, getCurrentTick(),
93  fuelPriceMap, null, 0);
94  else
95  clearIterativeCO2ElectricitySpotMarketAndFutureMarketTwoCountryForTimestepAndFuelPrices(model,
96  getCurrentTick());
97 
98  }
99 
100  @Transactional
101  public void makeCentralElectricityMarketForecastForTimeStep(long clearingTick) {
102 
103  DecarbonizationModel model = template.findAll(DecarbonizationModel.class).iterator().next();
104 
105  Map<Substance, Double> fuelPriceMap = predictFuelPrices(model.getCentralForecastBacklookingYears(),
106  clearingTick);
107 
108  // logger.warn("Fuel prices: {}", fuelPriceMap);
109 
110  Map<ElectricitySpotMarket, Double> demandGrowthMap = predictDemand(model.getCentralForecastBacklookingYears(),
111  clearingTick);
112 
113  Government government = template.findAll(Government.class).iterator().next();
114 
115  // find national minimum CO2 prices. Initial Map size is 2.
116  Map<ElectricitySpotMarket, Double> nationalMinCo2Prices = new HashMap<ElectricitySpotMarket, Double>(2);
117  Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
118  for (NationalGovernment nG : nationalGovernments) {
119  if (model.isCo2TradingImplemented()) {
120  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
121  .getMinNationalCo2PriceTrend().getValue(clearingTick));
122  } else {
123  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
124  }
125 
126  }
127 
128  determineFuelMixRole.determineFuelMixForecastForYearAndFuelPriceMap(clearingTick, fuelPriceMap,
129  nationalMinCo2Prices);
130 
131  submitOffersToElectricitySpotMarketRole.createOffersForElectricitySpotMarket(null, clearingTick, true,
132  fuelPriceMap);
133 
134  Iterable<PowerPlantDispatchPlan> ppdps = reps.powerPlantDispatchPlanRepository.findAll();
135  for (PowerPlantDispatchPlan ppdp : ppdps) {
136  logger.info(ppdp.toString() + " in " + ppdp.getBiddingMarket() + " accepted: " + ppdp.getAcceptedAmount());
137  }
138 
139  double previouslyBankedCertificates = reps.decarbonizationAgentRepository
140  .determineTotallyBankedCO2Certificates();
141 
142  if (clearingTick > model.getCentralForecastingYear()) {
143  CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
144  ClearingPoint forecastedCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(
145  co2Auction, clearingTick - 1, true);
146  ClearingPoint lastCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(co2Auction,
147  clearingTick - model.getCentralForecastingYear() - 1, false);
148 
149  double targetProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(clearingTick,
150  clearingTick + model.getCentralForecastingYear(), government, model);
151 
152  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, true, clearingTick,
153  fuelPriceMap, demandGrowthMap,
154  (previouslyBankedCertificates - targetProducerBanking) / model.getCentralForecastingYear());
155  } else {
156  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, true, clearingTick,
157  fuelPriceMap, demandGrowthMap, 0);
158  }
159 
160  }
161 
162  public CO2SecantSearch clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(
163  DecarbonizationModel model, boolean forecast, long clearingTick, Map<Substance, Double> fuelPriceMap,
164  Map<ElectricitySpotMarket, Double> demandGrowthMap, double co2CapAdjustment) {
165 
166  if (model == null)
167  model = template.findAll(DecarbonizationModel.class).iterator().next();
168 
169  if (fuelPriceMap == null && forecast)
170  fuelPriceMap = predictFuelPrices(model.getCentralForecastBacklookingYears(), clearingTick);
171 
172  if (demandGrowthMap == null && forecast)
173  demandGrowthMap = predictDemand(model.getCentralForecastBacklookingYears(), clearingTick);
174 
175  // find all operational power plants and store the ones operational to a
176  // list.
177 
178  logger.info("Clearing the CO2 and electricity spot markets using iteration for 2 countries ");
179 
180  // find all fuel prices
181 
182  // find all interconnectors
183  Interconnector interconnector = template.findAll(Interconnector.class).iterator().next();
184 
185  // find all segments
186  List<Segment> segments = Utils.asList(reps.segmentRepository.findAll());
187 
188  // find the EU government
189  Government government = template.findAll(Government.class).iterator().next();
190 
191  // find national minimum CO2 prices. Initial Map size is 2.
192  Map<ElectricitySpotMarket, Double> nationalMinCo2Prices = new HashMap<ElectricitySpotMarket, Double>(2);
193  Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
194  for (NationalGovernment nG : nationalGovernments) {
195  if (model.isCo2TradingImplemented()) {
196  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
197  .getMinNationalCo2PriceTrend().getValue(clearingTick));
198  } else {
199  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
200  }
201 
202  }
203 
204  CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
205  CO2SecantSearch co2SecantSearch = null;
206 
207  if (model.isCo2TradingImplemented()) {
208 
209  co2SecantSearch = new CO2SecantSearch();
210  co2SecantSearch.stable = false;
211  co2SecantSearch.twoPricesExistWithBelowAboveEmissions = false;
212  co2SecantSearch.co2Price = findLastKnownPriceOnMarket(co2Auction);
213  co2SecantSearch.tooHighEmissionsPair = null;
214  co2SecantSearch.tooLowEmissionsPair = null;
215 
216  // Change Iteration algorithm here, and a few lines below...
217 
218  ClearingPoint lastClearingPointOfCo2Market = reps.clearingPointRepositoryOld
219  .findClearingPointForMarketAndTime(co2Auction, getCurrentTick() - 1, false);
220  if (lastClearingPointOfCo2Market != null) {
221  co2SecantSearch.co2Emissions = lastClearingPointOfCo2Market.getVolume();
222  } else {
223  co2SecantSearch.co2Emissions = 0d;
224  }
225 
226  int breakOffIterator = 0;
227  while (!co2SecantSearch.stable) {
228 
229  if (breakOffIterator > 15) {
230  logger.warn("Iteration cancelled, last found CO2 Price is used.");
231  break;
232  }
233 
234  // Clear the electricity markets with the expected co2Price
235 
236  // updatePowerPlanDispatchPlansWithNewCO2Prices(co2SecantSearch.co2Price,
237  // nationalMinCo2Prices);
238  // submitOffersToElectricitySpotMarketRole.updateMarginalCostInclCO2AfterFuelMixChange(
239  // co2SecantSearch.co2Price, nationalMinCo2Prices, clearingTick,
240  // forecast, fuelPriceMap);
241  //
242  // if (model.isLongTermContractsImplemented())
243  // determineCommitmentOfPowerPlantsOnTheBasisOfLongTermContracts(segments,
244  // forecast);
245  //
246  // for (Segment segment : segments) {
247  // clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForOneSegment(interconnector.getCapacity(),
248  // segment, government, clearingTick, forecast,
249  // demandGrowthMap);
250  // }
251  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick, forecast,
252  demandGrowthMap, fuelPriceMap, co2SecantSearch.co2Price, nationalMinCo2Prices, segments,
253  interconnector, model);
254 
255  // Change Iteration algorithm here
256  // co2PriceStability =
257  // determineStabilityOfCO2andElectricityPricesAndAdjustIfNecessary(co2PriceStability,
258  // model, government);
259  co2SecantSearch = co2PriceSecantSearchUpdate(co2SecantSearch, model, government, forecast,
260  clearingTick, co2CapAdjustment);
261  breakOffIterator++;
262 
263  }
264 
265  reps.clearingPointRepositoryOld.createOrUpdateClearingPoint(co2Auction, co2SecantSearch.co2Price,
266  co2SecantSearch.co2Emissions, clearingTick, forecast);
267  } else {
268  if (model.isLongTermContractsImplemented())
269  determineCommitmentOfPowerPlantsOnTheBasisOfLongTermContracts(segments, forecast);
270  for (Segment segment : segments) {
271  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForOneSegment(
272  interconnector.getCapacity(clearingTick),
273  segment, government, clearingTick, forecast, demandGrowthMap);
274  }
275 
276  }
277 
278  return co2SecantSearch;
279 
280  }
281 
291  @Transactional
292  void clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForOneSegment(double interconnectorCapacity,
293  Segment segment, Government government, long clearingTick, boolean forecast,
294  Map<ElectricitySpotMarket, Double> demandGrowthMap) {
295 
296  GlobalSegmentClearingOutcome globalOutcome = new GlobalSegmentClearingOutcome();
297 
298  globalOutcome.loads = determineActualDemandForSpotMarkets(segment, demandGrowthMap);
299 
300  globalOutcome.globalLoad = determineTotalLoadFromLoadMap(globalOutcome.loads);
301 
302  // Keep track of supply per market. Start at 0.
303  for (ElectricitySpotMarket m : reps.marketRepository.findAllElectricitySpotMarkets()) {
304  globalOutcome.supplies.put(m, 0d);
305  }
306 
307  // empty list of plants that are supplying.
308  double marginalPlantMarginalCost = clearGlobalMarketWithNoCapacityConstraints(segment, globalOutcome, forecast,
309  clearingTick);
310 
311  // For each plant in the cost-ordered list
312 
313  // Determine the flow over the interconnector.
314  ElectricitySpotMarket firstMarket = reps.marketRepository.findAllElectricitySpotMarkets().iterator().next();
315  double loadInFirstMarket = globalOutcome.loads.get(firstMarket);
316  double supplyInFirstMarket = globalOutcome.supplies.get(firstMarket);
317 
318  // Interconnector flow defined as from market A --> market B = positive
319  double interconnectorFlow = supplyInFirstMarket - loadInFirstMarket;
320 
321  logger.info("Before market coupling interconnector flow: {}, available interconnector capacity {}",
322  interconnectorFlow, interconnectorCapacity);
323 
324  // if interconnector is not limiting or there is only one market, there
325  // is one price
326  if (reps.marketRepository.countAllElectricitySpotMarkets() < 2
327  || Math.abs(interconnectorFlow) + epsilon <= interconnectorCapacity) {
328  // Set the price to the bid of the marginal plant.
329  for (ElectricitySpotMarket market : reps.marketRepository.findAllElectricitySpotMarkets()) {
330  double supplyInThisMarket = globalOutcome.supplies.get(market);
331 
332  // globalOutcome.globalSupply += supplyInThisMarket;
333 
334  if (globalOutcome.globalLoad <= globalOutcome.globalSupply + epsilon) {
335  globalOutcome.globalPrice = marginalPlantMarginalCost;
336  } else {
337  globalOutcome.globalPrice = market.getValueOfLostLoad();
338  }
339 
340  double interconenctorFlowForCurrentMarket = market.equals(firstMarket) ? interconnectorFlow * (-1.0)
341  : interconnectorFlow;
342 
343  reps.clearingPointRepositoryOld.createOrUpdateSegmentClearingPoint(segment, market,
344  globalOutcome.globalPrice,
345  (supplyInThisMarket + interconenctorFlowForCurrentMarket) * segment.getLengthInHours(),
346  (interconenctorFlowForCurrentMarket * segment.getLengthInHours()), clearingTick, forecast);
347  logger.info("Stored a system-uniform price for market " + market + " / segment " + segment
348  + " -- supply " + supplyInThisMarket + " -- price: " + globalOutcome.globalPrice);
349  }
350 
351  } else {
352 
353  MarketSegmentClearingOutcome marketOutcomes = new MarketSegmentClearingOutcome();
354  for (ElectricitySpotMarket m : reps.marketRepository.findAllElectricitySpotMarkets()) {
355  marketOutcomes.supplies.put(m, 0d);
356  marketOutcomes.prices.put(m, m.getValueOfLostLoad());
357  }
358 
359  // else there are two prices
360  logger.info("There should be multiple prices, but first we should do market coupling.");
361 
362  boolean firstImporting = true;
363  if (interconnectorFlow > 0) {
364  firstImporting = false;
365  }
366 
367 
368  for (ElectricitySpotMarket market : reps.marketRepository.findAllElectricitySpotMarkets()) {
369  boolean first = market.equals(firstMarket);
370  // Update the load for this market. Which is market's true load
371  // +/- the full interconnector capacity, based on direction of
372  // the flow
373  if ((first && firstImporting) || (!first && !firstImporting)) {
374  marketOutcomes.loads.put(market, globalOutcome.loads.get(market) - interconnectorCapacity);
375  } else {
376  marketOutcomes.loads.put(market, globalOutcome.loads.get(market) + interconnectorCapacity);
377  }
378 
379  }
380 
381  // For each plant in the cost-ordered list
382 
383  clearTwoInterconnectedMarketsGivenAnInterconnectorAdjustedLoad(segment, marketOutcomes, clearingTick,
384  forecast);
385 
386  // updatePowerDispatchPlansAfterTwoCountryClearingIsComplete(segment);
387 
388  for (ElectricitySpotMarket market : reps.marketRepository.findAllElectricitySpotMarkets()) {
389  if (marketOutcomes.supplies.get(market) + epsilon < marketOutcomes.loads.get(market)) {
390  marketOutcomes.prices.put(market, market.getValueOfLostLoad());
391  }
392  }
393 
394  for (ElectricitySpotMarket market : reps.marketRepository.findAllElectricitySpotMarkets()) {
395  double interconenctorFlowForCurrentMarket = (market.equals(firstMarket) && firstImporting)
396  || (!market.equals(firstMarket) && !firstImporting) ? interconnectorCapacity
397  : interconnectorCapacity * (-1.0);
398  reps.clearingPointRepositoryOld.createOrUpdateSegmentClearingPoint(segment, market,
399  marketOutcomes.prices.get(market),
400  (marketOutcomes.supplies.get(market) + interconenctorFlowForCurrentMarket)
401  * segment.getLengthInHours(),
402  (interconenctorFlowForCurrentMarket * segment.getLengthInHours()), clearingTick, forecast);
403  logger.info("Stored a market specific price for market " + market + " / segment " + segment
404  + " -- supply " + marketOutcomes.supplies.get(market) + " -- demand: "
405  + marketOutcomes.loads.get(market) + " -- price: " + marketOutcomes.prices.get(market));
406  }
407 
408  @SuppressWarnings("unused")
409  int i = 0;
410  }
411  }
412 
413  void clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(Government government, long clearingTick,
414  boolean forecast, Map<ElectricitySpotMarket, Double> demandGrowthMap, Map<Substance, Double> fuelPriceMap,
415  double co2Price, Map<ElectricitySpotMarket, Double> nationalMinCo2Prices, List<Segment> segments,
416  Interconnector interconnector, DecarbonizationModel model) {
417 
418  submitOffersToElectricitySpotMarketRole.updateMarginalCostInclCO2AfterFuelMixChange(co2Price,
419  nationalMinCo2Prices, clearingTick, forecast, fuelPriceMap);
420 
421  if (model.isLongTermContractsImplemented())
422  determineCommitmentOfPowerPlantsOnTheBasisOfLongTermContracts(segments, forecast);
423 
424  for (Segment segment : segments) {
425  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForOneSegment(
426  interconnector.getCapacity(clearingTick),
427  segment, government, clearingTick, forecast, demandGrowthMap);
428  }
429  }
430 
431  void clearTwoInterconnectedMarketsGivenAnInterconnectorAdjustedLoad(Segment segment,
432  MarketSegmentClearingOutcome marketOutcomes, long clearingTick, boolean forecast) {
433 
434  for (PowerPlantDispatchPlan plan : reps.powerPlantDispatchPlanRepository
435  .findSortedPowerPlantDispatchPlansForSegmentForTime(segment, clearingTick, forecast)) {
436 
437  ElectricitySpotMarket myMarket = (ElectricitySpotMarket) plan.getBiddingMarket();
438 
439  // Make it produce as long as there is load.
440  double plantSupply = determineProductionOnSpotMarket(plan, marketOutcomes.supplies.get(myMarket),
441  marketOutcomes.loads.get(myMarket));
442  if (plantSupply > 0) {
443  // Plant is producing, store the information to
444  // determine price and so on.
445  marketOutcomes.supplies.put(myMarket, marketOutcomes.supplies.get(myMarket) + plantSupply);
446  marketOutcomes.prices.put(myMarket, plan.getPrice());
447  // logger.warn("Storing price: {} for plant {} in market " +
448  // myMarket, plantCost.getValue(), plant);
449  }
450  }
451 
452  }
453 
454  void clearCO2AndElectricitySpotMarketTwoCountryWithBanking(DecarbonizationModel model, boolean forecast,
455  long clearingTick, Map<Substance, Double> fuelPriceMap, Map<ElectricitySpotMarket, Double> demandGrowthMap) {
456 
457  double previouslyBankedCertificates = reps.decarbonizationAgentRepository
458  .determineTotallyBankedCO2Certificates();
459 
460  // Check we are not in timestep 0
461  if (clearingTick < 1) {
462  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, forecast, clearingTick,
463  fuelPriceMap, demandGrowthMap, 0);
464  return;
465  }
466  CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
467  // find all interconnectors
468  Interconnector interconnector = template.findAll(Interconnector.class).iterator().next();
469 
470  Government government = template.findAll(Government.class).iterator().next();
471 
472  // find all segments
473  List<Segment> segments = Utils.asList(reps.segmentRepository.findAll());
474 
475  ClearingPoint forecastedCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(
476  co2Auction, clearingTick + model.getCentralForecastingYear(), true);
477  ClearingPoint lastCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(co2Auction,
478  clearingTick - 1, false);
479 
480  double maximumEnergyProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(clearingTick,
481  clearingTick + model.getCentralForecastingYear(), government, model);
482 
483  CO2SecantSearch co2SecantSearch = clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(
484  model, forecast, clearingTick, fuelPriceMap, demandGrowthMap, 0);
485 
486  double fundamentalCO2Price = calculateFundamentalCO2PriceForEnergyProducers(clearingTick, model, co2Auction,
487  co2SecantSearch);
488  logger.warn("Fundanmental price: {}", fundamentalCO2Price);
489 
490  // find national minimum CO2 prices. Initial Map size is 2.
491  Map<ElectricitySpotMarket, Double> nationalMinCo2Prices = new HashMap<ElectricitySpotMarket, Double>(2);
492  Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
493  for (NationalGovernment nG : nationalGovernments) {
494  if (model.isCo2TradingImplemented()) {
495  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
496  .getMinNationalCo2PriceTrend().getValue(clearingTick));
497  } else {
498  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
499  }
500  }
501 
502  double adjustedFundamentalCO2Price = fundamentalCO2Price
503  * calculateCO2PriceReductionFactor(government, maximumEnergyProducerBanking,
504  previouslyBankedCertificates, 10, clearingTick);
505 
506  double previousAdjustedFundamentalCO2Price = adjustedFundamentalCO2Price;
507  double co2emissions;
508  double deltaBankedEmissionCertificates;
509 
510  // adjust fundamental CO2Price
511  int i = 0;
512  do {
513 
514  previousAdjustedFundamentalCO2Price = adjustedFundamentalCO2Price;
515 
516  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick, forecast,
517  demandGrowthMap, fuelPriceMap, adjustedFundamentalCO2Price, nationalMinCo2Prices, segments,
518  interconnector, model);
519 
520  co2emissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(forecast, clearingTick);
521 
522  deltaBankedEmissionCertificates = government.getCo2Cap(clearingTick) - co2emissions;
523  adjustedFundamentalCO2Price = fundamentalCO2Price
524  * calculateCO2PriceReductionFactor(government, maximumEnergyProducerBanking,
525  previouslyBankedCertificates, 10, clearingTick);
526 
527  i++;
528  adjustedFundamentalCO2Price = fundamentalCO2Price
529  * calculateCO2PriceReductionFactor(government, maximumEnergyProducerBanking,
530  previouslyBankedCertificates + deltaBankedEmissionCertificates, 10, clearingTick);
531 
532  logger.warn("CO2 Fundamental Price iteration " + i + ", Old: {}, New:{}",
533  previousAdjustedFundamentalCO2Price, adjustedFundamentalCO2Price);
534 
535  } while (Math.abs(adjustedFundamentalCO2Price - previousAdjustedFundamentalCO2Price) > 1);
536 
537  if (previouslyBankedCertificates + deltaBankedEmissionCertificates > 0) {
538 
539  reps.clearingPointRepositoryOld.createOrUpdateClearingPoint(co2Auction, adjustedFundamentalCO2Price,
540  co2emissions, clearingTick, forecast);
541 
542  for (EnergyProducer producer : reps.energyProducerRepository.findAll()) {
543  producer.setLastYearsCo2Allowances(producer.getCo2Allowances());
544  double futureEmissionShare = determineTotalEmissionsBasedOnPowerPlantDispatchPlanForEnergyProducer(
545  true, clearingTick + model.getCentralForecastingYear(), producer)
546  / forecastedCO2Clearing.getVolume();
547  double bankedEmissionsOfProducer = (deltaBankedEmissionCertificates + previouslyBankedCertificates)
548  * futureEmissionShare;
549  producer.setCo2Allowances(bankedEmissionsOfProducer);
550  }
551  } else {
552  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, forecast, clearingTick,
553  fuelPriceMap, demandGrowthMap, previouslyBankedCertificates);
554  for (EnergyProducer producer : reps.energyProducerRepository.findAll()) {
555  producer.setLastYearsCo2Allowances(producer.getCo2Allowances());
556  producer.setCo2Allowances(0);
557  }
558  }
559 
560  }
561 
562  public void clearIterativeCO2ElectricitySpotMarketAndFutureMarketTwoCountryForTimestepAndFuelPrices(
563  DecarbonizationModel model, long clearingTick) {
564 
565  if (model == null)
566  model = template.findAll(DecarbonizationModel.class).iterator().next();
567 
568  Map<Substance, Double> fuelPriceMap = new HashMap<Substance, Double>();
569  for (Substance substance : template.findAll(Substance.class)) {
570  fuelPriceMap.put(substance, findLastKnownPriceForSubstance(substance));
571  }
572 
573  Map<Substance, Double> futureFuelPriceMap = predictFuelPrices(model.getCentralForecastBacklookingYears(),
574  clearingTick + model.getCentralForecastingYear());
575 
576  Map<ElectricitySpotMarket, Double> futureDemandGrowthMap = predictDemand(
577  model.getCentralForecastBacklookingYears(), clearingTick + model.getCentralForecastingYear());
578 
579  // find national minimum CO2 prices. Initial Map size is 2.
580  Map<ElectricitySpotMarket, Double> futureNationalMinCo2Prices = new HashMap<ElectricitySpotMarket, Double>(2);
581  Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
582  for (NationalGovernment nG : nationalGovernments) {
583  if (model.isCo2TradingImplemented()) {
584  futureNationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG),
585  nG.getMinNationalCo2PriceTrend().getValue(clearingTick + model.getCentralForecastingYear()));
586  } else {
587  futureNationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG),
588  0d);
589  }
590 
591  }
592 
593  // find all operational power plants and store the ones operational to a
594  // list.
595 
596  logger.info("Clearing the CO2 and electricity spot markets using iteration for 2 countries ");
597 
598  // find all fuel prices
599 
600  // find all interconnectors
601  Interconnector interconnector = template.findAll(Interconnector.class).iterator().next();
602 
603  // find all segments
604  List<Segment> segments = Utils.asList(reps.segmentRepository.findAll());
605 
606  // find the EU government
607  Government government = template.findAll(Government.class).iterator().next();
608 
609  // find national minimum CO2 prices. Initial Map size is 2.
610  // find national minimum CO2 prices. Initial Map size is 2.
611  Map<ElectricitySpotMarket, Double> nationalMinCo2Prices = new HashMap<ElectricitySpotMarket, Double>(2);
612  nationalGovernments = template.findAll(NationalGovernment.class);
613  for (NationalGovernment nG : nationalGovernments) {
614  if (model.isCo2TradingImplemented()) {
615  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
616  .getMinNationalCo2PriceTrend().getValue(clearingTick));
617  } else {
618  nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
619  }
620 
621  }
622 
623  CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
624  CO2SecantSearch co2SecantSearch = null;
625 
626  double previouslyBankedCertificates = reps.decarbonizationAgentRepository
627  .determineTotallyBankedCO2Certificates();
628 
629  double targetEnergyProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(clearingTick,
630  clearingTick + model.getCentralForecastingYear(), government, model);
631  logger.warn("Hedging Target: {}, Relative Hedging: {}", targetEnergyProducerBanking, targetEnergyProducerBanking/government.getCo2Cap(getCurrentTick()));
632 
633  double deltaBankedEmissionCertificateToReachBankingTarget = (targetEnergyProducerBanking - previouslyBankedCertificates)
634  / model.getCentralCO2TargetReversionSpeedFactor();
635  ;
636  double deltaBankedEmissionCertificates;
637 
638  co2SecantSearch = new CO2SecantSearch();
639  co2SecantSearch.stable = false;
640  co2SecantSearch.twoPricesExistWithBelowAboveEmissions = false;
641  co2SecantSearch.bankingEffectiveMinimumPrice = calculateBankingEffectiveMinCO2Price(nationalMinCo2Prices,
642  futureNationalMinCo2Prices, model);
643  co2SecantSearch.co2Price = Math.max(findLastKnownPriceOnMarket(co2Auction),
644  co2SecantSearch.bankingEffectiveMinimumPrice);
645  co2SecantSearch.tooHighEmissionsPair = null;
646  co2SecantSearch.tooLowEmissionsPair = null;
647 
648  double futureCO2Price = 0;
649 
650  double currentEmissions = 0;
651  double futureEmissions = 0;
652  double co2CapAdjustment = 0;
653 
654  double averageCO2PriceOfLastTwoYears = reps.clearingPointRepository
655  .calculateAverageClearingPriceForMarketAndTimeRange(co2Auction, getCurrentTick() - 2,
656  getCurrentTick() - 1, false);
657 
658  determineFuelMixRole.determineFuelMixForecastForYearAndFuelPriceMap(clearingTick, futureFuelPriceMap,
659  futureNationalMinCo2Prices);
660 
661  submitOffersToElectricitySpotMarketRole.createOffersForElectricitySpotMarket(null,
662  clearingTick + model.getCentralForecastingYear(), true, futureFuelPriceMap);
663 
664  // Change Iteration algorithm here, and a few lines below...
665 
666  ClearingPoint lastClearingPointOfCo2Market = reps.clearingPointRepositoryOld.findClearingPointForMarketAndTime(
667  co2Auction, getCurrentTick() - 1, false);
668  if (lastClearingPointOfCo2Market != null) {
669  co2SecantSearch.co2Emissions = lastClearingPointOfCo2Market.getVolume();
670  } else {
671  co2SecantSearch.co2Emissions = 0d;
672  }
673 
674  boolean emergencyPriceTriggerActive = false;
675  int breakOffIterator = 0;
676  double emergencyAllowancesToBeReleased = 0;
677  while (breakOffIterator < 2 || !co2SecantSearch.stable) {
678 
679  if (breakOffIterator > 15) {
680  logger.warn("Iteration cancelled, last found CO2 Price is used.");
681  break;
682  }
683 
684  futureCO2Price = co2SecantSearch.co2Price
685  * Math.pow(1 + model.getCentralPrivateDiscountingRate(), model.getCentralForecastingYear());
686 
687  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government,
688  clearingTick + model.getCentralForecastingYear(), true, futureDemandGrowthMap, futureFuelPriceMap,
689  futureCO2Price, futureNationalMinCo2Prices, segments, interconnector, model);
690  futureEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(true,
691  clearingTick + model.getCentralForecastingYear());
692 
693  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick, false, null,
694  fuelPriceMap, co2SecantSearch.co2Price, nationalMinCo2Prices, segments, interconnector, model);
695  currentEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(false, clearingTick);
696 
697  // for (Substance substance : template.findAll(Substance.class)) {
698  // double sum =
699  // reps.powerPlantRepository.calculateSubstanceUsage(substance);
700  // logger.warn(substance.getName() + ": {}", sum);
701  // }
702 
703  deltaBankedEmissionCertificates = government.getCo2Cap(clearingTick) - currentEmissions;
704 
705  co2SecantSearch = co2PriceSecantSearchUpdateWithCO2Banking(co2SecantSearch, model, government,
706  clearingTick, -deltaBankedEmissionCertificateToReachBankingTarget, currentEmissions,
707  futureEmissions, previouslyBankedCertificates, averageCO2PriceOfLastTwoYears);
708 
709  // logger.warn("Iteration: " + breakOffIterator + ": " +
710  // co2SecantSearch.toString() + ", Future: "
711  // + futureCO2Price);
712 
713  if (!co2SecantSearch.stable) {
714  targetEnergyProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(currentEmissions,
715  futureEmissions, model);
716  // logger.warn("Hedging Target: {}, Relative Hedging: {}",
717  // targetEnergyProducerBanking,
718  // targetEnergyProducerBanking/government.getCo2Cap(getCurrentTick()));
719 
720  deltaBankedEmissionCertificateToReachBankingTarget = (targetEnergyProducerBanking - previouslyBankedCertificates)
721  / model.getCentralCO2TargetReversionSpeedFactor();
722  }
723 
724  if (co2SecantSearch.stable || breakOffIterator >= 15) {
725  logger.warn("Average price last 2 years: {}, Clearing price {}", averageCO2PriceOfLastTwoYears,
726  co2SecantSearch.co2Price);
727  logger.warn("StabilityReserveActive: "
728  + model.isStabilityReserveIsActive()
729  + ", InOperation: "
730  + (model.getStabilityReserveFirstYearOfOperation() <= clearingTick
731  + model.getCentralForecastingYear()) + ", priceEmergencyTriggerNotActive: "
732  + !emergencyPriceTriggerActive + ", 3x Price: "
733  + (co2SecantSearch.co2Price > 3 * averageCO2PriceOfLastTwoYears));
734  if ((model.isStabilityReserveIsActive()
735  && (model.getStabilityReserveFirstYearOfOperation() <= clearingTick) && !emergencyPriceTriggerActive)
736  && (co2SecantSearch.co2Price > 3 * averageCO2PriceOfLastTwoYears)
737  && government.getStabilityReserve() > 0) {
738  breakOffIterator = 0;
739  emergencyPriceTriggerActive = true;
740  emergencyAllowancesToBeReleased = Math.min(government.getStabilityReserve(), government
741  .getStabilityReserveReleaseQuantityTrend().getValue(clearingTick));
742  logger.warn(
743  "Stability Reserve releasing " + emergencyAllowancesToBeReleased
744  + " credits since price would be {}, which has 3x time higher than {}",
745  co2SecantSearch.co2Price, averageCO2PriceOfLastTwoYears);
746  co2SecantSearch.stable = false;
747  co2SecantSearch.twoPricesExistWithBelowAboveEmissions = false;
748  co2SecantSearch.tooHighEmissionsPair = null;
749  co2SecantSearch.tooLowEmissionsPair = null;
750  government.getCo2CapTrend().setValue(getCurrentTick(),
751  government.getCo2CapTrend().getValue(getCurrentTick()) + emergencyAllowancesToBeReleased);
752  government.setStabilityReserve(government.getStabilityReserve() - emergencyAllowancesToBeReleased);
753  }
754  }
755  breakOffIterator++;
756 
757  }
758 
759  if (model.getCentralCO2BackSmoothingFactor() != 0) {
760 
761  Iterable<ClearingPoint> clearingPoints = reps.clearingPointRepository
762  .findAllClearingPointsForMarketAndTimeRange(co2Auction,
763  clearingTick - model.getCentralForecastBacklookingYears(), clearingTick, false);
764 
765  double averagePastCO2Price = 0;
766  int i = 0;
767  for (ClearingPoint cp : clearingPoints) {
768  averagePastCO2Price = (cp.getPrice()
769  / Math.pow(1 + model.getCentralPrivateDiscountingRate(), clearingTick - cp.getTime()) + i
770  * averagePastCO2Price)
771  / (i + 1);
772  i++;
773  }
774 
775  double oldCO2Price = co2SecantSearch.co2Price;
776  co2SecantSearch.co2Price = (1 - model.getCentralCO2BackSmoothingFactor()) * co2SecantSearch.co2Price
777  + model.getCentralCO2BackSmoothingFactor() * averagePastCO2Price;
778  if (oldCO2Price != co2SecantSearch.bankingEffectiveMinimumPrice
779  && co2SecantSearch.co2Price > co2SecantSearch.bankingEffectiveMinimumPrice) {
780  futureCO2Price = co2SecantSearch.co2Price
781  * Math.pow(1 + model.getCentralPrivateDiscountingRate(), model.getCentralForecastingYear());
782  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government,
783  clearingTick + model.getCentralForecastingYear(), true, futureDemandGrowthMap,
784  futureFuelPriceMap, futureCO2Price, futureNationalMinCo2Prices, segments, interconnector, model);
785  futureEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(true,
786  clearingTick + model.getCentralForecastingYear());
787 
788  clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick, false,
789  null, fuelPriceMap, co2SecantSearch.co2Price, nationalMinCo2Prices, segments, interconnector,
790  model);
791  currentEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(false, clearingTick);
792  } else {
793  co2SecantSearch.co2Price = oldCO2Price;
794  }
795  }
796 
797  reps.clearingPointRepositoryOld.createOrUpdateCO2MarketClearingPoint(co2Auction, co2SecantSearch.co2Price,
798  currentEmissions, emergencyPriceTriggerActive, emergencyAllowancesToBeReleased, clearingTick, false);
799  reps.clearingPointRepositoryOld.createOrUpdateCO2MarketClearingPoint(co2Auction,
800  Math.max(futureCO2Price, Collections.min(futureNationalMinCo2Prices.values())), futureEmissions, false,
801  0,
802  clearingTick + model.getCentralForecastingYear(), true);
803 
804  if (co2SecantSearch.co2Price > co2SecantSearch.bankingEffectiveMinimumPrice)
805  deltaBankedEmissionCertificates = government.getCo2Cap(clearingTick) - currentEmissions;
806  else {
807  deltaBankedEmissionCertificates = Math.min(deltaBankedEmissionCertificateToReachBankingTarget,
808  government.getCo2Cap(clearingTick) - currentEmissions);
809  }
810 
811  if (previouslyBankedCertificates + deltaBankedEmissionCertificates > 0) {
812  for (EnergyProducer producer : reps.energyProducerRepository.findAll()) {
813  producer.setLastYearsCo2Allowances(producer.getCo2Allowances());
814  double emissionShare = determineTotalEmissionsBasedOnPowerPlantDispatchPlanForEnergyProducer(false,
815  clearingTick, producer) / currentEmissions;
816  double bankedEmissionsOfProducer = (deltaBankedEmissionCertificates + previouslyBankedCertificates)
817  * emissionShare;
818  producer.setCo2Allowances(bankedEmissionsOfProducer);
819  }
820  } else {
821  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, false, clearingTick,
822  fuelPriceMap, null, previouslyBankedCertificates);
823  clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, true, clearingTick
824  + model.getCentralForecastingYear(), futureFuelPriceMap, futureDemandGrowthMap, 0);
825  for (EnergyProducer producer : reps.energyProducerRepository.findAll()) {
826  producer.setLastYearsCo2Allowances(producer.getCo2Allowances());
827  producer.setCo2Allowances(0);
828  }
829 
830  }
831  }
832 
833  double[] interpolateLinearDiscountedPricesBetweenClearingPoints(ClearingPoint cp1, ClearingPoint cp2,
834  double discountRate) {
835  int length = (int) Math.abs(cp1.getTime() - cp2.getTime());
836  ClearingPoint earlierCp = cp1.getTime() <= cp2.getTime() ? cp1 : cp2;
837  ClearingPoint laterCp = cp1.getTime() <= cp2.getTime() ? cp2 : cp1;
838  double[] priceInterpolation = new double[length];
839  for (int i = 0; i < length; i++) {
840  priceInterpolation[i] = earlierCp.getPrice() + ((double) i + 1) / (length)
841  * (laterCp.getPrice() - earlierCp.getPrice()) / Math.pow((1 + discountRate), i);
842  }
843 
844  return priceInterpolation;
845  }
846 
847  double[] interpolateLinearVolumesBetweenClearingPoints(ClearingPoint cp1, ClearingPoint cp2) {
848  int length = (int) Math.abs(cp1.getTime() - cp2.getTime());
849  ClearingPoint earlierCp = cp1.getTime() <= cp2.getTime() ? cp1 : cp2;
850  ClearingPoint laterCp = cp1.getTime() <= cp2.getTime() ? cp2 : cp1;
851  double[] volumeInterpolation = new double[length];
852  for (int i = 0; i < length; i++) {
853  volumeInterpolation[i] = earlierCp.getVolume() + ((double) i + 1) / (length)
854  * (laterCp.getVolume() - earlierCp.getVolume());
855  }
856 
857  return volumeInterpolation;
858  }
859 
860  double calculateTargetCO2EmissionBankingOfEnergyProducers(long timeFrom, long timeTo, Government government, DecarbonizationModel model) {
861  int length = (int) (timeTo - timeFrom);
862  double[] volumeInterpolation = new double[length];
863  for (int i = 0; i < length; i++) {
864  volumeInterpolation[i] = government.getCo2Cap(timeFrom) + ((double) i + 1) / (length)
865  * (government.getCo2Cap(timeTo) - government.getCo2Cap(timeFrom));
866  }
867  // double targetBankedEmissions = 0.8 * volumeInterpolation[0] + 0.5 *
868  // volumeInterpolation[1] + 0.2
869  // * volumeInterpolation[2];
870  double targetBankedEmissions = model.getStabilityReserveBankingFirstYear() * volumeInterpolation[0]
871  + model.getStabilityReserveBankingSecondYear() * volumeInterpolation[1]
872  + model.getStabilityReserveBankingThirdYear()
873  * volumeInterpolation[2];
874 
875  return targetBankedEmissions;
876  }
877 
878  double calculateTargetCO2EmissionBankingOfEnergyProducers(double currentEmissions, double futureEmissions,
879  DecarbonizationModel model) {
880  int length = (int) model.getCentralForecastingYear();
881  double[] volumeInterpolation = new double[length];
882  for (int i = 0; i < length; i++) {
883  volumeInterpolation[i] = currentEmissions + ((double) i + 1) / (length)
884  * (futureEmissions - currentEmissions);
885  }
886  // double targetBankedEmissions = 0.8 * volumeInterpolation[0] + 0.5 * volumeInterpolation[1] + 0.2
887  // * volumeInterpolation[2];
888  double targetBankedEmissions = model.getStabilityReserveBankingFirstYear() * volumeInterpolation[0]
889  + model.getStabilityReserveBankingSecondYear() * volumeInterpolation[1]
890  + model.getStabilityReserveBankingThirdYear()
891  * volumeInterpolation[2];
892 
893  return targetBankedEmissions;
894  }
895 
896  double calculateFundamentalCO2PriceForEnergyProducers(long clearingTick, DecarbonizationModel model,
897  CO2Auction co2Auction, CO2SecantSearch co2SecantSearch) {
898 
899  ClearingPoint forecastedCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(
900  co2Auction, clearingTick + model.getCentralForecastingYear(), true);
901  ClearingPoint lastCO2Clearing;
902  if (co2SecantSearch == null)
903  lastCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(co2Auction,
904  clearingTick - 1, false);
905  else {
906  lastCO2Clearing = new ClearingPoint();
907  lastCO2Clearing.setPrice(co2SecantSearch.co2Price);
908  lastCO2Clearing.setVolume(co2SecantSearch.co2Emissions);
909  lastCO2Clearing.setTime(clearingTick);
910  }
911  double[] priceInterpolation = interpolateLinearDiscountedPricesBetweenClearingPoints(lastCO2Clearing,
912  forecastedCO2Clearing, model.getCentralPrivateDiscountingRate());
913  logger.warn("Price interpolation: {}", priceInterpolation);
914  double[] volumeInterpolation = interpolateLinearVolumesBetweenClearingPoints(lastCO2Clearing,
915  forecastedCO2Clearing);
916  logger.warn("Volume interpolation: {}", volumeInterpolation);
917  double totalEmissions = 0;
918  double fundamentalPrice = 0;
919  for (int i = 0; i < volumeInterpolation.length; i++) {
920  fundamentalPrice += priceInterpolation[i] * volumeInterpolation[i];
921  totalEmissions += volumeInterpolation[i];
922  }
923  fundamentalPrice = fundamentalPrice / totalEmissions;
924  return fundamentalPrice;
925  }
926 
927  double calculateCO2PriceReductionFactor(Government government, double maximumEnergyProducerBanking,
928  double bankedCertificates, long yearsForwardLooking, long clearingTick) {
929  double allowedEmissionsInFuture = 0d;
930  for (long i = clearingTick; i < (clearingTick + yearsForwardLooking); i++) {
931  allowedEmissionsInFuture += government.getCo2Cap(i);
932  }
933  double co2PriceReductionFactor = 1 - ((bankedCertificates - maximumEnergyProducerBanking) / allowedEmissionsInFuture);
934  return co2PriceReductionFactor;
935  }
936 
937  double calculateBankingEffectiveMinCO2Price(Map<ElectricitySpotMarket, Double> nationalMinCo2Prices,
938  Map<ElectricitySpotMarket, Double> futureNationalMinCo2Prices, DecarbonizationModel model) {
939  double currentMinimumPrice = Collections.min(nationalMinCo2Prices.values());
940  // double futureMinimumPrice =
941  // Collections.min(futureNationalMinCo2Prices.values());
942  // double bankingEffectiveCO2Price = Math.max(
943  // currentMinimumPrice,
944  // futureMinimumPrice
945  // / Math.pow(1 + model.getCentralPrivateDiscountingRate(),
946  // model.getCentralForecastingYear()));
947  return currentMinimumPrice;
948  }
949 
950  CO2SecantSearch co2PriceSecantSearchUpdateWithCO2Banking(CO2SecantSearch co2SecantSearch,
951  DecarbonizationModel model, Government government, long clearingTick, double co2CapAdjustment,
952  double currentEmissions, double futureEmissions, double previouslyBankedCertificates,
953  double averageCO2PriceOfLastTwoYears) {
954  co2SecantSearch.stable = false;
955  double capDeviationCriterion = model.getCapDeviationCriterion();
956  double currentCap = government.getCo2Cap(clearingTick);
957  // logger.warn("Current cap: {}", currentCap);
958  double futureCap = government.getCo2Cap(clearingTick + model.getCentralForecastingYear());
959  // logger.warn("Future cap: {}", futureCap);
960  // logger.warn("Is MSR active in future ? {}.",
961  // (model.isStabilityReserveIsActive() && (model.getStabilityReserveFirstYearOfOperation() <= clearingTick
962  // + model.getCentralForecastingYear())));
963  // logger.warn("First year MSR is active: {}", model.getStabilityReserveFirstYearOfOperation());
964  double expectedBankedPermits = calculateExpectedBankedCertificates(currentEmissions, futureEmissions,
965  currentCap, futureCap, previouslyBankedCertificates, model.getCentralForecastingYear());
966  double effectiveCapInFuture = (model.isStabilityReserveIsActive() && (model
967  .getStabilityReserveFirstYearOfOperation() <= clearingTick + model.getCentralForecastingYear())) ? futureCap
968  - marketStabilityReserveRole.calculateInflowToMarketReserveForTimeStep(
969  clearingTick + model.getCentralForecastingYear(),
970  expectedBankedPermits,
971  government)
972  : futureCap;
973  // logger.warn("effective future cap: {}", effectiveCapInFuture);
974  effectiveCapInFuture -= (government.isActivelyAdjustingTheCO2Cap() & getCurrentTick() > 0) ? renewableAdaptiveCO2CapRole
975  .calculatedExpectedCapReductionForTimeStep(government, clearingTick,
976  clearingTick + model.getCentralForecastingYear(), currentEmissions, futureEmissions,
977  model.getCentralForecastingYear()) : 0;
978  double co2Cap = currentCap + effectiveCapInFuture + co2CapAdjustment;
979  co2SecantSearch.co2Emissions = currentEmissions + futureEmissions;
980 
981  double deviation = (co2SecantSearch.co2Emissions - co2Cap) / co2Cap;
982 
983  if (co2SecantSearch.co2Price == co2SecantSearch.bankingEffectiveMinimumPrice
984  && co2SecantSearch.co2Emissions < co2Cap) {
985  co2SecantSearch.stable = true;
986  return co2SecantSearch;
987  }
988 
989  // Check if current price leads to emissions close to the cap.
990  if (Math.abs(deviation) < capDeviationCriterion
991  && co2SecantSearch.co2Price > co2SecantSearch.bankingEffectiveMinimumPrice) {
992  // logger.warn("Deviation is less than capDeviationCriterion");
993  co2SecantSearch.stable = true;
994  return co2SecantSearch;
995  }
996 
997  // Check and update the twoPricesExistWithBelowAboveEmissions
998 
999  if (co2SecantSearch.tooHighEmissionsPair != null && co2SecantSearch.tooLowEmissionsPair != null) {
1000  co2SecantSearch.twoPricesExistWithBelowAboveEmissions = true;
1001  } else if (co2SecantSearch.co2Price == government.getMinCo2Price(clearingTick)
1002  && co2SecantSearch.co2Emissions < co2Cap) {
1003  // logger.warn("Deviation CO2 price has reached minimum");
1004  // check if stable enough --> 2. Cap is met with a co2Price
1005  // equal to the minimum co2 price
1006  co2SecantSearch.stable = true;
1007  return co2SecantSearch;
1008  } else if (co2SecantSearch.co2Price >= government.getCo2Penalty(clearingTick)
1009  && co2SecantSearch.co2Emissions >= co2Cap) {
1010  // Only if above the cap...
1011  // logger.warn("CO2 price ceiling reached {}",
1012  // co2SecantSearch.co2Price);
1013  co2SecantSearch.co2Price = government.getCo2Penalty(clearingTick);
1014  co2SecantSearch.stable = true;
1015  return co2SecantSearch;
1016  }
1017 
1018  // Check whether we know two pairs, one with EmissionsAboveCap, one with
1019  // EmissionsBelowCap
1020  // in case of yes: calculate new CO2 price via secant calculation. In
1021  // case of no: Take last known
1022  // price above or below, or halve/double the price.
1023  if (co2SecantSearch.twoPricesExistWithBelowAboveEmissions) {
1024 
1025  // Update the emission pairs
1026  if (deviation > 0) {
1027  co2SecantSearch.tooHighEmissionsPair.price = co2SecantSearch.co2Price;
1028  co2SecantSearch.tooHighEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1029  } else {
1030  co2SecantSearch.tooLowEmissionsPair.price = co2SecantSearch.co2Price;
1031  co2SecantSearch.tooLowEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1032  }
1033 
1034  double p2 = co2SecantSearch.tooHighEmissionsPair.price;
1035  double p1 = co2SecantSearch.tooLowEmissionsPair.price;
1036  double e2 = co2SecantSearch.tooHighEmissionsPair.emission;
1037  double e1 = co2SecantSearch.tooLowEmissionsPair.emission;
1038 
1039  // Interrupts long iterations by making a binary search step.
1040  if (co2SecantSearch.iteration < 5) {
1041  co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
1042  co2SecantSearch.iteration++;
1043  // logger.warn("New CO2 Secant price {}",
1044  // co2SecantSearch.co2Price);
1045  } else {
1046  co2SecantSearch.co2Price = (p1 + p2) / 2;
1047  co2SecantSearch.iteration = 0;
1048  // logger.warn("New CO2 Binary price {}",
1049  // co2SecantSearch.co2Price);
1050  }
1051 
1052  } else {
1053 
1054  if (deviation > 0) {
1055  if (co2SecantSearch.tooHighEmissionsPair == null)
1056  co2SecantSearch.tooHighEmissionsPair = new PriceEmissionPair();
1057 
1058  co2SecantSearch.tooHighEmissionsPair.price = co2SecantSearch.co2Price;
1059  co2SecantSearch.tooHighEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1060 
1061  if (co2SecantSearch.tooLowEmissionsPair == null) {
1062  co2SecantSearch.co2Price = (co2SecantSearch.co2Price != 0d) ? ((co2SecantSearch.co2Price * 2 < government
1063  .getCo2Penalty(clearingTick)) ? (co2SecantSearch.co2Price * 2) : government
1064  .getCo2Penalty(clearingTick)) : 5d;
1065  // logger.warn("New doubled CO2 search price {}",
1066  // co2SecantSearch.co2Price);
1067  } else {
1068  double p2 = co2SecantSearch.tooHighEmissionsPair.price;
1069  double p1 = co2SecantSearch.tooLowEmissionsPair.price;
1070  double e2 = co2SecantSearch.tooHighEmissionsPair.emission;
1071  double e1 = co2SecantSearch.tooLowEmissionsPair.emission;
1072 
1073  co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
1074  co2SecantSearch.iteration++;
1075  // logger.warn("New CO2 Secant price {}",
1076  // co2SecantSearch.co2Price);
1077  }
1078 
1079  } else {
1080 
1081  if (co2SecantSearch.tooLowEmissionsPair == null)
1082  co2SecantSearch.tooLowEmissionsPair = new PriceEmissionPair();
1083 
1084  co2SecantSearch.tooLowEmissionsPair.price = co2SecantSearch.co2Price;
1085  co2SecantSearch.tooLowEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1086 
1087  if (co2SecantSearch.tooHighEmissionsPair == null) {
1088  co2SecantSearch.co2Price = Math.max((co2SecantSearch.co2Price / 2),
1089  co2SecantSearch.bankingEffectiveMinimumPrice);
1090  // logger.warn("New halved CO2 search price {}",
1091  // co2SecantSearch.co2Price);
1092  } else {
1093  double p2 = co2SecantSearch.tooHighEmissionsPair.price;
1094  double p1 = co2SecantSearch.tooLowEmissionsPair.price;
1095  double e2 = co2SecantSearch.tooHighEmissionsPair.emission;
1096  double e1 = co2SecantSearch.tooLowEmissionsPair.emission;
1097 
1098  co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
1099  // logger.warn("New CO2 Secant price {}",
1100  // co2SecantSearch.co2Price);
1101  co2SecantSearch.iteration++;
1102 
1103  }
1104 
1105  if (co2SecantSearch.co2Price < 0.5
1106  || co2SecantSearch.co2Price - government.getMinCo2Price(clearingTick) < 0.5) {
1107  co2SecantSearch.stable = true;
1108  }
1109 
1110  }
1111 
1112  }
1113 
1114  return co2SecantSearch;
1115 
1116  }
1117 
1118  double calculateExpectedBankedCertificates(double currentEmissions, double futureEmissions, double currentCap,
1119  double futureCap, double currentlyBankedEmissions, long centralForecastingYear) {
1120  double expectedBankedCertificates = currentlyBankedEmissions + currentCap - currentEmissions + futureCap
1121  - futureEmissions;
1122  expectedBankedCertificates = 1 / 3.0
1123  * expectedBankedCertificates
1124  + 2 / 3.0 * currentlyBankedEmissions;
1125  return expectedBankedCertificates;
1126 
1127  }
1128 
1129  @Override
1130  public Reps getReps() {
1131  return reps;
1132  }
1133 
1134 }
Iterable< ElectricitySpotMarket > findAllElectricitySpotMarkets()