16 package emlab.gen.role.investment;
18 import java.util.HashMap;
19 import java.util.LinkedList;
21 import java.util.Map.Entry;
23 import java.util.TreeMap;
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;
34 import agentspring.role.Role;
71 public class InvestInPowerGenerationTechnologiesStandard<T
extends EnergyProducer> extends GenericInvestmentRole<T>
82 Neo4jTemplate
template;
90 Map<ElectricitySpotMarket, MarketInformation> marketInfoMap =
new HashMap<ElectricitySpotMarket, MarketInformation>();
93 public void act(T agent) {
95 long futureTimePoint = getCurrentTick() + agent.getInvestmentFutureTimeHorizon();
100 Map<Substance, Double> expectedFuelPrices = predictFuelPrices(agent, futureTimePoint);
103 Map<ElectricitySpotMarket, Double> expectedCO2Price = determineExpectedCO2PriceInclTaxAndFundamentalForecast(
105 agent.getNumberOfYearsBacklookingForForecasting(), 0, getCurrentTick());
110 Map<ElectricitySpotMarket, Double> expectedCO2PriceOld = determineExpectedCO2PriceInclTax(futureTimePoint,
111 agent.getNumberOfYearsBacklookingForForecasting(), getCurrentTick());
118 Map<ElectricitySpotMarket, Double> expectedDemand =
new HashMap<ElectricitySpotMarket, Double>();
121 for(
long time = getCurrentTick(); time>getCurrentTick()-agent.getNumberOfYearsBacklookingForForecasting() && time>=0; time=time-1){
122 gtr.addData(time, elm.getDemandGrowthTrend().getValue(time));
124 expectedDemand.put(elm, gtr.predict(futureTimePoint));
134 MarketInformation marketInformation =
new MarketInformation(market, expectedDemand, expectedFuelPrices, expectedCO2Price.get(market)
135 .doubleValue(), futureTimePoint);
153 double highestValue = Double.MIN_VALUE;
159 DecarbonizationModel model = reps.genericRepository.findAll(DecarbonizationModel.class).iterator().next();
161 if (technology.isIntermittent() && model.isNoPrivateIntermittentRESInvestment())
164 Iterable<PowerGridNode> possibleInstallationNodes;
170 if(technology.isIntermittent())
171 possibleInstallationNodes = reps.powerGridNodeRepository.findAllPowerGridNodesByZone(market.getZone());
173 possibleInstallationNodes =
new LinkedList<PowerGridNode>();
174 ((LinkedList<PowerGridNode>) possibleInstallationNodes).add(reps.powerGridNodeRepository
175 .findAllPowerGridNodesByZone(market.getZone()).iterator().next());
185 plant.specifyNotPersist(getCurrentTick(), agent, node, technology);
188 double expectedInstalledCapacityOfTechnology = reps.powerPlantRepository
189 .calculateCapacityOfExpectedOperationalPowerPlantsInMarketAndTechnology(market, technology, futureTimePoint);
191 .findOneByTechnologyAndMarket(technology, market);
192 if (technologyTarget != null) {
193 double technologyTargetCapacity = technologyTarget.getTrend().getValue(futureTimePoint);
194 expectedInstalledCapacityOfTechnology = (technologyTargetCapacity > expectedInstalledCapacityOfTechnology) ? technologyTargetCapacity
195 : expectedInstalledCapacityOfTechnology;
197 double pgtNodeLimit = Double.MAX_VALUE;
199 .findOneByTechnologyAndNode(technology, plant.getLocation());
200 if (pgtLimit != null) {
201 pgtNodeLimit = pgtLimit.getUpperCapacityLimit(futureTimePoint);
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,
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());
220 .getMaximumInstalledCapacityFractionInCountry()) {
226 }
else if (expectedOwnedCapacityInMarketOfThisTechnology > expectedOwnedTotalCapacityInMarket
227 * technology.getMaximumInstalledCapacityFractionPerAgent()) {
231 }
else if (capacityInPipelineInMarket > 0.2 * marketInformation.maxExpectedLoad) {
234 }
else if ((capacityOfTechnologyInPipeline > 2.0 * operationalCapacityOfTechnology)
235 && capacityOfTechnologyInPipeline > 9000) {
240 }
else if (plant.getActualInvestedCapital() * (1 - agent.getDebtRatioOfInvestments()) > agent
241 .getDownpaymentFractionOfCash() * agent.getCash()) {
247 Map<Substance, Double> myFuelPrices =
new HashMap<Substance, Double>();
248 for (
Substance fuel : technology.getFuels()) {
249 myFuelPrices.put(fuel, expectedFuelPrices.get(fuel));
251 Set<SubstanceShareInFuelMix> fuelMix = calculateFuelMix(plant, myFuelPrices, expectedCO2Price.get(market));
252 plant.setFuelMix(fuelMix);
254 double expectedMarginalCost = determineExpectedMarginalCost(plant, expectedFuelPrices, expectedCO2Price.get(market));
255 double runningHours = 0d;
256 double expectedGrossProfit = 0d;
258 long numberOfSegments = reps.segmentRepository.count();
263 for (
SegmentLoad segmentLoad : market.getLoadDurationCurve()) {
264 double expectedElectricityPrice = marketInformation.expectedElectricityPricesPerSegment.get(segmentLoad
266 double hours = segmentLoad.getSegment().getLengthInHours();
267 if (expectedMarginalCost <= expectedElectricityPrice) {
268 runningHours += hours;
269 if (technology.isIntermittent())
270 expectedGrossProfit += (expectedElectricityPrice - expectedMarginalCost)
272 * plant.getActualNominalCapacity()
273 * reps.intermittentTechnologyNodeLoadFactorRepository
274 .findIntermittentTechnologyNodeLoadFactorForNodeAndTechnology(node,
275 technology).getLoadFactorForSegment(segmentLoad.getSegment());
277 expectedGrossProfit += (expectedElectricityPrice - expectedMarginalCost)
279 * plant.getAvailableCapacity(futureTimePoint, segmentLoad.getSegment(), numberOfSegments);
287 if (runningHours < plant.getTechnology().getMinimumRunningHours()) {
293 double fixedOMCost = calculateFixedOperatingCost(plant, getCurrentTick());
296 double operatingProfit = expectedGrossProfit - fixedOMCost;
306 double wacc = (1 - agent.getDebtRatioOfInvestments()) * agent.getEquityInterestRate()
307 + agent.getDebtRatioOfInvestments() * agent.getLoanInterestRate();
311 TreeMap<Integer, Double> discountedProjectCapitalOutflow = calculateSimplePowerPlantInvestmentCashFlow(
312 technology.getDepreciationTime(), (int) plant.getActualLeadtime(),
313 plant.getActualInvestedCapital(), 0);
315 TreeMap<Integer, Double> discountedProjectCashInflow = calculateSimplePowerPlantInvestmentCashFlow(
316 technology.getDepreciationTime(), (int) plant.getActualLeadtime(), 0, operatingProfit);
318 double discountedCapitalCosts = npv(discountedProjectCapitalOutflow, wacc);
327 double discountedOpProfit = npv(discountedProjectCashInflow, wacc);
333 double projectValue = discountedOpProfit + discountedCapitalCosts;
359 highestValue = projectValue / plant.getActualNominalCapacity();
360 bestTechnology = plant.getTechnology();
370 if (bestTechnology != null) {
375 plant.specifyAndPersist(getCurrentTick(), agent, bestNode, bestTechnology);
377 BigBank bigbank = reps.genericRepository.findFirst(BigBank.class);
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);
384 double amount = determineLoanAnnuities(investmentCostPayedByDebt, plant.getTechnology().getDepreciationTime(),
385 agent.getLoanInterestRate());
387 Loan loan = reps.loanRepository.createLoan(agent, bigbank, amount, plant.getTechnology().getDepreciationTime(),
388 getCurrentTick(), plant);
390 plant.createOrUpdateLoan(loan);
397 setNotWillingToInvest(agent);
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);
418 agent.setWillingToInvest(
false);
430 Map<Substance, Double> expectedFuelPrices =
new HashMap<Substance, Double>();
431 for (
Substance substance : reps.substanceRepository.findAllSubstancesTradedOnCommodityMarkets()) {
433 Iterable<ClearingPoint> cps = reps.clearingPointRepository
434 .findAllClearingPointsForSubstanceTradedOnCommodityMarkesAndTimeRange(substance, getCurrentTick()
435 - (agent.getNumberOfYearsBacklookingForForecasting() - 1), getCurrentTick(),
false);
438 SimpleRegression gtr =
new SimpleRegression();
441 gtr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
443 gtr.addData(getCurrentTick(), findLastKnownPriceForSubstance(substance, getCurrentTick()));
444 expectedFuelPrices.put(substance, gtr.predict(futureTimePoint));
447 return expectedFuelPrices;
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);
460 for (
int i = buildingTime; i < depriacationTime + buildingTime; i++) {
461 investmentCashFlow.put(
new Integer(i), operatingProfit);
464 return investmentCashFlow;
467 private double npv(TreeMap<Integer, Double> netCashFlow,
double wacc) {
469 for (Integer iterator : netCashFlow.keySet()) {
470 npv += netCashFlow.get(iterator).doubleValue() / Math.pow(1 + wacc, iterator.intValue());
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;
482 public double determineExpectedMarginalFuelCost(PowerPlant powerPlant, Map<Substance, Double> expectedFuelPrices) {
484 for (SubstanceShareInFuelMix mix : powerPlant.getFuelMix()) {
485 double amount = mix.getShare();
486 double fuelPrice = expectedFuelPrices.get(mix.getSubstance());
487 fc += amount * fuelPrice;
492 private PowerGridNode getNodeForZone(Zone zone) {
493 for (PowerGridNode node : reps.genericRepository.findAll(PowerGridNode.class)) {
494 if (node.getZone().equals(zone)) {
501 private class MarketInformation {
503 Map<Segment, Double> expectedElectricityPricesPerSegment;
504 double maxExpectedLoad = 0d;
505 Map<PowerPlant, Double> meritOrder;
508 MarketInformation(ElectricitySpotMarket market, Map<ElectricitySpotMarket, Double> expectedDemand, Map<Substance, Double> fuelPrices,
double co2price,
long time) {
510 expectedElectricityPricesPerSegment =
new HashMap<Segment, Double>();
511 Map<PowerPlant, Double> marginalCostMap =
new HashMap<PowerPlant, Double>();
515 for (PowerPlant plant : reps.powerPlantRepository.findExpectedOperationalPowerPlantsInMarket(market, time)) {
517 double plantMarginalCost = determineExpectedMarginalCost(plant, fuelPrices, co2price);
518 marginalCostMap.put(plant, plantMarginalCost);
519 capacitySum += plant.getActualNominalCapacity();
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;
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));
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;
569 MapValueComparator comp =
new MapValueComparator(marginalCostMap);
570 meritOrder =
new TreeMap<PowerPlant, Double>(comp);
571 meritOrder.putAll(marginalCostMap);
573 long numberOfSegments = reps.segmentRepository.count();
575 double demandFactor = expectedDemand.get(market).doubleValue();
578 for (SegmentLoad segmentLoad : market.getLoadDurationCurve()) {
580 double expectedSegmentLoad = segmentLoad.getBaseLoad() * demandFactor;
582 if (expectedSegmentLoad > maxExpectedLoad) {
583 maxExpectedLoad = expectedSegmentLoad;
586 double segmentSupply = 0d;
587 double segmentPrice = 0d;
588 double totalCapacityAvailable = 0d;
590 for (Entry<PowerPlant, Double> plantCost : meritOrder.entrySet()) {
591 PowerPlant plant = plantCost.getKey();
592 double plantCapacity = 0d;
595 plantCapacity = plant.getExpectedAvailableCapacity(time, segmentLoad.getSegment(), numberOfSegments);
596 totalCapacityAvailable += plantCapacity;
600 if (segmentSupply < expectedSegmentLoad) {
601 segmentSupply += plantCapacity;
602 segmentPrice = plantCost.getValue();
613 double reservePrice = 0;
614 double reserveVolume = 0;
615 for (StrategicReserveOperator
operator : strategicReserveOperatorRepository.findAll()) {
616 ElectricitySpotMarket market1 = reps.marketRepository.findElectricitySpotMarketForZone(operator
618 if (market.getNodeId().intValue() == market1.getNodeId().intValue()) {
619 reservePrice = operator.getReservePriceSR();
620 reserveVolume = operator.getReserveVolume();
624 if (segmentSupply >= expectedSegmentLoad
625 && ((totalCapacityAvailable - expectedSegmentLoad) <= (reserveVolume))) {
626 expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), reservePrice);
629 }
else if (segmentSupply >= expectedSegmentLoad
630 && ((totalCapacityAvailable - expectedSegmentLoad) > (reserveVolume))) {
631 expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), segmentPrice);
635 expectedElectricityPricesPerSegment.put(segmentLoad.getSegment(), market.getValueOfLostLoad());
655 long futureTimePoint,
long yearsLookingBackForRegression,
int adjustmentForDetermineFuelMix,
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);
663 SimpleRegression sr =
new SimpleRegression();
664 Government government = reps.template.findAll(Government.class).iterator().next();
665 double lastPrice = 0;
666 double averagePrice = 0;
669 sr.addData(clearingPoint.getTime(), clearingPoint.getPrice());
670 lastPrice = clearingPoint.getPrice();
671 averagePrice += lastPrice;
674 averagePrice = averagePrice / i;
675 double expectedCO2Price;
676 double expectedRegressionCO2Price;
678 expectedRegressionCO2Price = sr.predict(futureTimePoint);
679 expectedRegressionCO2Price = Math.max(0, expectedRegressionCO2Price);
680 expectedRegressionCO2Price = Math
681 .min(expectedRegressionCO2Price, government.getCo2Penalty(futureTimePoint));
683 expectedRegressionCO2Price = lastPrice;
685 ClearingPoint expectedCO2ClearingPoint = reps.clearingPointRepository.findClearingPointForMarketAndTime(
688 + reps.genericRepository.findFirst(DecarbonizationModel.class).getCentralForecastingYear(),
690 expectedCO2Price = (expectedCO2ClearingPoint == null) ? 0 : expectedCO2ClearingPoint.getPrice();
691 expectedCO2Price = (expectedCO2Price + expectedRegressionCO2Price) / 2;
693 double nationalCo2MinPriceinFutureTick = reps.nationalGovernmentRepository
694 .findNationalGovernmentByElectricitySpotMarket(esm).getMinNationalCo2PriceTrend()
695 .getValue(futureTimePoint);
696 double co2PriceInCountry = 0d;
697 if (expectedCO2Price > nationalCo2MinPriceinFutureTick) {
698 co2PriceInCountry = expectedCO2Price;
700 co2PriceInCountry = nationalCo2MinPriceinFutureTick;
702 co2PriceInCountry += reps.genericRepository.findFirst(Government.class).getCO2Tax(futureTimePoint);
703 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()