com.almende.dsol.example.datacenters.Datacenter.java Source code

Java tutorial

Introduction

Here is the source code for com.almende.dsol.example.datacenters.Datacenter.java

Source

/* $Id: 20f5277bcad5c48c48adb1e45a93fe8ca96f0cbf $
 * 
 * @license
 * 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 com.almende.dsol.example.datacenters;

import io.coala.dsol.util.DsolUtil;
import io.coala.log.LogUtil;

import java.rmi.RemoteException;
import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

import nl.tudelft.simulation.dsol.SimRuntimeException;
import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
import nl.tudelft.simulation.event.EventInterface;
import nl.tudelft.simulation.event.EventListenerInterface;
import nl.tudelft.simulation.jstats.distributions.DistConstant;
import nl.tudelft.simulation.jstats.distributions.DistContinuous;
import nl.tudelft.simulation.jstats.streams.StreamInterface;

import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.Interval;

/**
 * Datacenter
 */
public class Datacenter extends AbstractFederationModelComponent implements Comparable<Datacenter> {

    /** */
    private static final long serialVersionUID = 1L;

    /** */
    private static final Logger LOG = LogUtil.getLogger(Datacenter.class);

    /** */
    public static int ID_COUNT = 0;

    /** DC Energy Emissions (kg) per kWh */
    private final double emissionsPerKWh;// 1.66d, 5.0, 8.33

    /** DC Energy Costs (EUR) per kWh */
    private final double cashPerKWh;// = 0.1

    /** service power (Watt) = energy (J) over time (s) */
    private final DistContinuous consumptionKW;

    /** service cash flow (EUR) per hour */
    private final DistContinuous hourlyCashflow;

    /** DC cash flow (EUR) */
    private final Accumulator cashflow;

    /** DC emissions (kg CO2) */
    private final Accumulator emissionsKgCO2;

    /** DC power consumption (KWh) */
    private final Accumulator consumptionKWh;

    /** DC current service count */
    private final Indicator currentServiceCount;

    /** DC total service count */
    private final Indicator totalServiceCount;

    /** DC total service count */
    public final Indicator exchangedServiceCount;

    /** DC total service count */
    public final Indicator workloadHours;

    /** DC total service count */
    public final Indicator adaptionHours;

    /** */
    public final Indicator emissionFactor;

    /** */
    public final Indicator consumptionFactor;

    /** */
    protected final NavigableMap<DateTime, Interval> outsourceSchedule = new ConcurrentSkipListMap<>();

    /** */
    protected final NavigableMap<DateTime, Interval> insourceSchedule = new ConcurrentSkipListMap<>();

    /**
     * @param model
     * @throws RemoteException
     * @throws SimRuntimeException
     */
    public Datacenter(final FederationModel model) throws Exception {
        super(model, "DC" + ID_COUNT++);

        final String indicatorName = getName();
        this.cashflow = new Accumulator(0d, model, "Cash (flow)", "Money (EUR)", indicatorName, 0d,
                TimeUnitInterface.HOUR);

        this.emissionsKgCO2 = new Accumulator(0d, model, "Carbon Emissions (rate)", "CO2 (kg/kW)", indicatorName,
                0d, TimeUnitInterface.HOUR);

        // idle (kW) 20 servers*100 watt(idle)=2 kw (idle * consumption)
        this.consumptionKWh = new Accumulator(0d, model, "Energy Consumption (rate)", "Energy (kW)", indicatorName,
                2d, TimeUnitInterface.HOUR);

        this.currentServiceCount = new Indicator(model, "Current Services", "Services (#)", indicatorName, 0d);

        this.totalServiceCount = new Indicator(model, "Total Services", "Services (#)", indicatorName, 0d);

        this.exchangedServiceCount = new Indicator(model, "Exchanges (#)", "Exchanges (#)", indicatorName, 0d);

        this.workloadHours = new Indicator(model, "Workload Duration", "Hours (#)", indicatorName, 0d);

        this.adaptionHours = new Indicator(model, "Adaption Duration", "Hours (#)", indicatorName, 0d);

        this.emissionFactor = new Indicator(model, "Power Mix Variation", "Carbon Emission factor", indicatorName,
                1d);

        this.consumptionFactor = new Indicator(model, "Power Adaption", "Energy Consumption factor", indicatorName,
                1d);

        final StreamInterface stream = model.getSimulator().getReplication().getStream(FederationModel.RNG_ID);

        this.cashPerKWh = 0.1d;// new DistConstant(stream, .1d);
        this.emissionsPerKWh = 5.0d;// new DistConstant(stream, 5.0d);
        this.consumptionKW = new DistConstant(stream, .1d);
        this.hourlyCashflow = new DistConstant(stream, .1d);

        // add demand and listen for new demand events
        final DemandGenerator demand = new DemandGenerator(model);
        demand.addListener(new EventListenerInterface() {

            @Override
            public void notify(final EventInterface event) throws RemoteException {
                try {
                    newService((Interval) event.getContent());
                } catch (final SimRuntimeException e) {
                    LOG.error("Problem scheduling new service", e);
                }
            }
        }, DemandGenerator.NEW_DEMAND);
    }

    /* @see FederationModelComponent#getName() */
    @Override
    public String toString() {
        return getName();
    }

    public Accumulator getCashflow() {
        return this.cashflow;
    }

    public Accumulator getEmissionsKgCO2() {
        return this.emissionsKgCO2;
    }

    public Accumulator getConsumptionKWh() {
        return this.consumptionKWh;
    }

    public Indicator getCurrentServiceCount() {
        return this.currentServiceCount;
    }

    public Indicator getTotalServiceCount() {
        return this.totalServiceCount;
    }

    private boolean isSavingsMode(final DateTime date) {
        final Map.Entry<DateTime, Interval> lastBefore = this.outsourceSchedule.floorEntry(date);
        if (lastBefore != null && lastBefore.getValue().contains(date))
            return true;
        final Map.Entry<DateTime, Interval> firstAfter = this.outsourceSchedule.ceilingEntry(date);
        if (firstAfter != null && firstAfter.getValue().contains(date))
            return true;
        return false;
    }

    private boolean isExtraMode(final DateTime date) {
        final Map.Entry<DateTime, Interval> lastBefore = this.insourceSchedule.floorEntry(date);
        if (lastBefore != null && lastBefore.getValue().contains(date))
            return true;
        final Map.Entry<DateTime, Interval> firstAfter = this.insourceSchedule.ceilingEntry(date);
        if (firstAfter != null && firstAfter.getValue().contains(date))
            return true;
        return false;
    }

    protected void newService(final Interval interval) throws RemoteException, SimRuntimeException {
        if (getModel().hasRemoteAllocation()) {
            if (isSavingsMode(interval.getStart())) {
                if (getModel().hasBroker())
                    for (Datacenter dc : getModel().getDatacenters()) {
                        if (dc == this)
                            continue;

                        if (dc.isExtraMode(interval.getStart())) {
                            newService(interval, .1, .1);
                            dc.newService(interval, 1.0, .9);
                            // LOG.trace(getName() + " Outsourced to EXTRA dc "
                            // + interval);
                            return;
                        }
                    }
                for (Datacenter dc : getModel().getDatacenters()) {
                    if (dc == this)
                        continue;

                    if (!dc.isSavingsMode(interval.getStart())) {
                        newService(interval, .1, .1);
                        dc.newService(interval, 1.0, .9);
                        return;
                    }
                }

                LOG.trace(getName() + " Could not remotely allocate service " + interval);
            }
            // else
            // LOG.trace(getName()
            // + " Running locally during EXTRA mode, service: "
            // + interval);
        }
        // else
        // LOG.trace(getName()
        // + " Running locally during REGULAR mode, service: "
        // + interval);
        newService(interval, 1.0, 1.0);
        newService(interval, 0.0, 0.0);
    }

    protected void newService(final Interval interval, final double consumptionFactor, final double cashFlowFactor)
            throws RemoteException, SimRuntimeException {
        final Service service = new Service(getModel(), this, interval,
                this.consumptionKW.draw() * consumptionFactor, this.hourlyCashflow.draw() * cashFlowFactor);

        final Interval cropped = DsolUtil.crop(interval, getTreatment());
        this.workloadHours.addValue(DsolUtil.toTimeUnit(TimeUnitInterface.HOUR, cropped.toDurationMillis(),
                TimeUnitInterface.MILLISECOND));

        // listen for service start and stop events
        service.addListener(new EventListenerInterface() {

            @Override
            public void notify(final EventInterface event) {
                if (consumptionFactor == 0.0)
                    return;
                getCurrentServiceCount().addValue(1);
                getTotalServiceCount().addValue(1);
                if (consumptionFactor < 1.0)
                    exchangedServiceCount.addValue(1);
            }
        }, Service.SERVICE_STARTED);

        service.addListener(new EventListenerInterface() {

            @Override
            public void notify(final EventInterface event) {
                getCurrentServiceCount().addValue(-1);
            }
        }, Service.SERVICE_COMPLETED);

        service.addListener(new EventListenerInterface() {

            @Override
            public void notify(final EventInterface event) {
                final Number kW = (Number) event.getContent();
                getConsumptionKWh()
                        .addRate(kW.doubleValue() * Datacenter.this.consumptionFactor.getValue().doubleValue());
                getEmissionsKgCO2()
                        .addRate(kW.doubleValue() * Datacenter.this.consumptionFactor.getValue().doubleValue()
                                * Datacenter.this.emissionsPerKWh
                                * Datacenter.this.emissionFactor.getValue().doubleValue());
                getCashflow().addRate(kW.doubleValue() * Datacenter.this.cashPerKWh);
            }
        }, Service.CONSUMPTION_CHANGED);

        service.addListener(new EventListenerInterface() {

            @Override
            public void notify(final EventInterface event) {
                getCashflow().addRate((Number) event.getContent());
            }
        }, Service.CASHFLOW_CHANGED);

    }

    protected void newAdaption(final Interval interval, final double consumptionFactor)
            throws RemoteException, SimRuntimeException {
        if (consumptionFactor > 1.0)
            this.outsourceSchedule.put(interval.getStart(), interval);
        else if (consumptionFactor < 1.0)
            this.insourceSchedule.put(interval.getStart(), interval);

        final double startTime = simTime(interval.getStart());
        final double endTime = simTime(interval.getEnd());
        this.adaptionHours.addValue(DsolUtil.toTimeUnit(TimeUnitInterface.HOUR, interval.toDuration().getMillis(),
                TimeUnitInterface.MILLISECOND));
        getSimulator().scheduleEvent(new SimEvent(startTime, this, this, APPLY_CONSUMPTION_FACTOR_METHOD_ID,
                new Object[] { consumptionFactor, false }));
        getSimulator().scheduleEvent(new SimEvent(endTime, this, this, APPLY_CONSUMPTION_FACTOR_METHOD_ID,
                new Object[] { 1.0 / consumptionFactor, true }));
    }

    protected void newPowermix(final Interval interval, final double emissionFactor)
            throws RemoteException, SimRuntimeException {
        final double startTime = simTime(interval.getStart());
        final double endTime = simTime(interval.getEnd());
        getSimulator().scheduleEvent(new SimEvent(startTime, this, this, APPLY_EMISSION_FACTOR_METHOD_ID,
                new Object[] { emissionFactor, false }));
        getSimulator().scheduleEvent(new SimEvent(endTime, this, this, APPLY_EMISSION_FACTOR_METHOD_ID,
                new Object[] { 1.0 / emissionFactor, true }));
    }

    private static final Number ONE = 1.0d;

    private static final String APPLY_CONSUMPTION_FACTOR_METHOD_ID = "applyConsumptionFactor";

    protected void applyConsumptionFactor(final double consumptionFactor, final boolean reset) {
        getConsumptionKWh().multiplyRate(consumptionFactor);
        this.consumptionFactor.setValue(reset ? ONE : consumptionFactor);

        // TODO live (re)migration of some active workload?
    }

    private static final String APPLY_EMISSION_FACTOR_METHOD_ID = "applyEmissionFactor";

    protected void applyEmissionFactor(final double emissionFactor, final boolean reset) {
        getEmissionsKgCO2().multiplyRate(emissionFactor);
        this.emissionFactor.setValue(reset ? ONE : emissionFactor);
    }

    @Override
    public int compareTo(final Datacenter o) {
        return getName().compareTo(o.getName());
    }

}