EMlab-generation Documentation  1.0
Documentation of the EMLab-Generation model.
SelectLongTermElectricityContractsRole.java
1 /*******************************************************************************
2  * Copyright 2012 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 package emlab.gen.role.market;
17 
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.TreeMap;
23 
24 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.transaction.annotation.Transactional;
26 
27 import agentspring.role.AbstractRole;
28 import agentspring.role.Role;
29 import agentspring.role.RoleComponent;
44 
54 @RoleComponent
56 AbstractRole<EnergyConsumer> implements Role<EnergyConsumer> {
57 
58  @Autowired
59  Reps reps;
60 
61  HashMap<Zone, ExistingContractsInformation> existingContractsInformations;
62 
63  @Override
64  @Transactional
65  public void act(EnergyConsumer consumer) {
66 
67  logger.info("{} will now accept offers or not", consumer);
68  existingContractsInformations = new HashMap<Zone, ExistingContractsInformation>();
69 
70  // Make an overview of the capacity of existing contracts per ltc type.
71  // Store that in a nested info class.
72  for (Zone zone : reps.genericRepository.findAll(Zone.class)) {
73  ExistingContractsInformation info = new ExistingContractsInformation(
74  zone, consumer);
75  existingContractsInformations.put(zone, info);
76  info.updateExisingContractsInformation();
77  }
78 
79  // Rank the offers, by multiplying by better factors,
80  // TODO based on past performance??
81  Map<LongTermContractOffer, Double> offersUnsorted = new HashMap<LongTermContractOffer, Double>();
82 
83  // Adjust the price based on duration of the offer
84  for (LongTermContractOffer offer : findLongTermContractOffersActiveAtTime()) {
85  double duration = offer.getDuration().getDuration();
86  double thisPrice = offer.getPrice() * (1 + ((duration-1)*consumer.getContractDurationPreferenceFactor()));
87  offersUnsorted.put(offer, thisPrice);
88  }
89 
90  @SuppressWarnings("unused")
91  MapValueComparator comp = new MapValueComparator(offersUnsorted);
92  Map<LongTermContractOffer, Double> offersRanked = new TreeMap<LongTermContractOffer, Double>(
93  comp);
94  offersRanked.putAll(offersUnsorted);
95 
96  for (LongTermContractOffer offer : offersRanked.keySet()) {
97 
98  // is this offer still valid (i.e. no other conflicting offer been
99  // accepted?)
100  boolean stillvalid = true;
101 
102  // if a similar offer has been accepted (same power plant)
103  // we have to ignore this offer
104  if (reps.contractRepository
105  .findLongTermContractForPowerPlantActiveAtTime(
106  offer.getUnderlyingPowerPlant(), getCurrentTick()) != null) {
107  stillvalid = false;
108  } else {
109  // check whether there is load to meet this type.
110  double volumeInContractTypePossible = consumer
111  .getLtcMaximumCoverageFraction()
112  * determineVolumeForContractType(
113  offer.getLongTermContractType(),
114  offer.getZone());
115  // check what the capacity is of existing contracts for this ltc
116  // type.
117  double volumeInCurrentContracts = existingContractsInformations
118  .get(offer.getZone()).capacityContractedInZonesForLTCType
119  .get(offer.getLongTermContractType());
120  if (volumeInCurrentContracts + offer.getCapacity() > volumeInContractTypePossible) {
121  logger.info("Contract impossible for {}", offer
122  .getLongTermContractType().getName());
123  stillvalid = false;
124  } else {
125  logger.info("Contract possible for {}", offer
126  .getLongTermContractType().getName());
127  }
128  logger.info(
129  "Volume in current contracts: {}, while possible for load: {}",
130  volumeInCurrentContracts, volumeInContractTypePossible);
131  }
132 
133  if (stillvalid) {
134  // It is possible, but do we want it?
135  // determine the weighted average spot price for this contract
136  // if it is then adjusted price, make a contract.
137  double hours = 0d;
138  double weightedElectricitySpotPrices = 0d;
139  for (Segment s : offer.getLongTermContractType().getSegments()) {
140  hours += s.getLengthInHours();
141 
142  SegmentClearingPoint point = (SegmentClearingPoint) reps.clearingPointRepositoryOld
143  .findClearingPointForSegmentAndTime(s,
144  getCurrentTick() - 1, false);
145  weightedElectricitySpotPrices += point.getPrice()
146  * s.getLengthInHours();
147  logger.info("Found a clearing point {} for segment {}",
148  point, s);
149  }
150 
151  double averageElectricityPrice = weightedElectricitySpotPrices
152  / hours;
153 
154  double price = offersUnsorted.get(offer);
155  if (price < (averageElectricityPrice*consumer.getContractWillingnessToPayFactor())) {
156  reps.contractRepository.submitLongTermContractForElectricity(
157  offer.getUnderlyingPowerPlant(), offer
158  .getUnderlyingPowerPlant().getOwner(),
159  consumer, offer.getUnderlyingPowerPlant()
160  .getLocation().getZone(), offer.getPrice(),
161  offer.getUnderlyingPowerPlant()
162  .getAvailableCapacity(getCurrentTick()),
163  offer.getLongTermContractType(), getCurrentTick(),
164  offer.getDuration(), true, offer.getMainFuel(),
165  offer.getFuelPassThroughFactor(), offer
166  .getCo2PassThroughFactor(), offer
167  .getFuelPriceStart(), offer
168  .getCo2PriceStart());
169  logger.info(
170  "Accepted LTC offer type {}, duration {}, submitted contract",
171  offer.getLongTermContractType(),
172  offer.getDuration());
173  logger.warn(
174  "Accepted LTC for powerplant {}, price {} euro/MWh",
175  offer.getUnderlyingPowerPlant(), offer.getPrice());
176 
177  // Update the info on existing contracts for this zone
178  existingContractsInformations.get(
179  offer.getUnderlyingPowerPlant().getLocation()
180  .getZone())
181  .updateExisingContractsInformation();
182 
183  }
184 
185  }
186  }
187  }
188 
189  public double determineVolumeForContractType(LongTermContractType type,
190  Zone zone) {
191 
192  // calculate minimum load of the segments in this contract type
193  double minimumLoadInSegmentsOfContractType = Double.MAX_VALUE;
194  for (Segment segment : type.getSegments()) {
195  double loadOfSegment = reps.marketRepository
196  .findSegmentLoadForElectricitySpotMarketForZone(zone,
197  segment).getBaseLoad()
198  * reps.marketRepository.findElectricitySpotMarketForZone(zone)
199  .getDemandGrowthTrend().getValue(getCurrentTick());
200  if (loadOfSegment < minimumLoadInSegmentsOfContractType) {
201  minimumLoadInSegmentsOfContractType = loadOfSegment;
202  }
203  }
204  logger.info(
205  "For ltc type {}, the lowest load of the segments covered is {}",
206  type, minimumLoadInSegmentsOfContractType);
207  return minimumLoadInSegmentsOfContractType;
208  }
209 
210  public Iterable<LongTermContractOffer> findLongTermContractOffersActiveAtTime() {
211 
212  List<LongTermContractOffer> list = new ArrayList<LongTermContractOffer>();
213  for (LongTermContractOffer ltcOffer : reps.genericRepository
214  .findAll(LongTermContractOffer.class)) {
215  // If active
216  if (ltcOffer.getStart() == getCurrentTick()) {
217  list.add(ltcOffer);
218  }
219  }
220  return list;
221  }
222 
229  private class ExistingContractsInformation {
230  private final Zone zone;
231  private final EnergyConsumer consumer;
232  private final HashMap<LongTermContractType, Double> capacityContractedInZonesForLTCType;
233 
234  public ExistingContractsInformation(Zone zone, EnergyConsumer consumer) {
235  this.zone = zone;
236  this.consumer = consumer;
237  capacityContractedInZonesForLTCType = new HashMap<LongTermContractType, Double>();
238  }
239 
240  public void updateExisingContractsInformation() {
241 
242  for (LongTermContractType type : reps.genericRepository
243  .findAll(LongTermContractType.class)) {
244  capacityContractedInZonesForLTCType
245  .put(type,
246  findCapacityOfLongTermContractsForEnergyConsumerAlreadyActiveAtTimeForSegmentsForZone(
247  consumer, type));
248  }
249  }
250 
251  private double findCapacityOfLongTermContractsForEnergyConsumerAlreadyActiveAtTimeForSegmentsForZone(
252  EnergyConsumer consumer, LongTermContractType type) {
253 
254  double maxCapacity = 0d;
255  for (Segment segment : type.getSegments()) {
256  double thisCapacity = 0d;
257  for (Contract c : reps.contractRepository
258  .findLongTermContractsForEnergyConsumerForSegmentForZoneActiveAtTime(
259  consumer, segment, zone, getCurrentTick())) {
261  thisCapacity += ltc.getCapacity();
262  logger.info(
263  "Found existing contract {} active in segment {}",
264  ltc, segment);
265  }
266  // More contracts for this segment? Keep track of the largest
267  // contracted capacity of each of the valid segments
268  if (thisCapacity > maxCapacity) {
269  maxCapacity = thisCapacity;
270  }
271  }
272  return maxCapacity;
273  }
274  }
275 }