Source code for server.forecasting.optimizing.auto_optimization

"""
This module contains the algorithm for optimizing the costs of energy systems.

"""
from datetime import datetime
from scipy.optimize import fmin_l_bfgs_b
import calendar
import cProfile
import copy
from collections import namedtuple
import numpy as np
from numpy import array

from server.devices.base import BaseEnvironment

from server.functions import get_configuration
import multiprocessing
from multiprocessing.process import Process
import os
from server.settings import BASE_DIR
from csv import writer
import dateutil


DEFAULT_FORECAST_INTERVAL = 1 * 3600.0
"""The interval for how long one auto_optimize will forecast and for how long one specific workload is set.
Note, that this constant also represents a compromise: Shorter intervals can adjust to quick changes,
f.e. electricity demands changes, while longer intervals can incorporate more forecasts, but wont be able
to adjust quickly. 
The interval of one hour lead to good results in our tests.
"""

[docs]def auto_optimize(forecast): """ Tries to optimize the cost and sets the ``cu.overwrite_workload`` The method forecasts from ``env.now`` with different cu workloads and finds the one with the lowest cost. The length of the forecast is :attr:`DEFAULT_FORECAST_INTERVAL`. :param forecast: the forecast to be optimized """ optimized_config = find_optimal_config(forecast) cu = forecast.getCU() cu.overwrite_workload = float(optimized_config["cu_overwrite_workload"]) print "optimization round at time: ",datetime.fromtimestamp(forecast.env.now),":", optimized_config
[docs]def find_optimal_config(initial_time, forecast): """ ``Internal Method`` Main method, which optimizes the costs by running a global approximation for the best configuration and then running a local minimization method on this approximation""" prices = {} prices["gas_costs"] = get_configuration('gas_costs') prices["electrical_costs"] = get_configuration('electrical_costs') rewards = {} rewards["thermal_revenues"] = get_configuration('thermal_revenues') rewards["warmwater_revenues"] = get_configuration('warmwater_revenues') rewards["electrical_revenues"] = get_configuration('electrical_revenues') rewards["feed_in_reward"] = get_configuration('feed_in_reward') arguments = (initial_time, forecast, prices, rewards) #find initial approximation for parameters results = [] for cu_load in range(0,100,10): config = [cu_load,] cost = estimate_cost(config, *arguments) results.append(BilanceResult(cost, config)) boundaries = [(0.0,100.0)] #take parameters with lowest cost initial_parameters = min(results,key=lambda result: result.cost).params parameters = fmin_l_bfgs_b(estimate_cost, x0 = array(initial_parameters), args = arguments, bounds = boundaries, approx_grad = True, factr=10**4, iprint=0, epsilon=1, maxfun =50) cu_workload, = parameters[0] return {"cu_overwrite_workload":cu_workload}
[docs]def estimate_cost(params, *args): """``Internal Method`` copies the devices and environment, forwards it and returns the costs. :param list params: parameter to be optimized (CU.workload for now) :param args: (initial_time, forecast, prices, rewards) """ (initial_time, forecast, prices, rewards) = args copied_devices = copy.deepcopy(forecast.devices) cu = copied_devices.cu cu.overwrite_workload = params[0] simplified_forecast(cu.env, initial_time, copied_devices) return total_costs(copied_devices, prices, rewards)
[docs]def simplified_forecast(env, initial_time, devices): """runs the forward loop only executing the step function""" forward = DEFAULT_FORECAST_INTERVAL while forward > 0: for device in devices: device.step() env.now += env.step_size forward -= env.step_size
[docs]def total_costs(devices, prices, rewards): """``Internal Method`` Returns the cost of a forecast run. The function uses the prices which are stored in the db deviceconfiguration. It is also constrained by boundaries, f.e. the heatstorage should never go below min temperature. :param devices: The devices after the forecast :param dict prices, rewards: Cached prices and rewards """ d = devices cu,plb,ec,pm,tc,hs = d.cu,d.plb,d.ec,d.pm,d.tc,d.hs #maintenance_costs = cu.power_on_count gas_costs = (cu.total_gas_consumption + plb.total_gas_consumption) * prices["gas_costs"] own_el_consumption = ec.total_consumption - pm.fed_in_electricity - pm.total_purchased electric_rewards = pm.fed_in_electricity * rewards["feed_in_reward"] + own_el_consumption * rewards["electrical_revenues"] electric_costs = pm.total_purchased * prices["electrical_costs"] thermal_rewards = tc.total_consumed * rewards["thermal_revenues"] final_cost = electric_costs-electric_rewards + gas_costs - thermal_rewards temp = hs.get_temperature() above_penalty = abs(min(hs.config["critical_temperature"] - temp, 0) * 1000) below_penalty = abs(max(hs.config["min_temperature"] - temp, 0) * 1000) small_penalties = (temp > hs.config["target_temperature"]+5) * 15 + (temp < hs.config["target_temperature"]-5) * 5 return final_cost + above_penalty + below_penalty + small_penalties
[docs]class BilanceResult(object): """ wrapper for storing a optimization result""" def __init__(self, cost, params): self.params = params self.cost = cost #################################### ######### multiprocess map ######### ####################################
def multiprocess_map(target,params, *args): mgr = multiprocessing.Manager() dict_threadsafe = mgr.dict() jobs = [Process(target=target_wrapper, args=(target,param,index,dict_threadsafe,args)) for index, param in enumerate(params)] for job in jobs: job.start() for job in jobs: job.join() return dict_threadsafe.values() def target_wrapper(target, params, index, dict_threadsafe, args): dict_threadsafe[index] = BilanceResult(target(params, *args),params)