16 package emlab.gen.role.investment;
18 import java.util.HashMap;
20 import java.util.Map.Entry;
22 import java.util.TreeMap;
24 import org.apache.commons.math.stat.regression.SimpleRegression;
25 import org.springframework.beans.factory.annotation.Autowired;
26 import org.springframework.beans.factory.annotation.Configurable;
27 import org.springframework.data.annotation.Transient;
28 import org.springframework.data.neo4j.annotation.NodeEntity;
29 import org.springframework.data.neo4j.aspects.core.NodeBacked;
30 import org.springframework.data.neo4j.support.Neo4jTemplate;
31 import org.springframework.transaction.annotation.Transactional;
33 import agentspring.role.Role;
68 public class InvestInPowerGenerationTechnologiesWithCO2ForecastRole<T
extends EnergyProducer> extends GenericInvestmentRole<T>
79 Neo4jTemplate
template;
87 Map<ElectricitySpotMarket, MarketInformation> marketInfoMap =
new HashMap<ElectricitySpotMarket, MarketInformation>();
90 public void act(T agent) {
92 long futureTimePoint = getCurrentTick() + agent.getInvestmentFutureTimeHorizon();
97 Map<Substance, Double> expectedFuelPrices = predictFuelPrices(agent, futureTimePoint);
100 Map<ElectricitySpotMarket, Double> expectedCO2Price = determineExpectedCO2PriceInclTaxAndFundamentalForecast(
102 agent.getNumberOfYearsBacklookingForForecasting(), 0, getCurrentTick());
107 Map<ElectricitySpotMarket, Double> expectedCO2PriceOld = determineExpectedCO2PriceInclTax(futureTimePoint,
108 agent.getNumberOfYearsBacklookingForForecasting(), getCurrentTick());
115 Map<ElectricitySpotMarket, Double> expectedDemand =
new HashMap<ElectricitySpotMarket, Double>();
118 for(
long time = getCurrentTick(); time>getCurrentTick()-agent.getNumberOfYearsBacklookingForForecasting() && time>=0; time=time-1){
119 gtr.addData(time, elm.getDemandGrowthTrend().getValue(time));
121 expectedDemand.put(elm, gtr.predict(futureTimePoint));
131 MarketInformation marketInformation =
new MarketInformation(market, expectedDemand, expectedFuelPrices, expectedCO2Price.get(market)
132 .doubleValue(), futureTimePoint);
150 double highestValue = Double.MIN_VALUE;
156 plant.specifyNotPersist(getCurrentTick(), agent, getNodeForZone(market.getZone()), technology);
159 double expectedInstalledCapacityOfTechnology = reps.powerPlantRepository
160 .calculateCapacityOfExpectedOperationalPowerPlantsInMarketAndTechnology(market, technology, futureTimePoint);
162 if(technologyTarget!=null){
163 double technologyTargetCapacity = technologyTarget.getTrend().getValue(futureTimePoint);
164 expectedInstalledCapacityOfTechnology = (technologyTargetCapacity > expectedInstalledCapacityOfTechnology) ? technologyTargetCapacity : expectedInstalledCapacityOfTechnology;
166 double pgtNodeLimit = Double.MAX_VALUE;
168 .findOneByTechnologyAndNode(technology, plant.getLocation());
169 if (pgtLimit != null) {
170 pgtNodeLimit = pgtLimit.getUpperCapacityLimit(futureTimePoint);
172 double expectedInstalledCapacityOfTechnologyInNode = reps.powerPlantRepository
173 .calculateCapacityOfExpectedOperationalPowerPlantsByNodeAndTechnology(plant.getLocation(),
174 technology, futureTimePoint);
175 double expectedOwnedTotalCapacityInMarket = reps.powerPlantRepository
176 .calculateCapacityOfExpectedOperationalPowerPlantsInMarketByOwner(market, futureTimePoint, agent);
177 double expectedOwnedCapacityInMarketOfThisTechnology = reps.powerPlantRepository
178 .calculateCapacityOfExpectedOperationalPowerPlantsInMarketByOwnerAndTechnology(market, technology, futureTimePoint,
180 double capacityOfTechnologyInPipeline = reps.powerPlantRepository.calculateCapacityOfPowerPlantsByTechnologyInPipeline(
181 technology, getCurrentTick());
182 double operationalCapacityOfTechnology = reps.powerPlantRepository.calculateCapacityOfOperationalPowerPlantsByTechnology(
183 technology, getCurrentTick());
184 double capacityInPipelineInMarket = reps.powerPlantRepository
185 .calculateCapacityOfPowerPlantsByMarketInPipeline(market, getCurrentTick());
189 .getMaximumInstalledCapacityFractionInCountry()) {
195 }
else if (expectedOwnedCapacityInMarketOfThisTechnology > expectedOwnedTotalCapacityInMarket
196 * technology.getMaximumInstalledCapacityFractionPerAgent()) {
200 }
else if (capacityInPipelineInMarket > 0.2 * marketInformation.maxExpectedLoad) {
203 }
else if ((capacityOfTechnologyInPipeline > 2.0 * operationalCapacityOfTechnology)
204 && capacityOfTechnologyInPipeline > 9000) {
209 }
else if (plant.getActualInvestedCapital() * (1 - agent.getDebtRatioOfInvestments()) > agent
210 .getDownpaymentFractionOfCash() * agent.getCash()) {
216 Map<Substance, Double> myFuelPrices =
new HashMap<Substance, Double>();
217 for (
Substance fuel : technology.getFuels()) {
218 myFuelPrices.put(fuel, expectedFuelPrices.get(fuel));
220 Set<SubstanceShareInFuelMix> fuelMix = calculateFuelMix(plant, myFuelPrices, expectedCO2Price.get(market));
221 plant.setFuelMix(fuelMix);
223 double expectedMarginalCost = determineExpectedMarginalCost(plant, expectedFuelPrices, expectedCO2Price.get(market));
224 double runningHours = 0d;
225 double expectedGrossProfit = 0d;
227 long numberOfSegments = reps.segmentRepository.count();
232 for (
SegmentLoad segmentLoad : market.getLoadDurationCurve()) {
233 double expectedElectricityPrice = marketInformation.expectedElectricityPricesPerSegment.get(segmentLoad
235 double hours = segmentLoad.getSegment().getLengthInHours();
236 if (expectedMarginalCost <= expectedElectricityPrice) {
237 runningHours += hours;
238 expectedGrossProfit += (expectedElectricityPrice - expectedMarginalCost) * hours
239 * plant.getAvailableCapacity(futureTimePoint, segmentLoad.getSegment(), numberOfSegments);
247 if (runningHours < plant.getTechnology().getMinimumRunningHours()) {
253 double fixedOMCost = calculateFixedOperatingCost(plant, getCurrentTick());
256 double operatingProfit = expectedGrossProfit - fixedOMCost;
266 double wacc = (1 - agent.getDebtRatioOfInvestments()) * agent.getEquityInterestRate()
267 + agent.getDebtRatioOfInvestments() * agent.getLoanInterestRate();
271 TreeMap<Integer, Double> discountedProjectCapitalOutflow = calculateSimplePowerPlantInvestmentCashFlow(
272 technology.getDepreciationTime(), (int) plant.getActualLeadtime(),
273 plant.getActualInvestedCapital(), 0);
275 TreeMap<Integer, Double> discountedProjectCashInflow = calculateSimplePowerPlantInvestmentCashFlow(
276 technology.getDepreciationTime(), (int) plant.getActualLeadtime(), 0, operatingProfit);
278 double discountedCapitalCosts = npv(discountedProjectCapitalOutflow, wacc);
287 double discountedOpProfit = npv(discountedProjectCashInflow, wacc);
293 double projectValue = discountedOpProfit + discountedCapitalCosts;
313 highestValue = projectValue / plant.getActualNominalCapacity();
314 bestTechnology = plant.getTechnology();
321 if (bestTechnology != null) {
325 plant.specifyAndPersist(getCurrentTick(), agent, getNodeForZone(market.getZone()), bestTechnology);
327 BigBank bigbank = reps.genericRepository.findFirst(BigBank.class);
329 double investmentCostPayedByEquity = plant.getActualInvestedCapital() * (1 - agent.getDebtRatioOfInvestments());
330 double investmentCostPayedByDebt = plant.getActualInvestedCapital() * agent.getDebtRatioOfInvestments();
331 double downPayment = investmentCostPayedByEquity;
332 createSpreadOutDownPayments(agent, manufacturer, downPayment, plant);
334 double amount = determineLoanAnnuities(investmentCostPayedByDebt, plant.getTechnology().getDepreciationTime(),
335 agent.getLoanInterestRate());
337 Loan loan = reps.loanRepository.createLoan(agent, bigbank, amount, plant.getTechnology().getDepreciationTime(),
338 getCurrentTick(), plant);
340 plant.createOrUpdateLoan(loan);
347 setNotWillingToInvest(agent);
358 int buildingTime = (int) plant.getActualLeadtime();
359 reps.nonTransactionalCreateRepository.createCashFlow(agent, manufacturer, totalDownPayment / buildingTime,
360 CashFlow.DOWNPAYMENT, getCurrentTick(), plant);
361 Loan downpayment = reps.loanRepository.createLoan(agent, manufacturer, totalDownPayment / buildingTime,
362 buildingTime - 1, getCurrentTick(), plant);
363 plant.createOrUpdateDownPayment(downpayment);
368 agent.setWillingToInvest(
false);
380 Map<Substance, Double> expectedFuelPrices =
new HashMap<Substance, Double>();
381 for (
Substance substance : reps.substanceRepository.findAllSubstancesTradedOnCommodityMarkets()) {
383 Iterable<ClearingPoint> cps = reps.clearingPointRepository
384 .findAllClearingPointsForSubstanceTradedOnCommodityMarkesAndTimeRange(substance, getCurrentTick()
385 - (agent.getNumberOfYearsBacklookingForForecasting() - 1), getCurrentTick(),
false);
388 SimpleRegression gtr =
new SimpleRegression();
391 gtr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
393 gtr.addData(getCurrentTick(), findLastKnownPriceForSubstance(substance, getCurrentTick()));
394 expectedFuelPrices.put(substance, gtr.predict(futureTimePoint));
397 return expectedFuelPrices;
403 private TreeMap<Integer, Double> calculateSimplePowerPlantInvestmentCashFlow(
int depriacationTime,
int buildingTime,
404 double totalInvestment,
double operatingProfit) {
405 TreeMap<Integer, Double> investmentCashFlow =
new TreeMap<Integer, Double>();
406 double equalTotalDownPaymentInstallement = totalInvestment / buildingTime;
407 for (
int i = 0; i < buildingTime; i++) {
408 investmentCashFlow.put(
new Integer(i), -equalTotalDownPaymentInstallement);
410 for (
int i = buildingTime; i < depriacationTime + buildingTime; i++) {
411 investmentCashFlow.put(
new Integer(i), operatingProfit);
414 return investmentCashFlow;
417 private double npv(TreeMap<Integer, Double> netCashFlow,
double wacc) {
419 for (Integer iterator : netCashFlow.keySet()) {
420 npv += netCashFlow.get(iterator).doubleValue() / Math.pow(1 + wacc, iterator.intValue());
425 public double determineExpectedMarginalCost(PowerPlant plant, Map<Substance, Double> expectedFuelPrices,
double expectedCO2Price) {
426 double mc = determineExpectedMarginalFuelCost(plant, expectedFuelPrices);
427 double co2Intensity = plant.calculateEmissionIntensity();
428 mc += co2Intensity * expectedCO2Price;
432 public double determineExpectedMarginalFuelCost(PowerPlant powerPlant, Map<Substance, Double> expectedFuelPrices) {
434 for (SubstanceShareInFuelMix mix : powerPlant.getFuelMix()) {
435 double amount = mix.getShare();
436 double fuelPrice = expectedFuelPrices.get(mix.getSubstance());
437 fc += amount * fuelPrice;
442 private PowerGridNode getNodeForZone(Zone zone) {
443 for (PowerGridNode node : reps.genericRepository.findAll(PowerGridNode.class)) {
444 if (node.getZone().equals(zone)) {
451 private class MarketInformation {
453 Map<Segment, Double> expectedElectricityPricesPerSegment;
454 double maxExpectedLoad = 0d;
455 Map<PowerPlant, Double> meritOrder;
458 MarketInformation(ElectricitySpotMarket market, Map<ElectricitySpotMarket, Double> expectedDemand, Map<Substance, Double> fuelPrices,
double co2price,
long time) {
460 expectedElectricityPricesPerSegment =
new HashMap<Segment, Double>();
461 Map<PowerPlant, Double> marginalCostMap =
new HashMap<PowerPlant, Double>();
465 for (PowerPlant plant : reps.powerPlantRepository.findExpectedOperationalPowerPlantsInMarket(market, time)) {
467 double plantMarginalCost = determineExpectedMarginalCost(plant, fuelPrices, co2price);
468 marginalCostMap.put(plant, plantMarginalCost);
469 capacitySum += plant.getActualNominalCapacity();
473 for(PowerGeneratingTechnologyTarget pggt : reps.powerGenerationTechnologyTargetRepository.findAllByMarket(market)){
474 double expectedTechnologyCapacity = reps.powerPlantRepository.calculateCapacityOfExpectedOperationalPowerPlantsInMarketAndTechnology(market, pggt.getPowerGeneratingTechnology(), time);
475 double targetDifference = pggt.getTrend().getValue(time) - expectedTechnologyCapacity;
476 if(targetDifference > 0){
477 PowerPlant plant =
new PowerPlant();
478 plant.specifyNotPersist(getCurrentTick(),
new EnergyProducer(), reps.powerGridNodeRepository.findFirstPowerGridNodeByElectricitySpotMarket(market), pggt.getPowerGeneratingTechnology());
479 plant.setActualNominalCapacity(targetDifference);
480 double plantMarginalCost = determineExpectedMarginalCost(plant, fuelPrices, co2price);
481 marginalCostMap.put(plant, plantMarginalCost);
482 capacitySum += targetDifference;
486 MapValueComparator comp =
new MapValueComparator(marginalCostMap);
487 meritOrder =
new TreeMap<PowerPlant, Double>(comp);
488 meritOrder.putAll(marginalCostMap);
490 long numberOfSegments = reps.segmentRepository.count();
492 double demandFactor = expectedDemand.get(market).doubleValue();
495 for (SegmentLoad segmentLoad : market.getLoadDurationCurve()) {
497 double expectedSegmentLoad = segmentLoad.getBaseLoad() * demandFactor;
499 if (expectedSegmentLoad > maxExpectedLoad) {
500 maxExpectedLoad = expectedSegmentLoad;
503 double segmentSupply = 0d;
504 double segmentPrice = 0d;
505 double totalCapacityAvailable = 0d;
507 for (Entry<PowerPlant, Double> plantCost : meritOrder.entrySet()) {
508 PowerPlant plant = plantCost.getKey();
509 double plantCapacity = 0d;
512 plantCapacity = plant.getExpectedAvailableCapacity(time, segmentLoad.getSegment(), numberOfSegments);
513 totalCapacityAvailable += plantCapacity;
517 if (segmentSupply < expectedSegmentLoad) {
518 segmentSupply += plantCapacity;
519 segmentPrice = plantCost.getValue();
530 double reservePrice = 0;
531 double reserveVolume = 0;
532 for (StrategicReserveOperator
operator : strategicReserveOperatorRepository.findAll()) {
533 ElectricitySpotMarket market1 = reps.marketRepository.findElectricitySpotMarketForZone(operator
535 if (market.getNodeId().intValue() == market1.getNodeId().intValue()) {
536 reservePrice = operator.getReservePriceSR();
537 reserveVolume = operator.getReserveVolume();
541 if (segmentSupply >= expectedSegmentLoad
542 && ((totalCapacityAvailable - expectedSegmentLoad) <= (reserveVolume))) {
543 expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), reservePrice);
546 }
else if (segmentSupply >= expectedSegmentLoad
547 && ((totalCapacityAvailable - expectedSegmentLoad) > (reserveVolume))) {
548 expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), segmentPrice);
552 expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), market.getValueOfLostLoad());
572 long futureTimePoint,
long yearsLookingBackForRegression,
int adjustmentForDetermineFuelMix,
574 HashMap<ElectricitySpotMarket, Double> co2Prices =
new HashMap<ElectricitySpotMarket, Double>();
575 CO2Auction co2Auction = reps.genericRepository.findFirst(CO2Auction.class);
576 Iterable<ClearingPoint> cps = reps.clearingPointRepository.findAllClearingPointsForMarketAndTimeRange(
577 co2Auction, clearingTick - yearsLookingBackForRegression + 1 - adjustmentForDetermineFuelMix,
578 clearingTick - adjustmentForDetermineFuelMix,
false);
580 SimpleRegression sr =
new SimpleRegression();
581 Government government = reps.template.findAll(Government.class).iterator().next();
582 double lastPrice = 0;
583 double averagePrice = 0;
586 sr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
587 lastPrice = clearingPoint.getPrice();
588 averagePrice += lastPrice;
591 averagePrice = averagePrice / i;
592 double expectedCO2Price;
593 double expectedRegressionCO2Price;
595 expectedRegressionCO2Price = sr.predict(futureTimePoint);
596 expectedRegressionCO2Price = Math.max(0, expectedRegressionCO2Price);
597 expectedRegressionCO2Price = Math
598 .min(expectedRegressionCO2Price, government.getCo2Penalty(futureTimePoint));
600 expectedRegressionCO2Price = lastPrice;
602 ClearingPoint expectedCO2ClearingPoint = reps.clearingPointRepository.findClearingPointForMarketAndTime(
605 + reps.genericRepository.findFirst(DecarbonizationModel.class).getCentralForecastingYear(),
607 expectedCO2Price = (expectedCO2ClearingPoint == null) ? 0 : expectedCO2ClearingPoint.getPrice();
608 expectedCO2Price = (expectedCO2Price + expectedRegressionCO2Price) / 2;
610 double nationalCo2MinPriceinFutureTick = reps.nationalGovernmentRepository
611 .findNationalGovernmentByElectricitySpotMarket(esm).getMinNationalCo2PriceTrend()
612 .getValue(futureTimePoint);
613 double co2PriceInCountry = 0d;
614 if (expectedCO2Price > nationalCo2MinPriceinFutureTick) {
615 co2PriceInCountry = expectedCO2Price;
617 co2PriceInCountry = nationalCo2MinPriceinFutureTick;
619 co2PriceInCountry += reps.genericRepository.findFirst(Government.class).getCO2Tax(futureTimePoint);
620 co2Prices.put(esm, Double.valueOf(co2PriceInCountry));
Map< Substance, Double > predictFuelPrices(EnergyProducer agent, long futureTimePoint)
HashMap< ElectricitySpotMarket, Double > determineExpectedCO2PriceInclTaxAndFundamentalForecast(long futureTimePoint, long yearsLookingBackForRegression, int adjustmentForDetermineFuelMix, long clearingTick)
double getActualNominalCapacity()