EMlab-generation Documentation  1.0
Documentation of the EMLab-Generation model.
InvestInPowerGenerationTechnologiesStandard.java
1 /*******************************************************************************
2  * Copyright 2014 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.investment;
17 
18 import java.util.HashMap;
19 import java.util.LinkedList;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Set;
23 import java.util.TreeMap;
24 
25 import org.apache.commons.math.stat.regression.SimpleRegression;
26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.beans.factory.annotation.Configurable;
28 import org.springframework.data.annotation.Transient;
29 import org.springframework.data.neo4j.annotation.NodeEntity;
30 import org.springframework.data.neo4j.aspects.core.NodeBacked;
31 import org.springframework.data.neo4j.support.Neo4jTemplate;
32 import org.springframework.transaction.annotation.Transactional;
33 
34 import agentspring.role.Role;
62 
69 @Configurable
70 @NodeEntity
71 public class InvestInPowerGenerationTechnologiesStandard<T extends EnergyProducer> extends GenericInvestmentRole<T>
72 implements
73 Role<T>,
74 NodeBacked {
75 
76  @Transient
77  @Autowired
78  Reps reps;
79 
80  @Transient
81  @Autowired
82  Neo4jTemplate template;
83 
84  @Transient
85  @Autowired
86  StrategicReserveOperatorRepository strategicReserveOperatorRepository;
87 
88  // market expectations
89  @Transient
90  Map<ElectricitySpotMarket, MarketInformation> marketInfoMap = new HashMap<ElectricitySpotMarket, MarketInformation>();
91 
92  @Override
93  public void act(T agent) {
94 
95  long futureTimePoint = getCurrentTick() + agent.getInvestmentFutureTimeHorizon();
96  // logger.warn(agent + " is looking at timepoint " + futureTimePoint);
97 
98  // ==== Expectations ===
99 
100  Map<Substance, Double> expectedFuelPrices = predictFuelPrices(agent, futureTimePoint);
101 
102  // CO2
103  Map<ElectricitySpotMarket, Double> expectedCO2Price = determineExpectedCO2PriceInclTaxAndFundamentalForecast(
104  futureTimePoint,
105  agent.getNumberOfYearsBacklookingForForecasting(), 0, getCurrentTick());
106 
107  // logger.warn("{} expects CO2 prices {}", agent.getName(),
108  // expectedCO2Price);
109 
110  Map<ElectricitySpotMarket, Double> expectedCO2PriceOld = determineExpectedCO2PriceInclTax(futureTimePoint,
111  agent.getNumberOfYearsBacklookingForForecasting(), getCurrentTick());
112  // logger.warn("{} used to expect CO2 prices {}", agent.getName(),
113  // expectedCO2PriceOld);
114 
115  // logger.warn(expectedCO2Price.toString());
116 
117  //Demand
118  Map<ElectricitySpotMarket, Double> expectedDemand = new HashMap<ElectricitySpotMarket, Double>();
119  for(ElectricitySpotMarket elm : reps.template.findAll(ElectricitySpotMarket.class)){
121  for(long time = getCurrentTick(); time>getCurrentTick()-agent.getNumberOfYearsBacklookingForForecasting() && time>=0; time=time-1){
122  gtr.addData(time, elm.getDemandGrowthTrend().getValue(time));
123  }
124  expectedDemand.put(elm, gtr.predict(futureTimePoint));
125  }
126 
127 
128 
129  // Investment decision
130  // for (ElectricitySpotMarket market :
131  // reps.genericRepository.findAllAtRandom(ElectricitySpotMarket.class))
132  // {
133  ElectricitySpotMarket market = agent.getInvestorMarket();
134  MarketInformation marketInformation = new MarketInformation(market, expectedDemand, expectedFuelPrices, expectedCO2Price.get(market)
135  .doubleValue(), futureTimePoint);
136  /*
137  * if (marketInfoMap.containsKey(market) && marketInfoMap.get(market).time == futureTimePoint) { marketInformation = marketInfoMap.get(market); } else { marketInformation = new
138  * MarketInformation(market, expectedFuelPrices, expectedCO2Price, futureTimePoint); marketInfoMap.put(market, marketInformation); }
139  */
140 
141  // logger.warn(agent + " is expecting a CO2 price of " +
142  // expectedCO2Price.get(market) + " Euro/MWh at timepoint "
143  // + futureTimePoint + " in Market " + market);
144 
145  // logger.warn("Agent {} found the expected prices to be {}", agent,
146  // marketInformation.expectedElectricityPricesPerSegment);
147 
148  // logger.warn("Agent {} found that the installed capacity in the market {} in future to be "
149  // + marketInformation.capacitySum +
150  // "and expectde maximum demand to be "
151  // + marketInformation.maxExpectedLoad, agent, market);
152 
153  double highestValue = Double.MIN_VALUE;
154  PowerGeneratingTechnology bestTechnology = null;
155  PowerGridNode bestNode = null;
156 
157  for (PowerGeneratingTechnology technology : reps.genericRepository.findAll(PowerGeneratingTechnology.class)) {
158 
159  DecarbonizationModel model = reps.genericRepository.findAll(DecarbonizationModel.class).iterator().next();
160 
161  if (technology.isIntermittent() && model.isNoPrivateIntermittentRESInvestment())
162  continue;
163 
164  Iterable<PowerGridNode> possibleInstallationNodes;
165 
166  /*
167  * For dispatchable technologies just choose a random node. For
168  * intermittent evaluate all possibilities.
169  */
170  if(technology.isIntermittent())
171  possibleInstallationNodes = reps.powerGridNodeRepository.findAllPowerGridNodesByZone(market.getZone());
172  else{
173  possibleInstallationNodes = new LinkedList<PowerGridNode>();
174  ((LinkedList<PowerGridNode>) possibleInstallationNodes).add(reps.powerGridNodeRepository
175  .findAllPowerGridNodesByZone(market.getZone()).iterator().next());
176  }
177 
178  // logger.warn("Calculating for " + technology.getName() +
179  // ", for Nodes: "
180  // + possibleInstallationNodes.toString());
181 
182  for (PowerGridNode node : possibleInstallationNodes) {
183 
184  PowerPlant plant = new PowerPlant();
185  plant.specifyNotPersist(getCurrentTick(), agent, node, technology);
186  // if too much capacity of this technology in the pipeline (not
187  // limited to the 5 years)
188  double expectedInstalledCapacityOfTechnology = reps.powerPlantRepository
189  .calculateCapacityOfExpectedOperationalPowerPlantsInMarketAndTechnology(market, technology, futureTimePoint);
190  PowerGeneratingTechnologyTarget technologyTarget = reps.powerGenerationTechnologyTargetRepository
191  .findOneByTechnologyAndMarket(technology, market);
192  if (technologyTarget != null) {
193  double technologyTargetCapacity = technologyTarget.getTrend().getValue(futureTimePoint);
194  expectedInstalledCapacityOfTechnology = (technologyTargetCapacity > expectedInstalledCapacityOfTechnology) ? technologyTargetCapacity
195  : expectedInstalledCapacityOfTechnology;
196  }
197  double pgtNodeLimit = Double.MAX_VALUE;
198  PowerGeneratingTechnologyNodeLimit pgtLimit = reps.powerGeneratingTechnologyNodeLimitRepository
199  .findOneByTechnologyAndNode(technology, plant.getLocation());
200  if (pgtLimit != null) {
201  pgtNodeLimit = pgtLimit.getUpperCapacityLimit(futureTimePoint);
202  }
203  double expectedInstalledCapacityOfTechnologyInNode = reps.powerPlantRepository
204  .calculateCapacityOfExpectedOperationalPowerPlantsByNodeAndTechnology(plant.getLocation(),
205  technology, futureTimePoint);
206  double expectedOwnedTotalCapacityInMarket = reps.powerPlantRepository
207  .calculateCapacityOfExpectedOperationalPowerPlantsInMarketByOwner(market, futureTimePoint, agent);
208  double expectedOwnedCapacityInMarketOfThisTechnology = reps.powerPlantRepository
209  .calculateCapacityOfExpectedOperationalPowerPlantsInMarketByOwnerAndTechnology(market, technology, futureTimePoint,
210  agent);
211  double capacityOfTechnologyInPipeline = reps.powerPlantRepository.calculateCapacityOfPowerPlantsByTechnologyInPipeline(
212  technology, getCurrentTick());
213  double operationalCapacityOfTechnology = reps.powerPlantRepository.calculateCapacityOfOperationalPowerPlantsByTechnology(
214  technology, getCurrentTick());
215  double capacityInPipelineInMarket = reps.powerPlantRepository
216  .calculateCapacityOfPowerPlantsByMarketInPipeline(market, getCurrentTick());
217 
218  if ((expectedInstalledCapacityOfTechnology + plant.getActualNominalCapacity())
219  / (marketInformation.maxExpectedLoad + plant.getActualNominalCapacity()) > technology
220  .getMaximumInstalledCapacityFractionInCountry()) {
221  // logger.warn(agent +
222  // " will not invest in {} technology because there's too much of this type in the market",
223  // technology);
224  } else if ((expectedInstalledCapacityOfTechnologyInNode + plant.getActualNominalCapacity()) > pgtNodeLimit) {
225 
226  } else if (expectedOwnedCapacityInMarketOfThisTechnology > expectedOwnedTotalCapacityInMarket
227  * technology.getMaximumInstalledCapacityFractionPerAgent()) {
228  // logger.warn(agent +
229  // " will not invest in {} technology because there's too much capacity planned by him",
230  // technology);
231  } else if (capacityInPipelineInMarket > 0.2 * marketInformation.maxExpectedLoad) {
232  // logger.warn("Not investing because more than 20% of demand in pipeline.");
233 
234  } else if ((capacityOfTechnologyInPipeline > 2.0 * operationalCapacityOfTechnology)
235  && capacityOfTechnologyInPipeline > 9000) { // TODO:
236  // reflects that you cannot expand a technology out of zero.
237  // logger.warn(agent +
238  // " will not invest in {} technology because there's too much capacity in the pipeline",
239  // technology);
240  } else if (plant.getActualInvestedCapital() * (1 - agent.getDebtRatioOfInvestments()) > agent
241  .getDownpaymentFractionOfCash() * agent.getCash()) {
242  // logger.warn(agent +
243  // " will not invest in {} technology as he does not have enough money for downpayment",
244  // technology);
245  } else {
246 
247  Map<Substance, Double> myFuelPrices = new HashMap<Substance, Double>();
248  for (Substance fuel : technology.getFuels()) {
249  myFuelPrices.put(fuel, expectedFuelPrices.get(fuel));
250  }
251  Set<SubstanceShareInFuelMix> fuelMix = calculateFuelMix(plant, myFuelPrices, expectedCO2Price.get(market));
252  plant.setFuelMix(fuelMix);
253 
254  double expectedMarginalCost = determineExpectedMarginalCost(plant, expectedFuelPrices, expectedCO2Price.get(market));
255  double runningHours = 0d;
256  double expectedGrossProfit = 0d;
257 
258  long numberOfSegments = reps.segmentRepository.count();
259 
260  // TODO somehow the prices of long-term contracts could also
261  // be used here to determine the expected profit. Maybe not
262  // though...
263  for (SegmentLoad segmentLoad : market.getLoadDurationCurve()) {
264  double expectedElectricityPrice = marketInformation.expectedElectricityPricesPerSegment.get(segmentLoad
265  .getSegment());
266  double hours = segmentLoad.getSegment().getLengthInHours();
267  if (expectedMarginalCost <= expectedElectricityPrice) {
268  runningHours += hours;
269  if (technology.isIntermittent())
270  expectedGrossProfit += (expectedElectricityPrice - expectedMarginalCost)
271  * hours
272  * plant.getActualNominalCapacity()
273  * reps.intermittentTechnologyNodeLoadFactorRepository
274  .findIntermittentTechnologyNodeLoadFactorForNodeAndTechnology(node,
275  technology).getLoadFactorForSegment(segmentLoad.getSegment());
276  else
277  expectedGrossProfit += (expectedElectricityPrice - expectedMarginalCost)
278  * hours
279  * plant.getAvailableCapacity(futureTimePoint, segmentLoad.getSegment(), numberOfSegments);
280  }
281  }
282 
283  // logger.warn(agent +
284  // "expects technology {} to have {} running", technology,
285  // runningHours);
286  // expect to meet minimum running hours?
287  if (runningHours < plant.getTechnology().getMinimumRunningHours()) {
288  // logger.warn(agent+
289  // " will not invest in {} technology as he expect to have {} running, which is lower then required",
290  // technology, runningHours);
291  } else {
292 
293  double fixedOMCost = calculateFixedOperatingCost(plant, getCurrentTick());// /
294  // plant.getActualNominalCapacity();
295 
296  double operatingProfit = expectedGrossProfit - fixedOMCost;
297 
298  // TODO Alter discount rate on the basis of the amount
299  // in long-term contracts?
300  // TODO Alter discount rate on the basis of other stuff,
301  // such as amount of money, market share, portfolio
302  // size.
303 
304  // Calculation of weighted average cost of capital,
305  // based on the companies debt-ratio
306  double wacc = (1 - agent.getDebtRatioOfInvestments()) * agent.getEquityInterestRate()
307  + agent.getDebtRatioOfInvestments() * agent.getLoanInterestRate();
308 
309  // Creation of out cash-flow during power plant building
310  // phase (note that the cash-flow is negative!)
311  TreeMap<Integer, Double> discountedProjectCapitalOutflow = calculateSimplePowerPlantInvestmentCashFlow(
312  technology.getDepreciationTime(), (int) plant.getActualLeadtime(),
313  plant.getActualInvestedCapital(), 0);
314  // Creation of in cashflow during operation
315  TreeMap<Integer, Double> discountedProjectCashInflow = calculateSimplePowerPlantInvestmentCashFlow(
316  technology.getDepreciationTime(), (int) plant.getActualLeadtime(), 0, operatingProfit);
317 
318  double discountedCapitalCosts = npv(discountedProjectCapitalOutflow, wacc);// are
319  // defined
320  // negative!!
321  // plant.getActualNominalCapacity();
322 
323  // logger.warn("Agent {} found that the discounted capital for technology {} to be "
324  // + discountedCapitalCosts, agent,
325  // technology);
326 
327  double discountedOpProfit = npv(discountedProjectCashInflow, wacc);
328 
329  // logger.warn("Agent {} found that the projected discounted inflows for technology {} to be "
330  // + discountedOpProfit,
331  // agent, technology);
332 
333  double projectValue = discountedOpProfit + discountedCapitalCosts;
334 
335  // if (technology.isIntermittent()) {
336  // logger.warn(technology + "in " + node.getName() +
337  // ", NPV: " + projectValue
338  // + ", GrossProfit: " + expectedGrossProfit);
339  // }
340 
341  // logger.warn(
342  // "Agent {} found the project value for technology {} to be "
343  // + Math.round(projectValue /
344  // plant.getActualNominalCapacity())
345  // + " EUR/kW (running hours: " + runningHours + "",
346  // agent, technology);
347 
348  // double projectTotalValue = projectValuePerMW *
349  // plant.getActualNominalCapacity();
350 
351  // double projectReturnOnInvestment = discountedOpProfit
352  // / (-discountedCapitalCosts);
353 
354  /*
355  * Divide by capacity, in order not to favour large power plants (which have the single largest NPV
356  */
357 
358  if (projectValue > 0 && projectValue / plant.getActualNominalCapacity() > highestValue) {
359  highestValue = projectValue / plant.getActualNominalCapacity();
360  bestTechnology = plant.getTechnology();
361  bestNode = node;
362  }
363  }
364 
365  }
366 
367  }
368  }
369 
370  if (bestTechnology != null) {
371  // logger.warn("Agent {} invested in technology {} at tick " +
372  // getCurrentTick(), agent, bestTechnology);
373 
374  PowerPlant plant = new PowerPlant();
375  plant.specifyAndPersist(getCurrentTick(), agent, bestNode, bestTechnology);
376  PowerPlantManufacturer manufacturer = reps.genericRepository.findFirst(PowerPlantManufacturer.class);
377  BigBank bigbank = reps.genericRepository.findFirst(BigBank.class);
378 
379  double investmentCostPayedByEquity = plant.getActualInvestedCapital() * (1 - agent.getDebtRatioOfInvestments());
380  double investmentCostPayedByDebt = plant.getActualInvestedCapital() * agent.getDebtRatioOfInvestments();
381  double downPayment = investmentCostPayedByEquity;
382  createSpreadOutDownPayments(agent, manufacturer, downPayment, plant);
383 
384  double amount = determineLoanAnnuities(investmentCostPayedByDebt, plant.getTechnology().getDepreciationTime(),
385  agent.getLoanInterestRate());
386  // logger.warn("Loan amount is: " + amount);
387  Loan loan = reps.loanRepository.createLoan(agent, bigbank, amount, plant.getTechnology().getDepreciationTime(),
388  getCurrentTick(), plant);
389  // Create the loan
390  plant.createOrUpdateLoan(loan);
391 
392  } else {
393  // logger.warn("{} found no suitable technology anymore to invest in at tick "
394  // + getCurrentTick(), agent);
395  // agent will not participate in the next round of investment if
396  // he does not invest now
397  setNotWillingToInvest(agent);
398  }
399  }
400 
401  // }
402 
403  // Creates n downpayments of equal size in each of the n building years of a
404  // power plant
405  @Transactional
406  private void createSpreadOutDownPayments(EnergyProducer agent, PowerPlantManufacturer manufacturer, double totalDownPayment,
407  PowerPlant plant) {
408  int buildingTime = (int) plant.getActualLeadtime();
409  reps.nonTransactionalCreateRepository.createCashFlow(agent, manufacturer, totalDownPayment / buildingTime,
410  CashFlow.DOWNPAYMENT, getCurrentTick(), plant);
411  Loan downpayment = reps.loanRepository.createLoan(agent, manufacturer, totalDownPayment / buildingTime,
412  buildingTime - 1, getCurrentTick(), plant);
413  plant.createOrUpdateDownPayment(downpayment);
414  }
415 
416  @Transactional
417  private void setNotWillingToInvest(EnergyProducer agent) {
418  agent.setWillingToInvest(false);
419  }
420 
428  public Map<Substance, Double> predictFuelPrices(EnergyProducer agent, long futureTimePoint){
429  // Fuel Prices
430  Map<Substance, Double> expectedFuelPrices = new HashMap<Substance, Double>();
431  for (Substance substance : reps.substanceRepository.findAllSubstancesTradedOnCommodityMarkets()) {
432  //Find Clearing Points for the last 5 years (counting current year as one of the last 5 years).
433  Iterable<ClearingPoint> cps = reps.clearingPointRepository
434  .findAllClearingPointsForSubstanceTradedOnCommodityMarkesAndTimeRange(substance, getCurrentTick()
435  - (agent.getNumberOfYearsBacklookingForForecasting() - 1), getCurrentTick(), false);
436  //logger.warn("{}, {}", getCurrentTick()-(agent.getNumberOfYearsBacklookingForForecasting()-1), getCurrentTick());
437  //Create regression object
438  SimpleRegression gtr = new SimpleRegression();
439  for (ClearingPoint clearingPoint : cps) {
440  //logger.warn("CP {}: {} , in" + clearingPoint.getTime(), substance.getName(), clearingPoint.getPrice());
441  gtr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
442  }
443  gtr.addData(getCurrentTick(), findLastKnownPriceForSubstance(substance, getCurrentTick()));
444  expectedFuelPrices.put(substance, gtr.predict(futureTimePoint));
445  //logger.warn("Forecast {}: {}, in Step " + futureTimePoint, substance, expectedFuelPrices.get(substance));
446  }
447  return expectedFuelPrices;
448  }
449 
450  // Create a powerplant investment and operation cash-flow in the form of a
451  // map. If only investment, or operation costs should be considered set
452  // totalInvestment or operatingProfit to 0
453  private TreeMap<Integer, Double> calculateSimplePowerPlantInvestmentCashFlow(int depriacationTime, int buildingTime,
454  double totalInvestment, double operatingProfit) {
455  TreeMap<Integer, Double> investmentCashFlow = new TreeMap<Integer, Double>();
456  double equalTotalDownPaymentInstallement = totalInvestment / buildingTime;
457  for (int i = 0; i < buildingTime; i++) {
458  investmentCashFlow.put(new Integer(i), -equalTotalDownPaymentInstallement);
459  }
460  for (int i = buildingTime; i < depriacationTime + buildingTime; i++) {
461  investmentCashFlow.put(new Integer(i), operatingProfit);
462  }
463 
464  return investmentCashFlow;
465  }
466 
467  private double npv(TreeMap<Integer, Double> netCashFlow, double wacc) {
468  double npv = 0;
469  for (Integer iterator : netCashFlow.keySet()) {
470  npv += netCashFlow.get(iterator).doubleValue() / Math.pow(1 + wacc, iterator.intValue());
471  }
472  return npv;
473  }
474 
475  public double determineExpectedMarginalCost(PowerPlant plant, Map<Substance, Double> expectedFuelPrices, double expectedCO2Price) {
476  double mc = determineExpectedMarginalFuelCost(plant, expectedFuelPrices);
477  double co2Intensity = plant.calculateEmissionIntensity();
478  mc += co2Intensity * expectedCO2Price;
479  return mc;
480  }
481 
482  public double determineExpectedMarginalFuelCost(PowerPlant powerPlant, Map<Substance, Double> expectedFuelPrices) {
483  double fc = 0d;
484  for (SubstanceShareInFuelMix mix : powerPlant.getFuelMix()) {
485  double amount = mix.getShare();
486  double fuelPrice = expectedFuelPrices.get(mix.getSubstance());
487  fc += amount * fuelPrice;
488  }
489  return fc;
490  }
491 
492  private PowerGridNode getNodeForZone(Zone zone) {
493  for (PowerGridNode node : reps.genericRepository.findAll(PowerGridNode.class)) {
494  if (node.getZone().equals(zone)) {
495  return node;
496  }
497  }
498  return null;
499  }
500 
501  private class MarketInformation {
502 
503  Map<Segment, Double> expectedElectricityPricesPerSegment;
504  double maxExpectedLoad = 0d;
505  Map<PowerPlant, Double> meritOrder;
506  double capacitySum;
507 
508  MarketInformation(ElectricitySpotMarket market, Map<ElectricitySpotMarket, Double> expectedDemand, Map<Substance, Double> fuelPrices, double co2price, long time) {
509  // determine expected power prices
510  expectedElectricityPricesPerSegment = new HashMap<Segment, Double>();
511  Map<PowerPlant, Double> marginalCostMap = new HashMap<PowerPlant, Double>();
512  capacitySum = 0d;
513 
514  // get merit order for this market
515  for (PowerPlant plant : reps.powerPlantRepository.findExpectedOperationalPowerPlantsInMarket(market, time)) {
516 
517  double plantMarginalCost = determineExpectedMarginalCost(plant, fuelPrices, co2price);
518  marginalCostMap.put(plant, plantMarginalCost);
519  capacitySum += plant.getActualNominalCapacity();
520  }
521 
522  //get difference between technology target and expected operational capacity
523  for (TargetInvestor targetInvestor : reps.targetInvestorRepository.findAll()) {
524  if (!(targetInvestor instanceof StochasticTargetInvestor)) {
525  for (PowerGeneratingTechnologyTarget pggt : targetInvestor.getPowerGenerationTechnologyTargets()) {
526  double expectedTechnologyCapacity = reps.powerPlantRepository
527  .calculateCapacityOfExpectedOperationalPowerPlantsInMarketAndTechnology(market,
528  pggt.getPowerGeneratingTechnology(), time);
529  double targetDifference = pggt.getTrend().getValue(time) - expectedTechnologyCapacity;
530  if (targetDifference > 0) {
531  PowerPlant plant = new PowerPlant();
532  plant.specifyNotPersist(getCurrentTick(), new EnergyProducer(),
533  reps.powerGridNodeRepository.findFirstPowerGridNodeByElectricitySpotMarket(market),
534  pggt.getPowerGeneratingTechnology());
535  plant.setActualNominalCapacity(targetDifference);
536  double plantMarginalCost = determineExpectedMarginalCost(plant, fuelPrices, co2price);
537  marginalCostMap.put(plant, plantMarginalCost);
538  capacitySum += targetDifference;
539  }
540  }
541  } else {
542  for (PowerGeneratingTechnologyTarget pggt : targetInvestor.getPowerGenerationTechnologyTargets()) {
543  double expectedTechnologyCapacity = reps.powerPlantRepository
544  .calculateCapacityOfExpectedOperationalPowerPlantsInMarketAndTechnology(market,
545  pggt.getPowerGeneratingTechnology(), time);
546  double expectedTechnologyAddition = 0;
547  long contructionTime = getCurrentTick()
548  + pggt.getPowerGeneratingTechnology().getExpectedLeadtime()
549  + pggt.getPowerGeneratingTechnology().getExpectedPermittime();
550  for (long investmentTimeStep = contructionTime + 1; investmentTimeStep <= time; investmentTimeStep = investmentTimeStep + 1) {
551  expectedTechnologyAddition += (pggt.getTrend().getValue(investmentTimeStep) - pggt
552  .getTrend().getValue(investmentTimeStep - 1));
553  }
554  if (expectedTechnologyAddition > 0) {
555  PowerPlant plant = new PowerPlant();
556  plant.specifyNotPersist(getCurrentTick(), new EnergyProducer(),
557  reps.powerGridNodeRepository.findFirstPowerGridNodeByElectricitySpotMarket(market),
558  pggt.getPowerGeneratingTechnology());
559  plant.setActualNominalCapacity(expectedTechnologyAddition);
560  double plantMarginalCost = determineExpectedMarginalCost(plant, fuelPrices, co2price);
561  marginalCostMap.put(plant, plantMarginalCost);
562  capacitySum += expectedTechnologyAddition;
563  }
564  }
565  }
566 
567  }
568 
569  MapValueComparator comp = new MapValueComparator(marginalCostMap);
570  meritOrder = new TreeMap<PowerPlant, Double>(comp);
571  meritOrder.putAll(marginalCostMap);
572 
573  long numberOfSegments = reps.segmentRepository.count();
574 
575  double demandFactor = expectedDemand.get(market).doubleValue();
576 
577  // find expected prices per segment given merit order
578  for (SegmentLoad segmentLoad : market.getLoadDurationCurve()) {
579 
580  double expectedSegmentLoad = segmentLoad.getBaseLoad() * demandFactor;
581 
582  if (expectedSegmentLoad > maxExpectedLoad) {
583  maxExpectedLoad = expectedSegmentLoad;
584  }
585 
586  double segmentSupply = 0d;
587  double segmentPrice = 0d;
588  double totalCapacityAvailable = 0d;
589 
590  for (Entry<PowerPlant, Double> plantCost : meritOrder.entrySet()) {
591  PowerPlant plant = plantCost.getKey();
592  double plantCapacity = 0d;
593  // Determine available capacity in the future in this
594  // segment
595  plantCapacity = plant.getExpectedAvailableCapacity(time, segmentLoad.getSegment(), numberOfSegments);
596  totalCapacityAvailable += plantCapacity;
597  // logger.warn("Capacity of plant " + plant.toString() +
598  // " is " +
599  // plantCapacity/plant.getActualNominalCapacity());
600  if (segmentSupply < expectedSegmentLoad) {
601  segmentSupply += plantCapacity;
602  segmentPrice = plantCost.getValue();
603  }
604 
605  }
606 
607  // logger.warn("Segment " +
608  // segmentLoad.getSegment().getSegmentID() + " supply equals " +
609  // segmentSupply + " and segment demand equals " +
610  // expectedSegmentLoad);
611 
612  // Find strategic reserve operator for the market.
613  double reservePrice = 0;
614  double reserveVolume = 0;
615  for (StrategicReserveOperator operator : strategicReserveOperatorRepository.findAll()) {
616  ElectricitySpotMarket market1 = reps.marketRepository.findElectricitySpotMarketForZone(operator
617  .getZone());
618  if (market.getNodeId().intValue() == market1.getNodeId().intValue()) {
619  reservePrice = operator.getReservePriceSR();
620  reserveVolume = operator.getReserveVolume();
621  }
622  }
623 
624  if (segmentSupply >= expectedSegmentLoad
625  && ((totalCapacityAvailable - expectedSegmentLoad) <= (reserveVolume))) {
626  expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), reservePrice);
627  // logger.warn("Price: "+
628  // expectedElectricityPricesPerSegment);
629  } else if (segmentSupply >= expectedSegmentLoad
630  && ((totalCapacityAvailable - expectedSegmentLoad) > (reserveVolume))) {
631  expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), segmentPrice);
632  // logger.warn("Price: "+
633  // expectedElectricityPricesPerSegment);
634  } else {
635  expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), market.getValueOfLostLoad());
636  }
637 
638  }
639  }
640  }
641 
654  protected HashMap<ElectricitySpotMarket, Double> determineExpectedCO2PriceInclTaxAndFundamentalForecast(
655  long futureTimePoint, long yearsLookingBackForRegression, int adjustmentForDetermineFuelMix,
656  long clearingTick) {
657  HashMap<ElectricitySpotMarket, Double> co2Prices = new HashMap<ElectricitySpotMarket, Double>();
658  CO2Auction co2Auction = reps.genericRepository.findFirst(CO2Auction.class);
659  Iterable<ClearingPoint> cps = reps.clearingPointRepository.findAllClearingPointsForMarketAndTimeRange(
660  co2Auction, clearingTick - yearsLookingBackForRegression + 1 - adjustmentForDetermineFuelMix,
661  clearingTick - adjustmentForDetermineFuelMix, false);
662  // Create regression object and calculate average
663  SimpleRegression sr = new SimpleRegression();
664  Government government = reps.template.findAll(Government.class).iterator().next();
665  double lastPrice = 0;
666  double averagePrice = 0;
667  int i = 0;
668  for (ClearingPoint clearingPoint : cps) {
669  sr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
670  lastPrice = clearingPoint.getPrice();
671  averagePrice += lastPrice;
672  i++;
673  }
674  averagePrice = averagePrice / i;
675  double expectedCO2Price;
676  double expectedRegressionCO2Price;
677  if (i > 1) {
678  expectedRegressionCO2Price = sr.predict(futureTimePoint);
679  expectedRegressionCO2Price = Math.max(0, expectedRegressionCO2Price);
680  expectedRegressionCO2Price = Math
681  .min(expectedRegressionCO2Price, government.getCo2Penalty(futureTimePoint));
682  } else {
683  expectedRegressionCO2Price = lastPrice;
684  }
685  ClearingPoint expectedCO2ClearingPoint = reps.clearingPointRepository.findClearingPointForMarketAndTime(
686  co2Auction,
687  getCurrentTick()
688  + reps.genericRepository.findFirst(DecarbonizationModel.class).getCentralForecastingYear(),
689  true);
690  expectedCO2Price = (expectedCO2ClearingPoint == null) ? 0 : expectedCO2ClearingPoint.getPrice();
691  expectedCO2Price = (expectedCO2Price + expectedRegressionCO2Price) / 2;
692  for (ElectricitySpotMarket esm : reps.marketRepository.findAllElectricitySpotMarkets()) {
693  double nationalCo2MinPriceinFutureTick = reps.nationalGovernmentRepository
694  .findNationalGovernmentByElectricitySpotMarket(esm).getMinNationalCo2PriceTrend()
695  .getValue(futureTimePoint);
696  double co2PriceInCountry = 0d;
697  if (expectedCO2Price > nationalCo2MinPriceinFutureTick) {
698  co2PriceInCountry = expectedCO2Price;
699  } else {
700  co2PriceInCountry = nationalCo2MinPriceinFutureTick;
701  }
702  co2PriceInCountry += reps.genericRepository.findFirst(Government.class).getCO2Tax(futureTimePoint);
703  co2Prices.put(esm, Double.valueOf(co2PriceInCountry));
704  }
705  return co2Prices;
706  }
707 
708 }
HashMap< ElectricitySpotMarket, Double > determineExpectedCO2PriceInclTaxAndFundamentalForecast(long futureTimePoint, long yearsLookingBackForRegression, int adjustmentForDetermineFuelMix, long clearingTick)