emlab.role.market.SelectLongTermElectricityContractsRole.java Source code

Java tutorial

Introduction

Here is the source code for emlab.role.market.SelectLongTermElectricityContractsRole.java

Source

/*******************************************************************************
 * Copyright 2012 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package emlab.role.market;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import agentspring.role.AbstractRole;
import agentspring.role.Role;
import agentspring.role.RoleComponent;
import emlab.domain.agent.EnergyConsumer;
import emlab.domain.agent.EnergyProducer;
import emlab.domain.contract.Contract;
import emlab.domain.contract.LongTermContract;
import emlab.domain.contract.LongTermContractOffer;
import emlab.domain.contract.LongTermContractType;
import emlab.domain.gis.Zone;
import emlab.domain.market.Bid;
import emlab.domain.market.electricity.ElectricitySpotMarket;
import emlab.domain.market.electricity.Segment;
import emlab.domain.market.electricity.SegmentClearingPoint;
import emlab.domain.technology.PowerPlant;
import emlab.repository.Reps;
import emlab.util.MapValueComparator;

/**
 * {@link EnergyProducer} submits offers to the {@link ElectricitySpotMarket}.
 * One {@link Bid} per {@link PowerPlant}.
 * 
 * @author <a href="mailto:A.Chmieliauskas@tudelft.nl">Alfredas
 *         Chmieliauskas</a> @author <a
 *         href="mailto:E.J.L.Chappin@tudelft.nl">Emile Chappin</a>
 * 
 */
@RoleComponent
public class SelectLongTermElectricityContractsRole extends AbstractRole<EnergyConsumer>
        implements Role<EnergyConsumer> {

    @Autowired
    Reps reps;

    HashMap<Zone, ExistingContractsInformation> existingContractsInformations;

    @Transactional
    public void act(EnergyConsumer consumer) {

        logger.info("{} will now accept offers or not", consumer);
        existingContractsInformations = new HashMap<Zone, ExistingContractsInformation>();

        // Make an overview of the capacity of existing contracts per ltc type.
        // Store that in a nested info class.
        for (Zone zone : reps.genericRepository.findAll(Zone.class)) {
            ExistingContractsInformation info = new ExistingContractsInformation(zone, consumer);
            existingContractsInformations.put(zone, info);
            info.updateExisingContractsInformation();
        }

        // Rank the offers, by multiplying by better factors,
        // TODO based on past performance??
        Map<LongTermContractOffer, Double> offersUnsorted = new HashMap<LongTermContractOffer, Double>();

        // Adjust the price based on duration of the offer
        for (LongTermContractOffer offer : findLongTermContractOffersActiveAtTime()) {
            double duration = offer.getDuration().getDuration();
            double thisPrice = offer.getPrice()
                    * (1 + ((duration - 1) * consumer.getContractDurationPreferenceFactor()));
            offersUnsorted.put(offer, thisPrice);
        }

        @SuppressWarnings("unused")
        MapValueComparator comp = new MapValueComparator(offersUnsorted);
        Map<LongTermContractOffer, Double> offersRanked = new TreeMap<LongTermContractOffer, Double>(comp);
        offersRanked.putAll(offersUnsorted);

        for (LongTermContractOffer offer : offersRanked.keySet()) {

            // is this offer still valid (i.e. no other conflicting offer been
            // accepted?)
            boolean stillvalid = true;

            // if a similar offer has been accepted (same power plant)
            // we have to ignore this offer
            if (reps.contractRepository.findLongTermContractForPowerPlantActiveAtTime(
                    offer.getUnderlyingPowerPlant(), getCurrentTick()) != null) {
                stillvalid = false;
            } else {
                // check whether there is load to meet this type.
                double volumeInContractTypePossible = consumer.getLtcMaximumCoverageFraction()
                        * determineVolumeForContractType(offer.getLongTermContractType(), offer.getZone());
                // check what the capacity is of existing contracts for this ltc
                // type.
                double volumeInCurrentContracts = existingContractsInformations
                        .get(offer.getZone()).capacityContractedInZonesForLTCType
                                .get(offer.getLongTermContractType());
                if (volumeInCurrentContracts + offer.getCapacity() > volumeInContractTypePossible) {
                    logger.info("Contract impossible for {}", offer.getLongTermContractType().getName());
                    stillvalid = false;
                } else {
                    logger.info("Contract possible for {}", offer.getLongTermContractType().getName());
                }
                logger.info("Volume in current contracts: {}, while possible for load: {}",
                        volumeInCurrentContracts, volumeInContractTypePossible);
            }

            if (stillvalid) {
                // It is possible, but do we want it?
                // determine the weighted average spot price for this contract
                // if it is then adjusted price, make a contract.
                double hours = 0d;
                double weightedElectricitySpotPrices = 0d;
                for (Segment s : offer.getLongTermContractType().getSegments()) {
                    hours += s.getLengthInHours();

                    SegmentClearingPoint point = (SegmentClearingPoint) reps.clearingPointRepositoryOld
                            .findClearingPointForSegmentAndTime(s, getCurrentTick() - 1);
                    weightedElectricitySpotPrices += point.getPrice() * s.getLengthInHours();
                    logger.info("Found a clearing point {} for segment {}", point, s);
                }

                double averageElectricityPrice = weightedElectricitySpotPrices / hours;

                double price = offersUnsorted.get(offer);
                if (price < (averageElectricityPrice * consumer.getContractWillingnessToPayFactor())) {
                    reps.contractRepository.submitLongTermContractForElectricity(offer.getUnderlyingPowerPlant(),
                            offer.getUnderlyingPowerPlant().getOwner(), consumer,
                            offer.getUnderlyingPowerPlant().getLocation().getZone(), offer.getPrice(),
                            offer.getUnderlyingPowerPlant().getAvailableCapacity(getCurrentTick()),
                            offer.getLongTermContractType(), getCurrentTick(), offer.getDuration(), true,
                            offer.getMainFuel(), offer.getFuelPassThroughFactor(), offer.getCo2PassThroughFactor(),
                            offer.getFuelPriceStart(), offer.getCo2PriceStart());
                    logger.info("Accepted LTC offer type {}, duration {}, submitted contract",
                            offer.getLongTermContractType(), offer.getDuration());
                    logger.warn("Accepted LTC for powerplant {}, price {} euro/MWh",
                            offer.getUnderlyingPowerPlant(), offer.getPrice());

                    // Update the info on existing contracts for this zone
                    existingContractsInformations.get(offer.getUnderlyingPowerPlant().getLocation().getZone())
                            .updateExisingContractsInformation();

                }

            }
        }
    }

    public double determineVolumeForContractType(LongTermContractType type, Zone zone) {

        // calculate minimum load of the segments in this contract type
        double minimumLoadInSegmentsOfContractType = Double.MAX_VALUE;
        for (Segment segment : type.getSegments()) {
            double loadOfSegment = reps.marketRepository
                    .findSegmentLoadForElectricitySpotMarketForZone(zone, segment).getBaseLoad()
                    * reps.marketRepository.findElectricitySpotMarketForZone(zone).getDemandGrowthTrend()
                            .getValue(getCurrentTick());
            if (loadOfSegment < minimumLoadInSegmentsOfContractType) {
                minimumLoadInSegmentsOfContractType = loadOfSegment;
            }
        }
        logger.info("For ltc type {}, the lowest load of the segments covered is {}", type,
                minimumLoadInSegmentsOfContractType);
        return minimumLoadInSegmentsOfContractType;
    }

    public Iterable<LongTermContractOffer> findLongTermContractOffersActiveAtTime() {

        List<LongTermContractOffer> list = new ArrayList<LongTermContractOffer>();
        for (LongTermContractOffer ltcOffer : reps.genericRepository.findAll(LongTermContractOffer.class)) {
            // If active
            if (ltcOffer.getStart() == getCurrentTick()) {
                list.add(ltcOffer);
            }
        }
        return list;
    }

    /**
     * Contains the existing contracts information for a consumer for a zone.
     * 
     * @author ejlchappin
     * 
     */
    private class ExistingContractsInformation {
        private Zone zone;
        private EnergyConsumer consumer;
        private HashMap<LongTermContractType, Double> capacityContractedInZonesForLTCType;

        public ExistingContractsInformation(Zone zone, EnergyConsumer consumer) {
            this.zone = zone;
            this.consumer = consumer;
            capacityContractedInZonesForLTCType = new HashMap<LongTermContractType, Double>();
        }

        public void updateExisingContractsInformation() {

            for (LongTermContractType type : reps.genericRepository.findAll(LongTermContractType.class)) {
                capacityContractedInZonesForLTCType.put(type,
                        findCapacityOfLongTermContractsForEnergyConsumerAlreadyActiveAtTimeForSegmentsForZone(
                                consumer, type));
            }
        }

        private double findCapacityOfLongTermContractsForEnergyConsumerAlreadyActiveAtTimeForSegmentsForZone(
                EnergyConsumer consumer, LongTermContractType type) {

            double maxCapacity = 0d;
            for (Segment segment : type.getSegments()) {
                double thisCapacity = 0d;
                for (Contract c : reps.contractRepository
                        .findLongTermContractsForEnergyConsumerForSegmentForZoneActiveAtTime(consumer, segment,
                                zone, getCurrentTick())) {
                    LongTermContract ltc = (LongTermContract) c;
                    thisCapacity += ltc.getCapacity();
                    logger.info("Found existing contract {} active in segment {}", ltc, segment);
                }
                // More contracts for this segment? Keep track of the largest
                // contracted capacity of each of the valid segments
                if (thisCapacity > maxCapacity) {
                    maxCapacity = thisCapacity;
                }
            }
            return maxCapacity;
        }
    }
}