16 package emlab.gen.role.market;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.data.neo4j.support.Neo4jTemplate;
25 import org.springframework.transaction.annotation.Transactional;
27 import agentspring.role.Role;
28 import agentspring.role.RoleComponent;
63 AbstractClearElectricitySpotMarketRole<DecarbonizationModel> implements Role<DecarbonizationModel> {
81 Neo4jTemplate
template;
86 Map<Substance, Double> fuelPriceMap =
new HashMap<Substance, Double>();
88 fuelPriceMap.put(substance, findLastKnownPriceForSubstance(substance));
91 if (!model.isCo2BankingIsImplemented() || !model.isCo2TradingImplemented())
92 clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model,
false, getCurrentTick(),
93 fuelPriceMap, null, 0);
95 clearIterativeCO2ElectricitySpotMarketAndFutureMarketTwoCountryForTimestepAndFuelPrices(model,
101 public void makeCentralElectricityMarketForecastForTimeStep(
long clearingTick) {
105 Map<Substance, Double> fuelPriceMap = predictFuelPrices(model.getCentralForecastBacklookingYears(),
110 Map<ElectricitySpotMarket, Double> demandGrowthMap = predictDemand(model.getCentralForecastBacklookingYears(),
113 Government government = template.findAll(Government.class).iterator().next();
116 Map<ElectricitySpotMarket, Double> nationalMinCo2Prices =
new HashMap<ElectricitySpotMarket, Double>(2);
117 Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
119 if (model.isCo2TradingImplemented()) {
120 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
121 .getMinNationalCo2PriceTrend().getValue(clearingTick));
123 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
128 determineFuelMixRole.determineFuelMixForecastForYearAndFuelPriceMap(clearingTick, fuelPriceMap,
129 nationalMinCo2Prices);
131 submitOffersToElectricitySpotMarketRole.createOffersForElectricitySpotMarket(null, clearingTick,
true,
134 Iterable<PowerPlantDispatchPlan> ppdps = reps.powerPlantDispatchPlanRepository.findAll();
136 logger.info(ppdp.toString() +
" in " + ppdp.getBiddingMarket() +
" accepted: " + ppdp.getAcceptedAmount());
139 double previouslyBankedCertificates = reps.decarbonizationAgentRepository
140 .determineTotallyBankedCO2Certificates();
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);
149 double targetProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(clearingTick,
150 clearingTick + model.getCentralForecastingYear(), government, model);
152 clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model,
true, clearingTick,
153 fuelPriceMap, demandGrowthMap,
154 (previouslyBankedCertificates - targetProducerBanking) / model.getCentralForecastingYear());
156 clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model,
true, clearingTick,
157 fuelPriceMap, demandGrowthMap, 0);
162 public CO2SecantSearch clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(
163 DecarbonizationModel model,
boolean forecast,
long clearingTick, Map<Substance, Double> fuelPriceMap,
164 Map<ElectricitySpotMarket, Double> demandGrowthMap,
double co2CapAdjustment) {
167 model = template.findAll(DecarbonizationModel.class).iterator().next();
169 if (fuelPriceMap == null && forecast)
170 fuelPriceMap = predictFuelPrices(model.getCentralForecastBacklookingYears(), clearingTick);
172 if (demandGrowthMap == null && forecast)
173 demandGrowthMap = predictDemand(model.getCentralForecastBacklookingYears(), clearingTick);
178 logger.info(
"Clearing the CO2 and electricity spot markets using iteration for 2 countries ");
183 Interconnector interconnector = template.findAll(Interconnector.class).iterator().next();
186 List<Segment> segments = Utils.asList(reps.segmentRepository.findAll());
189 Government government = template.findAll(Government.class).iterator().next();
192 Map<ElectricitySpotMarket, Double> nationalMinCo2Prices =
new HashMap<ElectricitySpotMarket, Double>(2);
193 Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
195 if (model.isCo2TradingImplemented()) {
196 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
197 .getMinNationalCo2PriceTrend().getValue(clearingTick));
199 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
204 CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
205 CO2SecantSearch co2SecantSearch = null;
207 if (model.isCo2TradingImplemented()) {
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;
218 ClearingPoint lastClearingPointOfCo2Market = reps.clearingPointRepositoryOld
219 .findClearingPointForMarketAndTime(co2Auction, getCurrentTick() - 1,
false);
220 if (lastClearingPointOfCo2Market != null) {
221 co2SecantSearch.co2Emissions = lastClearingPointOfCo2Market.getVolume();
223 co2SecantSearch.co2Emissions = 0d;
226 int breakOffIterator = 0;
227 while (!co2SecantSearch.stable) {
229 if (breakOffIterator > 15) {
230 logger.warn(
"Iteration cancelled, last found CO2 Price is used.");
251 clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick, forecast,
252 demandGrowthMap, fuelPriceMap, co2SecantSearch.co2Price, nationalMinCo2Prices, segments,
253 interconnector, model);
259 co2SecantSearch = co2PriceSecantSearchUpdate(co2SecantSearch, model, government, forecast,
260 clearingTick, co2CapAdjustment);
265 reps.clearingPointRepositoryOld.createOrUpdateClearingPoint(co2Auction, co2SecantSearch.co2Price,
266 co2SecantSearch.co2Emissions, clearingTick, forecast);
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);
278 return co2SecantSearch;
292 void clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForOneSegment(
double interconnectorCapacity,
294 Map<ElectricitySpotMarket, Double> demandGrowthMap) {
296 GlobalSegmentClearingOutcome globalOutcome =
new GlobalSegmentClearingOutcome();
298 globalOutcome.loads = determineActualDemandForSpotMarkets(segment, demandGrowthMap);
300 globalOutcome.globalLoad = determineTotalLoadFromLoadMap(globalOutcome.loads);
304 globalOutcome.supplies.put(m, 0d);
308 double marginalPlantMarginalCost = clearGlobalMarketWithNoCapacityConstraints(segment, globalOutcome, forecast,
314 ElectricitySpotMarket firstMarket = reps.marketRepository.findAllElectricitySpotMarkets().iterator().next();
315 double loadInFirstMarket = globalOutcome.loads.get(firstMarket);
316 double supplyInFirstMarket = globalOutcome.supplies.get(firstMarket);
319 double interconnectorFlow = supplyInFirstMarket - loadInFirstMarket;
321 logger.info(
"Before market coupling interconnector flow: {}, available interconnector capacity {}",
322 interconnectorFlow, interconnectorCapacity);
326 if (reps.marketRepository.countAllElectricitySpotMarkets() < 2
327 || Math.abs(interconnectorFlow) + epsilon <= interconnectorCapacity) {
330 double supplyInThisMarket = globalOutcome.supplies.get(market);
334 if (globalOutcome.globalLoad <= globalOutcome.globalSupply + epsilon) {
335 globalOutcome.globalPrice = marginalPlantMarginalCost;
337 globalOutcome.globalPrice = market.getValueOfLostLoad();
340 double interconenctorFlowForCurrentMarket = market.equals(firstMarket) ? interconnectorFlow * (-1.0)
341 : interconnectorFlow;
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);
353 MarketSegmentClearingOutcome marketOutcomes =
new MarketSegmentClearingOutcome();
355 marketOutcomes.supplies.put(m, 0d);
356 marketOutcomes.prices.put(m, m.getValueOfLostLoad());
360 logger.info(
"There should be multiple prices, but first we should do market coupling.");
362 boolean firstImporting =
true;
363 if (interconnectorFlow > 0) {
364 firstImporting =
false;
369 boolean first = market.equals(firstMarket);
373 if ((first && firstImporting) || (!first && !firstImporting)) {
374 marketOutcomes.loads.put(market, globalOutcome.loads.get(market) - interconnectorCapacity);
376 marketOutcomes.loads.put(market, globalOutcome.loads.get(market) + interconnectorCapacity);
383 clearTwoInterconnectedMarketsGivenAnInterconnectorAdjustedLoad(segment, marketOutcomes, clearingTick,
389 if (marketOutcomes.supplies.get(market) + epsilon < marketOutcomes.loads.get(market)) {
390 marketOutcomes.prices.put(market, market.getValueOfLostLoad());
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));
408 @SuppressWarnings(
"unused")
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,
418 submitOffersToElectricitySpotMarketRole.updateMarginalCostInclCO2AfterFuelMixChange(co2Price,
419 nationalMinCo2Prices, clearingTick, forecast, fuelPriceMap);
421 if (model.isLongTermContractsImplemented())
422 determineCommitmentOfPowerPlantsOnTheBasisOfLongTermContracts(segments, forecast);
424 for (
Segment segment : segments) {
425 clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForOneSegment(
426 interconnector.getCapacity(clearingTick),
427 segment, government, clearingTick, forecast, demandGrowthMap);
431 void clearTwoInterconnectedMarketsGivenAnInterconnectorAdjustedLoad(
Segment segment,
432 MarketSegmentClearingOutcome marketOutcomes,
long clearingTick,
boolean forecast) {
435 .findSortedPowerPlantDispatchPlansForSegmentForTime(segment, clearingTick, forecast)) {
440 double plantSupply = determineProductionOnSpotMarket(plan, marketOutcomes.supplies.get(myMarket),
441 marketOutcomes.loads.get(myMarket));
442 if (plantSupply > 0) {
445 marketOutcomes.supplies.put(myMarket, marketOutcomes.supplies.get(myMarket) + plantSupply);
446 marketOutcomes.prices.put(myMarket, plan.getPrice());
454 void clearCO2AndElectricitySpotMarketTwoCountryWithBanking(
DecarbonizationModel model,
boolean forecast,
455 long clearingTick, Map<Substance, Double> fuelPriceMap, Map<ElectricitySpotMarket, Double> demandGrowthMap) {
457 double previouslyBankedCertificates = reps.decarbonizationAgentRepository
458 .determineTotallyBankedCO2Certificates();
461 if (clearingTick < 1) {
462 clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(model, forecast, clearingTick,
463 fuelPriceMap, demandGrowthMap, 0);
466 CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
468 Interconnector interconnector = template.findAll(Interconnector.class).iterator().next();
470 Government government = template.findAll(Government.class).iterator().next();
473 List<Segment> segments = Utils.asList(reps.segmentRepository.findAll());
475 ClearingPoint forecastedCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(
476 co2Auction, clearingTick + model.getCentralForecastingYear(),
true);
477 ClearingPoint lastCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(co2Auction,
478 clearingTick - 1,
false);
480 double maximumEnergyProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(clearingTick,
481 clearingTick + model.getCentralForecastingYear(), government, model);
483 CO2SecantSearch co2SecantSearch = clearIterativeCO2AndElectricitySpotMarketTwoCountryForTimestepAndFuelPrices(
484 model, forecast, clearingTick, fuelPriceMap, demandGrowthMap, 0);
486 double fundamentalCO2Price = calculateFundamentalCO2PriceForEnergyProducers(clearingTick, model, co2Auction,
488 logger.warn(
"Fundanmental price: {}", fundamentalCO2Price);
491 Map<ElectricitySpotMarket, Double> nationalMinCo2Prices =
new HashMap<ElectricitySpotMarket, Double>(2);
492 Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
494 if (model.isCo2TradingImplemented()) {
495 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
496 .getMinNationalCo2PriceTrend().getValue(clearingTick));
498 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
502 double adjustedFundamentalCO2Price = fundamentalCO2Price
503 * calculateCO2PriceReductionFactor(government, maximumEnergyProducerBanking,
504 previouslyBankedCertificates, 10, clearingTick);
506 double previousAdjustedFundamentalCO2Price = adjustedFundamentalCO2Price;
508 double deltaBankedEmissionCertificates;
514 previousAdjustedFundamentalCO2Price = adjustedFundamentalCO2Price;
516 clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick, forecast,
517 demandGrowthMap, fuelPriceMap, adjustedFundamentalCO2Price, nationalMinCo2Prices, segments,
518 interconnector, model);
520 co2emissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(forecast, clearingTick);
522 deltaBankedEmissionCertificates = government.getCo2Cap(clearingTick) - co2emissions;
523 adjustedFundamentalCO2Price = fundamentalCO2Price
524 * calculateCO2PriceReductionFactor(government, maximumEnergyProducerBanking,
525 previouslyBankedCertificates, 10, clearingTick);
528 adjustedFundamentalCO2Price = fundamentalCO2Price
529 * calculateCO2PriceReductionFactor(government, maximumEnergyProducerBanking,
530 previouslyBankedCertificates + deltaBankedEmissionCertificates, 10, clearingTick);
532 logger.warn(
"CO2 Fundamental Price iteration " + i +
", Old: {}, New:{}",
533 previousAdjustedFundamentalCO2Price, adjustedFundamentalCO2Price);
535 }
while (Math.abs(adjustedFundamentalCO2Price - previousAdjustedFundamentalCO2Price) > 1);
537 if (previouslyBankedCertificates + deltaBankedEmissionCertificates > 0) {
539 reps.clearingPointRepositoryOld.createOrUpdateClearingPoint(co2Auction, adjustedFundamentalCO2Price,
540 co2emissions, clearingTick, forecast);
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);
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);
562 public void clearIterativeCO2ElectricitySpotMarketAndFutureMarketTwoCountryForTimestepAndFuelPrices(
566 model = template.findAll(DecarbonizationModel.class).iterator().next();
568 Map<Substance, Double> fuelPriceMap =
new HashMap<Substance, Double>();
570 fuelPriceMap.put(substance, findLastKnownPriceForSubstance(substance));
573 Map<Substance, Double> futureFuelPriceMap = predictFuelPrices(model.getCentralForecastBacklookingYears(),
574 clearingTick + model.getCentralForecastingYear());
576 Map<ElectricitySpotMarket, Double> futureDemandGrowthMap = predictDemand(
577 model.getCentralForecastBacklookingYears(), clearingTick + model.getCentralForecastingYear());
580 Map<ElectricitySpotMarket, Double> futureNationalMinCo2Prices =
new HashMap<ElectricitySpotMarket, Double>(2);
581 Iterable<NationalGovernment> nationalGovernments = template.findAll(NationalGovernment.class);
583 if (model.isCo2TradingImplemented()) {
584 futureNationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG),
585 nG.getMinNationalCo2PriceTrend().getValue(clearingTick + model.getCentralForecastingYear()));
587 futureNationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG),
596 logger.info(
"Clearing the CO2 and electricity spot markets using iteration for 2 countries ");
601 Interconnector interconnector = template.findAll(Interconnector.class).iterator().next();
604 List<Segment> segments = Utils.asList(reps.segmentRepository.findAll());
607 Government government = template.findAll(Government.class).iterator().next();
611 Map<ElectricitySpotMarket, Double> nationalMinCo2Prices =
new HashMap<ElectricitySpotMarket, Double>(2);
612 nationalGovernments = template.findAll(NationalGovernment.class);
614 if (model.isCo2TradingImplemented()) {
615 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), nG
616 .getMinNationalCo2PriceTrend().getValue(clearingTick));
618 nationalMinCo2Prices.put(reps.marketRepository.findElectricitySpotMarketByNationalGovernment(nG), 0d);
623 CO2Auction co2Auction = template.findAll(CO2Auction.class).iterator().next();
624 CO2SecantSearch co2SecantSearch = null;
626 double previouslyBankedCertificates = reps.decarbonizationAgentRepository
627 .determineTotallyBankedCO2Certificates();
629 double targetEnergyProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(clearingTick,
630 clearingTick + model.getCentralForecastingYear(), government, model);
631 logger.warn(
"Hedging Target: {}, Relative Hedging: {}", targetEnergyProducerBanking, targetEnergyProducerBanking/government.getCo2Cap(getCurrentTick()));
633 double deltaBankedEmissionCertificateToReachBankingTarget = (targetEnergyProducerBanking - previouslyBankedCertificates)
634 / model.getCentralCO2TargetReversionSpeedFactor();
636 double deltaBankedEmissionCertificates;
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;
648 double futureCO2Price = 0;
650 double currentEmissions = 0;
651 double futureEmissions = 0;
652 double co2CapAdjustment = 0;
654 double averageCO2PriceOfLastTwoYears = reps.clearingPointRepository
655 .calculateAverageClearingPriceForMarketAndTimeRange(co2Auction, getCurrentTick() - 2,
656 getCurrentTick() - 1,
false);
658 determineFuelMixRole.determineFuelMixForecastForYearAndFuelPriceMap(clearingTick, futureFuelPriceMap,
659 futureNationalMinCo2Prices);
661 submitOffersToElectricitySpotMarketRole.createOffersForElectricitySpotMarket(null,
662 clearingTick + model.getCentralForecastingYear(),
true, futureFuelPriceMap);
666 ClearingPoint lastClearingPointOfCo2Market = reps.clearingPointRepositoryOld.findClearingPointForMarketAndTime(
667 co2Auction, getCurrentTick() - 1,
false);
668 if (lastClearingPointOfCo2Market != null) {
669 co2SecantSearch.co2Emissions = lastClearingPointOfCo2Market.getVolume();
671 co2SecantSearch.co2Emissions = 0d;
674 boolean emergencyPriceTriggerActive =
false;
675 int breakOffIterator = 0;
676 double emergencyAllowancesToBeReleased = 0;
677 while (breakOffIterator < 2 || !co2SecantSearch.stable) {
679 if (breakOffIterator > 15) {
680 logger.warn(
"Iteration cancelled, last found CO2 Price is used.");
684 futureCO2Price = co2SecantSearch.co2Price
685 * Math.pow(1 + model.getCentralPrivateDiscountingRate(), model.getCentralForecastingYear());
687 clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government,
688 clearingTick + model.getCentralForecastingYear(),
true, futureDemandGrowthMap, futureFuelPriceMap,
689 futureCO2Price, futureNationalMinCo2Prices, segments, interconnector, model);
690 futureEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(
true,
691 clearingTick + model.getCentralForecastingYear());
693 clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick,
false, null,
694 fuelPriceMap, co2SecantSearch.co2Price, nationalMinCo2Prices, segments, interconnector, model);
695 currentEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(
false, clearingTick);
703 deltaBankedEmissionCertificates = government.getCo2Cap(clearingTick) - currentEmissions;
705 co2SecantSearch = co2PriceSecantSearchUpdateWithCO2Banking(co2SecantSearch, model, government,
706 clearingTick, -deltaBankedEmissionCertificateToReachBankingTarget, currentEmissions,
707 futureEmissions, previouslyBankedCertificates, averageCO2PriceOfLastTwoYears);
713 if (!co2SecantSearch.stable) {
714 targetEnergyProducerBanking = calculateTargetCO2EmissionBankingOfEnergyProducers(currentEmissions,
715 futureEmissions, model);
720 deltaBankedEmissionCertificateToReachBankingTarget = (targetEnergyProducerBanking - previouslyBankedCertificates)
721 / model.getCentralCO2TargetReversionSpeedFactor();
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()
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));
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);
759 if (model.getCentralCO2BackSmoothingFactor() != 0) {
761 Iterable<ClearingPoint> clearingPoints = reps.clearingPointRepository
762 .findAllClearingPointsForMarketAndTimeRange(co2Auction,
763 clearingTick - model.getCentralForecastBacklookingYears(), clearingTick,
false);
765 double averagePastCO2Price = 0;
768 averagePastCO2Price = (cp.getPrice()
769 / Math.pow(1 + model.getCentralPrivateDiscountingRate(), clearingTick - cp.getTime()) + i
770 * averagePastCO2Price)
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());
788 clearOneOrTwoConnectedElectricityMarketsAtAGivenCO2PriceForSegments(government, clearingTick,
false,
789 null, fuelPriceMap, co2SecantSearch.co2Price, nationalMinCo2Prices, segments, interconnector,
791 currentEmissions = determineTotalEmissionsBasedOnPowerPlantDispatchPlan(
false, clearingTick);
793 co2SecantSearch.co2Price = oldCO2Price;
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,
802 clearingTick + model.getCentralForecastingYear(),
true);
804 if (co2SecantSearch.co2Price > co2SecantSearch.bankingEffectiveMinimumPrice)
805 deltaBankedEmissionCertificates = government.getCo2Cap(clearingTick) - currentEmissions;
807 deltaBankedEmissionCertificates = Math.min(deltaBankedEmissionCertificateToReachBankingTarget,
808 government.getCo2Cap(clearingTick) - currentEmissions);
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)
818 producer.setCo2Allowances(bankedEmissionsOfProducer);
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);
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);
844 return priceInterpolation;
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());
857 return volumeInterpolation;
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));
870 double targetBankedEmissions = model.getStabilityReserveBankingFirstYear() * volumeInterpolation[0]
871 + model.getStabilityReserveBankingSecondYear() * volumeInterpolation[1]
872 + model.getStabilityReserveBankingThirdYear()
873 * volumeInterpolation[2];
875 return targetBankedEmissions;
878 double calculateTargetCO2EmissionBankingOfEnergyProducers(
double currentEmissions,
double futureEmissions,
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);
888 double targetBankedEmissions = model.getStabilityReserveBankingFirstYear() * volumeInterpolation[0]
889 + model.getStabilityReserveBankingSecondYear() * volumeInterpolation[1]
890 + model.getStabilityReserveBankingThirdYear()
891 * volumeInterpolation[2];
893 return targetBankedEmissions;
896 double calculateFundamentalCO2PriceForEnergyProducers(
long clearingTick,
DecarbonizationModel model,
897 CO2Auction co2Auction, CO2SecantSearch co2SecantSearch) {
899 ClearingPoint forecastedCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(
900 co2Auction, clearingTick + model.getCentralForecastingYear(),
true);
902 if (co2SecantSearch == null)
903 lastCO2Clearing = reps.clearingPointRepository.findClearingPointForMarketAndTime(co2Auction,
904 clearingTick - 1,
false);
907 lastCO2Clearing.setPrice(co2SecantSearch.co2Price);
908 lastCO2Clearing.setVolume(co2SecantSearch.co2Emissions);
909 lastCO2Clearing.setTime(clearingTick);
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];
923 fundamentalPrice = fundamentalPrice / totalEmissions;
924 return fundamentalPrice;
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);
933 double co2PriceReductionFactor = 1 - ((bankedCertificates - maximumEnergyProducerBanking) / allowedEmissionsInFuture);
934 return co2PriceReductionFactor;
937 double calculateBankingEffectiveMinCO2Price(Map<ElectricitySpotMarket, Double> nationalMinCo2Prices,
939 double currentMinimumPrice = Collections.min(nationalMinCo2Prices.values());
947 return currentMinimumPrice;
950 CO2SecantSearch co2PriceSecantSearchUpdateWithCO2Banking(CO2SecantSearch co2SecantSearch,
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);
958 double futureCap = government.getCo2Cap(clearingTick + model.getCentralForecastingYear());
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,
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;
981 double deviation = (co2SecantSearch.co2Emissions - co2Cap) / co2Cap;
983 if (co2SecantSearch.co2Price == co2SecantSearch.bankingEffectiveMinimumPrice
984 && co2SecantSearch.co2Emissions < co2Cap) {
985 co2SecantSearch.stable =
true;
986 return co2SecantSearch;
990 if (Math.abs(deviation) < capDeviationCriterion
991 && co2SecantSearch.co2Price > co2SecantSearch.bankingEffectiveMinimumPrice) {
993 co2SecantSearch.stable =
true;
994 return co2SecantSearch;
999 if (co2SecantSearch.tooHighEmissionsPair != null && co2SecantSearch.tooLowEmissionsPair != null) {
1000 co2SecantSearch.twoPricesExistWithBelowAboveEmissions =
true;
1001 }
else if (co2SecantSearch.co2Price == government.getMinCo2Price(clearingTick)
1002 && co2SecantSearch.co2Emissions < co2Cap) {
1006 co2SecantSearch.stable =
true;
1007 return co2SecantSearch;
1008 }
else if (co2SecantSearch.co2Price >= government.getCo2Penalty(clearingTick)
1009 && co2SecantSearch.co2Emissions >= co2Cap) {
1013 co2SecantSearch.co2Price = government.getCo2Penalty(clearingTick);
1014 co2SecantSearch.stable =
true;
1015 return co2SecantSearch;
1023 if (co2SecantSearch.twoPricesExistWithBelowAboveEmissions) {
1026 if (deviation > 0) {
1027 co2SecantSearch.tooHighEmissionsPair.price = co2SecantSearch.co2Price;
1028 co2SecantSearch.tooHighEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1030 co2SecantSearch.tooLowEmissionsPair.price = co2SecantSearch.co2Price;
1031 co2SecantSearch.tooLowEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1034 double p2 = co2SecantSearch.tooHighEmissionsPair.price;
1035 double p1 = co2SecantSearch.tooLowEmissionsPair.price;
1036 double e2 = co2SecantSearch.tooHighEmissionsPair.emission;
1037 double e1 = co2SecantSearch.tooLowEmissionsPair.emission;
1040 if (co2SecantSearch.iteration < 5) {
1041 co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
1042 co2SecantSearch.iteration++;
1046 co2SecantSearch.co2Price = (p1 + p2) / 2;
1047 co2SecantSearch.iteration = 0;
1054 if (deviation > 0) {
1055 if (co2SecantSearch.tooHighEmissionsPair == null)
1056 co2SecantSearch.tooHighEmissionsPair =
new PriceEmissionPair();
1058 co2SecantSearch.tooHighEmissionsPair.price = co2SecantSearch.co2Price;
1059 co2SecantSearch.tooHighEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
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;
1068 double p2 = co2SecantSearch.tooHighEmissionsPair.price;
1069 double p1 = co2SecantSearch.tooLowEmissionsPair.price;
1070 double e2 = co2SecantSearch.tooHighEmissionsPair.emission;
1071 double e1 = co2SecantSearch.tooLowEmissionsPair.emission;
1073 co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
1074 co2SecantSearch.iteration++;
1081 if (co2SecantSearch.tooLowEmissionsPair == null)
1082 co2SecantSearch.tooLowEmissionsPair =
new PriceEmissionPair();
1084 co2SecantSearch.tooLowEmissionsPair.price = co2SecantSearch.co2Price;
1085 co2SecantSearch.tooLowEmissionsPair.emission = co2SecantSearch.co2Emissions - co2Cap;
1087 if (co2SecantSearch.tooHighEmissionsPair == null) {
1088 co2SecantSearch.co2Price = Math.max((co2SecantSearch.co2Price / 2),
1089 co2SecantSearch.bankingEffectiveMinimumPrice);
1093 double p2 = co2SecantSearch.tooHighEmissionsPair.price;
1094 double p1 = co2SecantSearch.tooLowEmissionsPair.price;
1095 double e2 = co2SecantSearch.tooHighEmissionsPair.emission;
1096 double e1 = co2SecantSearch.tooLowEmissionsPair.emission;
1098 co2SecantSearch.co2Price = p1 - (e1 * (p2 - p1) / (e2 - e1));
1101 co2SecantSearch.iteration++;
1105 if (co2SecantSearch.co2Price < 0.5
1106 || co2SecantSearch.co2Price - government.getMinCo2Price(clearingTick) < 0.5) {
1107 co2SecantSearch.stable =
true;
1114 return co2SecantSearch;
1118 double calculateExpectedBankedCertificates(
double currentEmissions,
double futureEmissions,
double currentCap,
1119 double futureCap,
double currentlyBankedEmissions,
long centralForecastingYear) {
1120 double expectedBankedCertificates = currentlyBankedEmissions + currentCap - currentEmissions + futureCap
1122 expectedBankedCertificates = 1 / 3.0
1123 * expectedBankedCertificates
1124 + 2 / 3.0 * currentlyBankedEmissions;
1125 return expectedBankedCertificates;
1130 public Reps getReps() {
Iterable< ElectricitySpotMarket > findAllElectricitySpotMarkets()