{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Class 7 - Sensitivity testing\n",
    "\n",
    "In this class, we will integrate a transport model with LCA. More specifically, we will take the output from a Matsim model run, and assess the potential for autonomous electric vehicles to reduce the environmental impact of vehicular traffic.\n",
    "\n",
    "## Bibliography\n",
    "\n",
    "### Summary of global and local sensitivity testing:\n",
    "\n",
    "[Global sensitivity analysis](http://eu.wiley.com/WileyCDA/WileyTitle/productCd-0470059974.html)\n",
    "\n",
    "### Variance-based global tests:\n",
    "\n",
    "[Morris method of elementary effects](https://en.wikipedia.org/wiki/Elementary_effects_method)\n",
    "\n",
    "[Fourier amplitude sensitivity testing](https://en.wikipedia.org/wiki/Fourier_amplitude_sensitivity_testing)\n",
    "\n",
    "[Variance-based sensitivity analysis](https://en.wikipedia.org/wiki/Variance-based_sensitivity_analysis)\n",
    "\n",
    "### Distribution-based global tests:\n",
    "\n",
    "[A simple and efficient method for global sensitivity analysis based on cumulative distribution functions (PAWN)](http://www.sciencedirect.com/science/article/pii/S1364815215000237)\n",
    "\n",
    "[Relative entropy based method for probabilistic sensitivity analysis in\n",
    "engineering design (Entropy)](https://www.researchgate.net/publication/228343915_Relative_Entropy_Based_Method_for_Probabilistic_Sensitivity_Analysis_in_Engineering_Design)\n",
    "\n",
    "[A new uncertainty importance measure (δ-sensitivity)](http://www.sciencedirect.com/science/article/pii/S0951832006000883)\n",
    "\n",
    "### Design of experiments\n",
    "\n",
    "[Design of experiments (Wikipedia)](https://en.wikipedia.org/wiki/Design_of_experiments)\n",
    "\n",
    "[Design of experiments (NIST Engineering statistics handbook)](http://www.itl.nist.gov/div898/handbook/pri/section1/pri11.htm)\n",
    "\n",
    "## Installing [SALib](http://salib.readthedocs.org/en/latest/index.html)\n",
    "\n",
    "1. Open a new terminal window, and activate the `bw2` environment, e.g. by running `C:\\bw2-python\\bw2-env.bat`.\n",
    "\n",
    "2. Run the command `pip install --no-cache-dir SALib`.\n",
    "\n",
    "Before getting started, make sure you have upgrade the Brightway2 packages. You should have at least the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Need to install SALib (https://github.com/SALib/SALib). See section above.\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    import SALib\n",
    "except ImportError:\n",
    "    print(\"Need to install SALib (https://github.com/SALib/SALib). See section above.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now import the necessary libraries:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: bad escape \\s\n"
     ]
    }
   ],
   "source": [
    "from brightway2 import *\n",
    "from bw2data.logs import get_logger\n",
    "import collections\n",
    "import csv\n",
    "import datetime\n",
    "import itertools\n",
    "import json\n",
    "import logging\n",
    "import numpy as np\n",
    "import os\n",
    "import pyprind\n",
    "import random"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prepare for graphics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning: inspect.getargspec() is deprecated, use inspect.signature() instead\n",
      "Warning: axes.color_cycle is deprecated and replaced with axes.prop_cycle; please use the latter.\n"
     ]
    }
   ],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "from matplotlib.patches import Rectangle\n",
    "import seaborn as sns\n",
    "\n",
    "sns.set(style=\"white\", palette=\"muted\", color_codes=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Can reuse project from Class 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "projects.current = \"Class 5\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This class will load and process the input data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Data(object):\n",
    "    def __init__(self, debug=False):\n",
    "        assert os.path.exists(os.path.join(os.getcwd(), \"matsim-files\", \"person-distances.csv\"))\n",
    "        self.pd = self.get_person_distances()\n",
    "        self.sd = self.get_station_distances()\n",
    "        self.rt = self.get_routes()\n",
    "        self.locations = self.get_locations()\n",
    "        self.log = get_logger(\"transport-model\", logging.INFO if debug else logging.WARNING)\n",
    "        if debug:\n",
    "            print(\"Writing to log:\\n\", self.log.handlers[0].baseFilename)\n",
    "            self.reduce_variables()\n",
    "    \n",
    "    def reduce_variables(self):\n",
    "        included = {'Riniken', 'Remigen', 'Villigen'}\n",
    "        self.sd = {k: v for k, v in self.sd.items() if k[0] in included and k[1] in included}\n",
    "        self.rt = [rt for rt in self.rt if rt[2] in included and rt[4] in included]\n",
    "    \n",
    "    def get_person_distances(self):\n",
    "        with open(os.path.join(os.getcwd(), \"matsim-files\", \"person-distances.csv\"), 'r') as csvfile:\n",
    "            data = np.array([float(x[0]) for x in csv.reader(csvfile)])\n",
    "        return data\n",
    "    \n",
    "    def get_station_distances(self):\n",
    "        with open(os.path.join(os.getcwd(), \"matsim-files\", \"station-distances.csv\"), 'r') as csvfile:\n",
    "            reader = csv.reader(csvfile)\n",
    "            data = {(x[0], x[1]): float(x[2]) for x in csv.reader(csvfile)}\n",
    "        return data\n",
    "\n",
    "    def get_routes(self):\n",
    "        def translate_time(o):\n",
    "            h, m, s = [float(x) for x in o.split(\":\")]\n",
    "            return round(s / 60 + m + 60 * h)\n",
    "        \n",
    "        with open(os.path.join(os.getcwd(), \"matsim-files\", \"routes.csv\"), 'r') as csvfile:\n",
    "            reader = csv.reader(csvfile)\n",
    "            data = [line for line in reader]\n",
    "        \n",
    "        # Add one minute to end time, as car is still in transit in end minute\n",
    "        return [(translate_time(obj[0]), translate_time(obj[1]) + 1, obj[2], float(obj[3]), obj[4], float(obj[5])) \n",
    "                for obj in data]\n",
    "\n",
    "    def get_locations(self):\n",
    "        return json.load(open(\"matsim-files/locations.json\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "data = Data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The basic model structure is the following:\n",
    "\n",
    "We have a set of cars in village centers (`Village`). The total numbers of cars is chosen before the model is run; the proportion of cars in each village comes from the number of trips originating in that village.\n",
    "\n",
    "We have a global timer in our model. Each time the timer clicks forward, for each car (`Car`), one or more of the following happens:\n",
    "\n",
    "* The car is underway; a certain amount of energy is removed from its batteries (this is done all at once)\n",
    "* The car is in a charging station; a certain amount of energy is add to its batteries, if necessary\n",
    "* The car is dispatched to a new route\n",
    "* The car arrives at a charging station\n",
    "\n",
    "We will go through the other classes in class :)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Car(object):\n",
    "    # Reduce memory usage - we will create thousands of these\n",
    "    __slots__ = ('id', 'charge', 'capacity', 'consumption', 'charge_rate', 'battery_recovery', 'used')\n",
    "    \n",
    "    def __init__(self, i, capacity, consumption, charge_rate):\n",
    "        self.id = i\n",
    "        self.capacity = capacity or 15 + 30 * random.random() # kWh\n",
    "        self.charge = np.ones((24 * 60)) * self.capacity  # Log of battery charge (kWh) over day\n",
    "        self.battery_recovery = np.zeros(self.charge.shape, dtype=bool)\n",
    "        self.consumption = consumption # kWh / meter\n",
    "        self.charge_rate = charge_rate # kWh / minute\n",
    "        self.used = False\n",
    "        \n",
    "    def assign_route(self, route):\n",
    "        \"\"\"Deplete battery by distance * self.consumption over route time.\"\"\"\n",
    "        self.used = True\n",
    "        energy_use = route.distance * self.consumption\n",
    "        assert route.end - route.start\n",
    "        # Any charging in minute of departure or future is cancelled\n",
    "        if route.start:\n",
    "            self.charge[route.start:] = self.charge[route.start - 1]\n",
    "        self.charge[route.start:route.end] -= np.linspace(0, energy_use, route.end - route.start)\n",
    "        self.charge[route.end:] = self.charge[route.end - 1]\n",
    "\n",
    "    def charge_battery(self, time):\n",
    "        \"\"\"Add `self.charge_rate` to each minute in the future until fully charged.\n",
    "        \n",
    "        Returns whether current battery state is at maximum capacity at `time`.\"\"\"\n",
    "        if not self.used or self.charge[time] == self.capacity:\n",
    "            return True        \n",
    "        \n",
    "        if self.charge[time] > self.capacity:\n",
    "            raise ValueError\n",
    "        \n",
    "        minutes_of_full_charge = min(int((self.capacity - self.charge[time]) // self.charge_rate), 24 * 60 - time)\n",
    "        charging_end = time + minutes_of_full_charge\n",
    "        self.charge[time:charging_end] += np.arange(1, minutes_of_full_charge + 1) * self.charge_rate\n",
    "        if charging_end < 24 * 60:\n",
    "            self.charge[charging_end:] = self.capacity\n",
    "\n",
    "        if self.charge[time] < 0.25 * self.capacity:\n",
    "            time_until_80_percent_full = int((self.capacity * 0.8 - self.charge[time]) / self.charge_rate) + 1\n",
    "            self.battery_recovery[time:time + time_until_80_percent_full] = True\n",
    "\n",
    "        return self.charge[time] == self.capacity\n",
    "            \n",
    "    def __str__(self):\n",
    "        return \"{} car {}\".format(\"Used\" if self.used else \"Unused\", self.id)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "Event = collections.namedtuple(\"Event\", ['event', 'kind', 'car', 'location'])\n",
    "\n",
    "class EventLog(object):\n",
    "    def __init__(self):\n",
    "        self.log = [[] for x in range(24 * 60)]\n",
    "        \n",
    "    def add_route(self, car, route, rebalance=False):\n",
    "        self.log[route.start].append(Event(\n",
    "                event='leave', \n",
    "                kind='rebalance' if rebalance else 'route', \n",
    "                car=car.id, \n",
    "                location=route.from_\n",
    "            )) \n",
    "        data.log.info(\"Adding event: leave, time: {}, car: {}, from: {}\".format(route.start, car.id, route.from_))\n",
    "        self.log[route.end].append(Event(\n",
    "                event='arrive', \n",
    "                kind='rebalance' if rebalance else 'route', \n",
    "                car=car.id, \n",
    "                location=route.to\n",
    "            )) \n",
    "        data.log.info(\"Adding event: arrive, time: {}, car: {}, from: {}\".format(route.end, car.id, route.to))\n",
    "\n",
    "    def arriving(self, time):\n",
    "        return (x for x in self.log[time] if x.event == 'arrive')\n",
    "\n",
    "    def __str__(self):\n",
    "        return \"Event log with {} entries\".format(len(self.log))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Unavailable(Exception):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Village(object):\n",
    "    def __init__(self, name, cars, distances):\n",
    "        self.name = name\n",
    "        self.cars = cars\n",
    "        self.neighbors = self.get_neighbors(distances)\n",
    "        self.log = np.zeros((24 * 60))\n",
    "                    \n",
    "    def arrival(self, car, time):\n",
    "        self.cars.append(car)\n",
    "        car.charge_battery(time)\n",
    "        \n",
    "    def departure(self, car):\n",
    "        del self.cars[self.cars.index(car)]\n",
    "        \n",
    "    def available(self, route, extra_distance):\n",
    "        try:\n",
    "            return sorted(\n",
    "                [car \n",
    "                 for car in self.cars \n",
    "                 if car.charge[route.start] > (route.distance + extra_distance) * car.consumption\n",
    "                 and not car.battery_recovery[route.start]], \n",
    "                key=lambda x: (x.charge[route.start], x.id),\n",
    "            )[0]\n",
    "        except IndexError:\n",
    "            raise Unavailable\n",
    "        \n",
    "    def get_neighbors(self, distances):\n",
    "        return sorted([(k[1], v) \n",
    "                       for k, v in data.sd.items() \n",
    "                       if k[0] == self.name \n",
    "                       and k[1] != self.name], \n",
    "                      key=lambda x: x[1])\n",
    "\n",
    "    def write_car_log(self, time):\n",
    "        self.log[time] = len(self.cars)\n",
    "    \n",
    "    def __str__(self):\n",
    "        return \"Village: {}\".format(self.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "\"\"\"Route runs from route.start to route.end - 1; car is only available the minute *after* the route is finished.\"\"\"\n",
    "Route = collections.namedtuple('Route', ['start', 'end', 'from_', 'to', 'distance'])\n",
    "\n",
    "class AEVModel(object):\n",
    "    def __init__(self, aev_efficiency_factor=0.15, charge_rate=1/4, \n",
    "                 battery_capacity=31.2, num_cars=1e4, avg_speed=500,\n",
    "                 consumption=0.0002):\n",
    "        self.aev_efficiency_factor = aev_efficiency_factor\n",
    "        self.charge_rate = charge_rate            # kWh/minute, from http://www.pluginamerica.org/drivers-seat/understanding-electric-vehicle-charging\n",
    "        self.battery_capacity = battery_capacity  # kWh - ecoinvent 2.2 default, 312 kg, 100 Wh/kg\n",
    "        self.num_cars = num_cars                  # Total number of cars to simluate\n",
    "        self.avg_speed = avg_speed                # meters/minute - 30 km/h\n",
    "        self.consumption = consumption            # kWh / meter - ecoinvent 2.2 default, 0.2 kWh/km\n",
    "        self.total_distance = 0\n",
    "        self.from_neighbors = 0\n",
    "        \n",
    "        self.event_log = EventLog()\n",
    "        self.routes = self.setup_routes(avg_speed)\n",
    "        self.villages, self.cars = self.setup_transport_system(num_cars)\n",
    "\n",
    "    def setup_routes(self, avg_speed):\n",
    "        \"\"\"Create data structure of (start, end, from_, to, distance) named tuples for routes.\n",
    "        \n",
    "        Uses `avg_speed` to translate distances to and from village centres into time.\"\"\"\n",
    "        for obj in data.rt:\n",
    "            assert round(obj[1]) > round(obj[0])\n",
    "        return [Route(\n",
    "                    start=round(obj[0] - obj[3] / avg_speed),\n",
    "                    end=round(obj[1] + obj[5] / avg_speed),\n",
    "                    from_=obj[2], \n",
    "                    to=obj[4], \n",
    "                    distance=data.sd[(obj[2], obj[4])] + obj[3] + obj[5]) \n",
    "                for obj in data.rt \n",
    "                # Must finish before day is over\n",
    "                if round(obj[1] + obj[5] / avg_speed) <= 24 * 60 - 1]\n",
    "        \n",
    "    def setup_transport_system(self, num_cars):\n",
    "        car_count = itertools.count()\n",
    "        count = {obj[0]: 0 for obj in data.sd}            \n",
    "        for route in self.routes:\n",
    "            count[route.from_] += 1\n",
    "            count[route.to] -= 0.5  # Penalize for incoming cars\n",
    "\n",
    "        # print(np.histogram(list(count.values())))\n",
    "\n",
    "        # At least a weight of 50 in each origin\n",
    "        for key, value in count.items():\n",
    "            if value < 150:\n",
    "                count[key] = 150\n",
    "            elif value > 500:\n",
    "                count[key] = 500 # 500 + 0.5 * (value - 500)\n",
    "        \n",
    "        total_trips = sum(count.values())\n",
    "        \n",
    "        villages = {}\n",
    "        for name in {obj[0] for obj in data.sd}:\n",
    "            num_cars = round(count[name] / total_trips * self.num_cars)\n",
    "            villages[name] = Village(\n",
    "                name, \n",
    "                [Car(next(car_count), self.battery_capacity, self.consumption, self.charge_rate) for x in range(num_cars)],\n",
    "                data.sd\n",
    "            )\n",
    "        \n",
    "        return villages, {car.id: car for k, v in villages.items() for car in v.cars}\n",
    "    \n",
    "    def rebalance_villages(self, time):\n",
    "        # TODO\n",
    "        return\n",
    "\n",
    "    def dispatch_car(self, route):\n",
    "        village = self.villages[route.from_]\n",
    "        car = None\n",
    "        try:\n",
    "            car = village.available(route, 0)\n",
    "            if route.end - route.start:\n",
    "                village.departure(car)\n",
    "            self.event_log.add_route(car, route)\n",
    "            car.assign_route(route)\n",
    "            self.total_distance += route.distance\n",
    "            return\n",
    "        except Unavailable:\n",
    "            pass\n",
    "        for neighbor, extra_distance in village.neighbors:\n",
    "            try:\n",
    "                nv = self.villages[neighbor]\n",
    "                delay = round(extra_distance / self.avg_speed)\n",
    "                total_distance = route.distance + extra_distance\n",
    "                car = nv.available(route, total_distance)\n",
    "                if route.end + delay >= 24 * 60:\n",
    "                    # Route can't be finished due to delays, so is dropped\n",
    "                    return\n",
    "                route = Route(route.start, route.end + delay, neighbor, route.to, total_distance)\n",
    "                if route.end - route.start:\n",
    "                    nv.departure(car)\n",
    "                car.assign_route(route)\n",
    "                self.event_log.add_route(car, route)\n",
    "                self.from_neighbors += 1\n",
    "                self.total_distance += total_distance\n",
    "                return\n",
    "            except Unavailable:\n",
    "                pass\n",
    "        raise Unavailable\n",
    "    \n",
    "    def iterate(self, time):\n",
    "        \"\"\"The fundamental model algorithm.\"\"\"\n",
    "        for event in self.event_log.arriving(time):\n",
    "            self.villages[event.location].arrival(self.cars[event.car], time)\n",
    "        for rt in (route for route in self.routes if route.start == time):\n",
    "            self.dispatch_car(rt)\n",
    "        # self.rebalance_villages(time)\n",
    "        # for v, k in self.villages.items():\n",
    "        #    k.charge_cars(time)        \n",
    "        for village in self.villages.values():\n",
    "            village.write_car_log(time)        \n",
    "    \n",
    "    def execute(self):\n",
    "        for x in pyprind.prog_bar(range(24*60)):\n",
    "            self.iterate(x)\n",
    "        self.wraparound()\n",
    "\n",
    "    def wraparound(self):\n",
    "        \"\"\"Charge the batteries of the cars which aren't full by starting again from time 0\"\"\"\n",
    "        for car in self.cars.values():\n",
    "            if car.charge[-1] == car.capacity:\n",
    "                continue\n",
    "            minutes_needed = int((car.capacity - car.charge[-1]) / car.charge_rate) + 1\n",
    "            if car.charge[minutes_needed] == car.capacity:\n",
    "                # Can charge before first use\n",
    "                car.charge[:minutes_needed] = car.charge[-1] + np.arange(1, minutes_needed + 1) * car.charge_rate \n",
    "            else:\n",
    "                first_use = np.where(car.charge < car.capacity)[0][0]\n",
    "                car.charge[:first_use] = car.charge[-1] + np.arange(1, first_use + 1) * car.charge_rate \n",
    "        \n",
    "    # Interpretation\n",
    "    ################\n",
    "    \n",
    "    def car_array(self, only_active=False):\n",
    "        if only_active:\n",
    "            return np.hstack([car.charge.reshape((-1, 1)) \n",
    "                              for car in self.cars.values()\n",
    "                              if car.used]) \n",
    "        else:\n",
    "            return np.hstack([car.charge.reshape((-1, 1)) for car in self.cars.values()]) \n",
    "    \n",
    "    def statistics(self):\n",
    "        arr = self.car_array(True)\n",
    "        total_cars = len(self.cars)\n",
    "        used = sum(1 for car in self.cars.values() if car.used)\n",
    "        energy_use = np.diff(arr, axis=0)\n",
    "        return {\n",
    "            'cars': total_cars,\n",
    "            'cars used': used,\n",
    "            'energy used': energy_use[energy_use < 0].sum(),\n",
    "            'energy charged': energy_use[energy_use > 0].sum()\n",
    "        }\n",
    "\n",
    "    def consumption_array(self):\n",
    "        arr = base_model.car_array(1)\n",
    "        energy_use = np.diff(arr, axis=0)\n",
    "        mask = energy_use < 0\n",
    "        energy_use[~mask] = 0\n",
    "        result = np.zeros(arr.shape[0])\n",
    "        result[1:] = energy_use.sum(axis=1)\n",
    "        return result\n",
    "        \n",
    "    def charging_array(self):\n",
    "        arr = base_model.car_array(1)\n",
    "        energy_use = np.diff(arr, axis=0)\n",
    "        mask = energy_use > 0\n",
    "        energy_use[~mask] = 0\n",
    "        result = np.zeros(arr.shape[0])\n",
    "        result[1:] = energy_use.sum(axis=1)\n",
    "        return result\n",
    "    \n",
    "    def plot_sample_cars(self):\n",
    "        plt.figure(figsize=(10, 8))\n",
    "\n",
    "        count = 0\n",
    "\n",
    "        xs = np.arange(24*60)\n",
    "        for k, car in self.cars.items():\n",
    "            if not car.used:\n",
    "                continue\n",
    "            else:\n",
    "                plt.plot(xs, car.charge, alpha=0.2)\n",
    "                count += 1\n",
    "            if count > 50:\n",
    "                break\n",
    "\n",
    "        plt.xlabel(\"Time (minutes)\")\n",
    "        plt.xlabel(\"Time (hours)\")\n",
    "        plt.xticks([120 * x for x in range(13)], range(0, 25, 2))\n",
    "        plt.xlim(0, 24*60)\n",
    "        plt.ylabel(\"Battery storage (kWh)\")        \n",
    "    \n",
    "    def plot_battery_charge(self, only_active=True):\n",
    "        plt.figure(figsize=(10, 4))\n",
    "\n",
    "        count = 0\n",
    "\n",
    "        xs = np.arange(24*60)\n",
    "        ys = self.consumption_array()\n",
    "\n",
    "        plt.plot(xs, ys)\n",
    "\n",
    "        plt.xlabel(\"Time (hours)\")\n",
    "        plt.xticks([120 * x for x in range(13)], range(0, 25, 2))\n",
    "        plt.xlim(0, 24*60)\n",
    "        plt.ylim(0, plt.ylim()[1] * 1.1)\n",
    "        plt.ylabel(\"Total battery charge (kWh)\")  \n",
    "        \n",
    "    def plot_charging(self):\n",
    "        plt.figure(figsize=(10, 4))\n",
    "\n",
    "        count = 0\n",
    "\n",
    "        xs = np.arange(24*60)\n",
    "        ys = self.charging_array()\n",
    "\n",
    "        plt.plot(xs, ys)\n",
    "\n",
    "        plt.xlabel(\"Time (hours)\")\n",
    "        plt.xticks([120 * x for x in range(13)], range(0, 25, 2))\n",
    "        plt.xlim(0, 24*60)\n",
    "        plt.ylim(0, plt.ylim()[1] * 1.1)\n",
    "        plt.ylabel(\"Total charging energy use (kWh)\")  \n",
    "        \n",
    "    def plot_villages(self):\n",
    "        directory_path = os.path.join(os.getcwd(), \"villages\")\n",
    "\n",
    "        if not os.path.isdir(directory_path):\n",
    "            os.mkdir(directory_path)\n",
    "\n",
    "        sns.set_style(\"white\")\n",
    "        rect_color = sns.color_palette()[1]\n",
    "\n",
    "        for i in pyprind.prog_bar(range(0, 24 * 60, 1)):\n",
    "            plt.clf()\n",
    "\n",
    "            xs, ys, sizes = [], [], []\n",
    "\n",
    "            for k, v in base_model.villages.items():\n",
    "                x, y = data.locations[k]\n",
    "                xs.append(x)\n",
    "                ys.append(y)\n",
    "                sizes.append(v.log[i])\n",
    "\n",
    "            plt.scatter(xs, ys, s=sizes, alpha=0.5, edgecolors=\"face\")\n",
    "\n",
    "            xlim = plt.xlim()\n",
    "\n",
    "            ylim = plt.ylim()\n",
    "            plt.ylim(ylim[0] - 0.1, ylim[1])\n",
    "\n",
    "            ylim = plt.ylim()\n",
    "\n",
    "            bottom = ylim[0] + 0.1 * (ylim[1] - ylim[0])\n",
    "            height = 0.05 * (ylim[1] - ylim[0])\n",
    "\n",
    "            left = xlim[0] + 0.1 * (xlim[1] - xlim[0])\n",
    "            width = 0.8 * (xlim[1] - xlim[0]) * (i / 1440)\n",
    "\n",
    "            ca = plt.gca()\n",
    "            ca.add_patch(Rectangle((left, bottom), width, height, facecolor=rect_color, edgecolor=\"none\", visible=True, alpha=0.5))\n",
    "            plt.text(left + width, bottom + height + 0.02, \"{:02d}:{:02d}\".format(i // 60, i % 60), ha='center', va='center', fontsize=14)\n",
    "\n",
    "            plt.xticks([])\n",
    "            plt.yticks([])\n",
    "            sns.despine(left=True, bottom=True, trim=True)\n",
    "            \n",
    "            plt.tight_layout()\n",
    "            plt.savefig(os.path.join(directory_path, \"car_map_{0:04d}.png\".format(i)), dpi=300)\n",
    "            \n",
    "        sns.set_style(\"darkgrid\")\n",
    "        \n",
    "    def save_calliope_charge_array(self):\n",
    "        arr = self.charging_array()\n",
    "        np.savetxt(\"charge.csv\", [arr[i * 15:(i * 15) + 1].sum() for i in range(24 * 4)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Set up our problem and objective function\n",
    "\n",
    "We will try to minize the climate change LCA score in this class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "'transport, passenger car, electric, LiMn2O4' (person kilometer, CH, ['transport systems', 'road'])\n",
      "'electricity, low voltage, consumer mix, at grid' (kilowatt hour, CH, ['electricity', 'supply mix'])\n",
      "'electricity, natural gas, at combined cycle plant, best technology' (kilowatt hour, RER, ['natural gas', 'power plants'])\n"
     ]
    }
   ],
   "source": [
    "db = Database(\"ecoinvent 2.2\")\n",
    "electric_car = db.search(\"transport electric car\")[0]\n",
    "print(electric_car)\n",
    "\n",
    "subtracted_electricity = list(db.search(\"operation passenger electric car\")[0].technosphere())[0].input\n",
    "print(subtracted_electricity)\n",
    "\n",
    "electricity = db.search(\"electricity, natural gas, at combined cycle plant, best technology\")[0]\n",
    "print(electricity)\n",
    "\n",
    "gwp =  ('IPCC 2013', 'climate change', 'GWP 100a')\n",
    "\n",
    "lca = LCA({electric_car: 1}, gwp)\n",
    "lca.lci()\n",
    "lca.lcia()\n",
    "electric_car_impact = lca.score / 1000 # Convert km to m\n",
    "\n",
    "lca = LCA({subtracted_electricity: 1}, gwp)\n",
    "lca.lci()\n",
    "lca.lcia()\n",
    "subtracted_electricity_impact = lca.score\n",
    "\n",
    "lca = LCA({electricity: 1}, gwp)\n",
    "lca.lci()\n",
    "lca.lcia()\n",
    "electricity_impact = lca.score\n",
    "\n",
    "def get_impact(distance, consumption):\n",
    "    return (distance * electric_car_impact + \n",
    "            (electricity_impact - subtracted_electricity_impact) * distance * consumption)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "initial_values = np.array((1/4, 31.2 * 1.5, 0.0002))\n",
    "\n",
    "problem = {\n",
    "    'num_vars': 3,\n",
    "    'names': ['charge rate', 'battery capacity', 'energy consumption'],\n",
    "    'groups': None,\n",
    "    'bounds': [[1/4 - 1/8, 1/4 + 1/8],\n",
    "               [31.2, 31.2 * 2],\n",
    "               [0.0001, 0.0003]]\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def objective_function(input_parameters):\n",
    "    charge_rate, battery_capacity, consumption = input_parameters\n",
    "    model = AEVModel(\n",
    "        num_cars = len(data.rt) / 10, \n",
    "        charge_rate=charge_rate, \n",
    "        battery_capacity=battery_capacity,\n",
    "        consumption=consumption\n",
    "    )\n",
    "    model.execute()\n",
    "    return get_impact(model.total_distance, consumption)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 33.218 sec\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "75836.475897054712"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "objective_function(initial_values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Local sensitivity testing\n",
    "\n",
    "Vary each parameter independently."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 22.062 sec\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "75843.84091030205"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "objective_function((1/4 * 0.9, 31.2, 0.0002))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 22.789 sec\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "75842.58283648797"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "objective_function((1/4, 31.2 * 0.9, 0.0002))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 22.698 sec\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "71875.88078335376"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "objective_function((1/4, 31.2, 0.0002 * 0.9))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Global sensitivity testing\n",
    "\n",
    "## Sampling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from SALib.sample import saltelli as saltelli_sample\n",
    "from SALib.sample import morris as morris_sample\n",
    "from SALib.analyze import morris as morris_analyze\n",
    "from SALib.sample import fast_sampler\n",
    "from SALib.sample import latin as latin_sampler\n",
    "from SALib.sample import sobol_sequence\n",
    "from SALib.analyze import sobol as sobol_analyze"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "example_problem = {\n",
    "    'num_vars': 2,\n",
    "    'names': ['x1', 'x2'],\n",
    "    'bounds': [[0, 10],\n",
    "               [0, 10]]\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def plot_sample(xs, ys):\n",
    "    plt.figure(figsize=(8,8))\n",
    "    plt.scatter(xs[:100], ys[:100])\n",
    "    plt.xticks([])\n",
    "    plt.yticks([])\n",
    "    plt.axis(\"off\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAAHMCAYAAABY25iGAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEoxJREFUeJzt3TGLXFl2B/DTbQ28/QAFZj3zCZwZNA2TCRZjrTBYyBjE\nJk3HyjY0KBBs6ExxQwW7nbiYaKhIjFPVBJv5C/TAgukPUHZvdzuQSmqVatSnqt+rd999v1+4werN\n61f1f/ecc28d3Nzc3AQA8EWHfV8AAAyBwASABIEJAAkCEwASBCYAJAhMAEgQmACQIDABIEFgAkCC\nwASABIEJAAkCEwASBCYAJAhMAEgQmACQIDABIEFgAkCCwASABIEJAAkCEwASBCYAJAhMAEgQmACQ\nIDABIEFgAkCCwASABIEJAAkCEwASBCYAJAhMAEgQmACQIDABIEFgAkCCwASABIEJAAkCEwASBCYA\nJAhMAEgQmACQIDABIEFgAkCCwASABIEJAAkCEwASBCYAJAhMAEgQmACQIDABIEFgAkCCwASABIEJ\nAAkCEwASBCYAJAhMAEgQmACQIDABIEFgAkCCwASABIEJAAkCEwASBCYAJAhMAEgQmACQIDABIEFg\nAkCCwASABIEJAAkCEwASBCYAJAhMAEgQmACQ8KDvCwBgd8vlMr7/4ceIiHj65FE0TdPzFdXr4Obm\n5qbviwBge8vlMl68PI2fD76NiIivbxbx+tWJ0OyIwAQYkNsrysvLy/jTn38dB4fvioXXV5dxfHQR\nz5897vMSq6UkCzAQ6yvKv/n5PyO+/teer2o8BGYB9CC25571z99g/77/4cf4+eDbDyvK//vbf45f\n/c88/nfyTxER8U38FE+fnPR5iVVTku2ZHsT2+rhnwuFTntt+nM3mMV1MPinB/u4f/hJfffVVRHg2\nu2ZbSc9uvzEeHD6I83j44YuZzfZ9z1bhMF1MYrqYxIuXp7FcLjv794bAc9uPp08exdc3i7i+uozr\nq8v4Jn6Kf/uXf4znzx7H82ePhWXHBCbcQThQiqZp4vWrkzg+uojjowur+j0TmD3b9Mb49Mmjvi+r\naO5Z//wN+tM0jRVlT/QwC6A/tr193rNVSfY8HkbEu8EKb/ae2yHzt9uNwIQEXzB0bV/PmIGt3QlM\ngJ7tM8Q2Tdo67CBHDxOgZwbLhkFgUqXlchlns3mczeaj3wICtxnY2p2SLNXRo2Fo9j1Ypie/G4FJ\ndfRoGCIhVj5nyQIUYLW/UnCWSw+T6ujRMFSOYSybkixV8pbOEGknlE1JliqtylvbErTUwHPcDStM\neM90LX1rY1rWc9wdgQnvKYdRgvuuDj3H3VGSBSjIru0EumdKFt4zXUsNPMfdUZKFWwxLUAPPcTcE\nJgAk6GFSHW/XQBesMKmKkXqgK4Z+qIrfFSTLT8CxLSVZ2IJyb7v6up/rlYg3b09VIriTFSZV6XKk\n3sHY7erzfqpEsAuBSVWaponXr07i+Ogijo8uWl01+JJtl/vJ0CjJUh0npXCXp08exZu3p3F+9fHM\n1qdPTnq+KkpnShaS2jgYe9P/51h7ol3cz23//bHee3YjMGELbX7J2gIjtBgWgQk98asSMCyGfgAg\nQWBCT/yqBAyLkiz0SA8PhkNgAkCCkiwAJDi4AEZMSRjylGRhpOwDhe0oycJIOcsVtqMkC3xweXkZ\nZ7N5RCjRwjolWRip9bNc/+76bdzcXMdfHnwXEUq0sE5gwojdHvq5vLyMP/35147qg1+gJAsjdvun\n0FalWGAzQz9ARDiqD+6iJAt8YF8m/DKBCQAJSrIAkCAwASDBlCzsQK9vM/eFmulhwpacwbqZ+0Lt\nlGRhS85g3cx9oXYCEwASBCZsyQb/zdwXaqeHCTsw3LKZ+0LNBCYAJNhWQrGsVoCSWGHSqrZCrsst\nCoIY2IXApDVthtzZbB7TxaT132Ycwl5BgQ5lMiVLa4awD6/0a1wF+nQxieliEi9ensZyuez7soAQ\nmBRqrFsUSg90GDOBSWvaDLmmaeL1q5M4PrqI46OL1sqmYw1i4P70MGnVEPpvJV/jqiR7Hg8jIuKb\n+Km4HiuMlcCEwpQc6DBmAhMAEhxcAO9Z2QFfYoUJMYz9mUC/TMlC2M4B3E1gAkCCwISwPxO4mx7m\niBly+ZT7QW080+0SmCNlyAXq5jPePiXZkTLkAnXzGW+fwASABIE5UoZcoG4+4+3TwxwxAwFQN5/x\ndglMPuNDBmXzGe2HwLyn2h5ck3VQNp/R/uhh3sPqwZ0uJjFdTOLFy9NYLpd9X9a9mKyDsvmM9kdg\n3oMHF2A8BCafMFkHZfMZ7Y8e5j2sSrLn8TAiIr6Jn6roJdTWl72t5v82xsNz3A+BeU8e3OEwLAHc\nh8DskbDdr7PZPKaLSRwcPoiIiOuryzg+uojnzx73fGXAEDzo+wLGan218+btqdUOQMEM/fTEhO3+\nGZagD8vlMs5m8zibzQe/7WzsrDAZjaZp4vWrk1tlcCt6uqWSVBc9zJ7UOmHbtU19X71gSqVvXhcr\nzJ5Y7Wxv09v6f/z77+L3f/ijN3igc1aYDMamt/W//+q/4r//+sgbPEVSSaqLFeYdlPuAXakk1cUK\n8wtsdC/Lprf1VUnWGzzQNYH5BRr25TH0A/RFSZZBaZrmsxeWTf8bQNscXPAFQ93obqM0QPuUZO8w\ntHKfvitANwRmZfRdAbqhJAsACQKzMkPtuwKUTkm2QkPruwIMgcAEeucljyEQmECvTHYzFHqYQK/8\nmDpDITABIEFgAr0y2c1Q6GECvTP0wxAITABI8GslAFuwGh4vK0yAJFtgxs3QD0CSLTDjJjABIEFg\nVs6PSUN7bIEZNz3Mium3MDRDGKgZwjXSDYFZMT8mzZB4waN0SrJUTUl6OAzUbOYZLofArNjY+y2r\nFct0MYnpYhIvXp76wmFQPMNlEZgVa5omXr86ieOjizg+uhhdecuKZVjG/oK3iWe4LE76qVzTNHqW\n3Ns+Bl1WL3gf/50vv+AZvmHfDP2wlSF9Sa3KWefxMCIivomfRrfKbkOJwzglXlMXPMNlEZikDfFL\nakgBX6oSp61LvKaueIbLoSRL2u1+SkTE+dW7fkrJX1JK0gydZ7gchn6ALypxGKfEa6J+SrKk6aeM\nV4llwRKviboJTLbiSwoYK4EJAAmGfviMVSTA56ww+cQQt44A7IMpWT7hKC6AzQQmACQITD5hfxvA\nZnqYfMbQD8DnBCbAQHiZ7ZfABGhZF8Fmgr1/epiwZrlcxtlsHmezuV+3Z2urYJsuJjFdTOLFy9NW\nniMT7P0TmHBLV192jIdgq5fA5BNjX135sqNUJtj752g8Pljvkbx5e6pHAlt6+uRRvHl7GudXH3/V\n5+mTk3v//zZNE69fndzqjfps7puhHz4Y06/Y/xI/YUYbTLPWyQoTbvEWTxuaphnVi+ZYWGHygdUV\nwC8TmHxCKQlgM4EJ0DEvonUQmAAdckJPPezDBOiQvb31EJgAkCAwATrkhJ566GECdMzQTx0EJgAk\nKMkCQILABIAEgQkACQ5fpzMGHYCaGPqhE043AWqjJEsnnG4C1EZJFgqhhA1ls8KkE0432c6qhD1d\nTGK6mMSLl6exXC77vizgFj1MOmPFlHc2m8d0MYmDw3dFn+uryzg+uojnzx73fGXAipIsnWmaxhc+\nUA0lWSiAEjaUT0kWCqGEDWUTmACQoCQLAAmGftgrZUdgqJRk2RvH5QFDpiTL3jguDxgygQkACQKT\nvbHXEBgyPUz2ytAPMFQCEwASlGQBIEFgAkCCwASABCf9VMIwTb38baEMhn4q4ASdevnbQjmUZCvg\nBJ16+dtCOZRkO6CEBlAfK8yWrUpo08UkpotJvHh5GsvlstN/0wk69fK3hXLoYbbsbDaP6WISB4fv\nFu/XV5dxfHQRz5897vTftaqtl78tlEFJthJN03QeyvTD3xbKoCTbMiU0gDopyXZACQ2gPgITABKU\nZAEgQWACQILABIAE20rgltIGtkq7HhgzQz/wXmkHnZd2PQyXF692KMkOzHK5jLPZPM5m886P3Bub\n0g46L+16GKY+juuslcAcEA8+sC0vXu0RmAPiwe9Waac0lXY9MHaGfuC9pmni9auTW72efvuFpV0P\nw/T0yaN48/Y0zq8eRkS8f/E66fmqhsnQz4CsSrLn8fHBNwQC3MXQTzsE5sB48IEI3wV9EJgAA2PL\nUT8M/QAMjAHAfhj6oWrKVkBbrDCpln2r1MqWo37oYVKts9k8potJHBy+K6RcX13G8dFFPH/2uOcr\ng/tTPdk/JVmAAWqaxsvfninJUq1NZavf/uY7Z/EyOs6gboeSLFW7Xbb67W++i9//4Y9G8RkVW1Da\nIzAZDT1Nxshz3x4lWQBIEJiMhlF8xshz3x4lWUbFKD5j5Llvh8AEgAQlWQBIEJgAkCAwASBBYAJA\ngrNkgc6YzqQmpmSBTjiSjdooyQKd+P6HH+Png2/j4PBBHBw+iPN4+GG1CUMkMAEgQWACnXAkG7XR\nwwQ6Y+iHmghMAEhQkgWABIEJAAkOLgDYQP+VdXqYAGscusAmSrIAaxy6wCYCEwASBCbAGocusIke\nJsAGhn5YJzABIEFJFgAS7MMERke5lV0oyfbIhxb2zx5LdqUk25PVh3a6mMR0MYkXL09juVz2fVnQ\niuVyGWezeZzN5sU91/ZYsiuB2RMfWmpV88tgyS8CdE9gAq0q/WVw1z2WNb8IkCMwe2JjNPSjaZp4\n/eokjo8u4vjoIt2/LP1FgO6Zku3J6kP7cejH0AF1ePrkUbx5exrnVw8jIt6/DJ70fFWfapomnj97\n3PdlMDCmZIHW1TgBvirJnsfHFwHTteMiMAGSanwRIE9gAoycF4EcgQkwYg5yyDMlCzBipn/zTMkC\nnVPyowZWmECnbPgvmz3heXqYlfAGT6nOZvOYLiZxcPiuoHV9dRnHRxf2QSbs63Pt+yNHSbYC6037\nN29PNe1h4Pb5uXaQQ46SbAVqado72LpOSn67qeVzXRMrTIpglVwvx0BSCyvMCtTwBr/r27RV6TCs\nSn7Pnz0Wlkk1fK5rY+inEkNv2u8yGHLfDddDv2clcA+75f6WRWBShF0Otr7P9KXTTe7PPWRslGQp\nwq6/UbgrAxX35x4yNoZ+KMa2o+1D+N1FoB5Ksgzarj0ev214f23dQ306hkJg9sAXRBn8He7vvvdQ\nH5QhEZh75gsCPrprcMtLDSUx9LNnBiUgx6HtlEZgAr350uZ8L5eURmDumdM74KN9byeC+9DD7IG+\nDNzNJDOlEZhAscb8ctnGf/uY718XBCYUxBccEe1M05vIb58eJhTCVCgrbQw8GZpqn8CEQviCg7IJ\nTIDCtDFNbyK/fXqYUAhTodxm6Kc8AhMK4gsOyiUwASBBDxMAEgQmACQITABIEJgAkPCg7wtgOExw\nAmNmSpYU51ICY6ckS4pj24CxE5gAkCAwSXEuZTmWy2WczeZxNpv7NRPYIz1M0gz99E8vGfojMBmk\nsYb32Wwe08UkDg7fDbhfX13G8dFFPH/2uOcrg/rZVsLgrK+y3rw9tcoCOqeHyeCMeWJXL5nb9LP3\nywoTBqRpmnj96uRWOdrKeqxUWvZPD5PB8UPLoJ/dBytMBscqC+iDFSZAy/Yxxa3Ssn8CE6BF+9wr\nO9btVX0RmAAt0lusl20lAJAgMAFaZK9svZRkAVpWWm+xtOsZKoEJUDEH9rdHSRY2cOQYtRjzUZJt\nc3ABrHHkGLCJFSas8UbePSv4/TGE1B4rTGCvrOD3y1GS7TH0A2scOdYtG/sZKitMWOONHNjEChPY\nKyt4hkpgFsCmYsbGM88QCcye2VQMdfEyUC/bSnpmCwPUY/UCPF1MYrqYxIuXp7bNVERgArTEC3Dd\nBGbPbCqGMjlcgXV6mAXQ8yifv9G47DpbYAK4bgIT7mAwa3zuc7iCl6t6ObgA7nC7LxURcX71ri/l\nZBo2aZrGs1EpPUyANWYL2ERJFu6gLzVOSqusE5iQ4MsTEJgAkGDoB6iSqgBts8IEqmMrEF0wJQtU\nxxF1dEFgAkCCwASqYx8lXdDDBKpk6Ie2CUwASFCSBYAEgQkACQITABIEJgAkCEwASBCYAJAgMAEg\nQWACQILABIAEgQkACQITABIEJgAkCEwASBCYAJAgMAEgQWACQILABIAEgQkACQITABIEJgAkCEwA\nSBCYAJAgMAEgQWACQILABIAEgQkACQITABIEJgAkCEwASBCYAJAgMAEgQWACQILABIAEgQkACQIT\nABIEJgAkCEwASBCYAJAgMAEgQWACQILABIAEgQkACQITABIEJgAkCEwASBCYAJAgMAEgQWACQILA\nBIAEgQkACQITABIEJgAkCEwASBCYAJAgMAEgQWACQILABIAEgQkACQITABIEJgAkCEwASBCYAJAg\nMAEgQWACQILABIAEgQkACQITABIEJgAkCEwASBCYAJDw/1qKgt+aOjl7AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10d312f28>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_sample(np.random.uniform(0, 10, size=1000), np.random.uniform(0, 10, size=1000))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAAHMCAYAAABY25iGAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAEl1JREFUeJzt3TFonGl6wPFnhJYV1gUVYQMX5ECwkmbTWpsqMOUiFx42\nKZZrFhWCgCFgsgMXgYUMm0JaFgKGAxWCbU5NxLiwUKmtEpBJF1wkMldYB8exzUeQ+Q5ZmhTmJrs+\n5faZ02i+b+b7/bpdMHpnGOnv730fvW71+/1+AAC/10zVCwCASSCYAJAgmACQIJgAkCCYAJAgmACQ\nIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAg\nmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCY\nAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgA\nkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQ\nIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQMFv1AoDJVxRF\nbG7tRETERnctFhYWKl7ReJVlGb2Do4iI6Ky0Y25uruIVcRNa/X6/X/UigMlVFEXcW92K+aVORESc\nnfTi2W63MdEsyzIePNqN09ZyREQs9o/jyeNV0ZxCtmSBa9nc2on5pU60ZmajNTMbt+7cHzxtNkHv\n4ChOW8uD1/8q7g6eNpkuggkACYIJXMtGdy3OTnpxeXEelxfn8frl09jorlW9rLHprLRjsX88eP23\n43l0VtpVL4sb4AwTpshVwyfjGMgx9GPopwkEE6bEVcMn//yPfxuf/P2/NHYgB0ZJMGFK7O0fxtfH\nH0Rr5u1vi11enMePfvXzOPvxT773/z5875v46ovPq1wqTCRnmACQIJgwJa4aPvnZlz9t9EAOjJIt\nWWrPQEVeVUM/08J7xe8jmNSaW1QYl6bfWMQPsyVLrblFhXFp+o1F/DDBBIAEwaTW3KLCuDT9xiJ+\nmDNMas/QD9eV/QxN69DPtL6ucRNMYKo1fXDMMNPo2JIFplrTB8cMM42OYAJAgmACU63pg2OGmUbH\nGSYw9Zo+OGboZzQEEwASbMkCQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJg\nAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAwmzVC6A5iqKIza2diIjY6K7FwsJCxSsar7Is\no3dwFBERnZV2zM3NVbwiYBitfr/fr3oRTL+iKOLe6lbML3UiIuLspBfPdruNiWZZlvHg0W6ctpYj\nImKxfxxPHq+KJkwQW7KMxebWTswvdaI1Mxutmdm4def+4GmzCXoHR3HaWh68/ldxd/C0CUwGwQSA\nBMFkLDa6a3F20ovLi/O4vDiP1y+fxkZ3repljU1npR2L/ePB678dz6Oz0q56WZUriiIerm/Hw/Xt\nKIqi6uXUXlmWsbd/GHv7h1GWZdXLaRxnmIyNoR9DP9/V9HPtYTkHr55gApV4uL4dL960ozXzdlj/\n8uI8Pnzvm/jqi88rXlk97e0fxtfHH3zv/frso2/j008+rnhlzWFLFgASBBOoRNPPtYflHLx6tmSB\nyjT9XHtYzsGrJZgN5IcUTAcBHS/BbBiTiTAdTM2OnzPMhmn6jTswLdweNX6CCQAJgtkwJhO5CU2/\ngaaKG4tMzY6fM8wGMvTDKDX9LK3KuQBDP+MlmMC1NP0GGjcWNYctWQBIEEzgWpp+lmYuoDlsyQLX\n1vSzNHMBzSCYAJBgSxYAEgQTABIEEwASBBMAEgQTABIEEwASBBMAEgQTABIEEwASBBMAEgQTABIE\nEwASBBMAEgQTABIEEwASBBMAEgQTABIEEwASBBMAEmarXgD/pyzL6B0cRUREZ6Udc3NzFa8IGIei\nKGJzayciIja6a7GwsFDxirhKq9/v96teBG9j+eDRbpy2liMiYrF/HE8er4omTLmiKOLe6lbML3Ui\nIuLspBfPdruiWUO2ZGuid3AUp63laM3MRmtmNl7F3cHTJjC9Nrd2Yn6pM/jev3Xn/uBpk3oRTABI\nEMya6Ky0Y7F/HJcX53F5cR6343l0VtpVL6sWiqKIh+vb8XB9O4qiqHo5TLGyLGNv/zD29g+jLMux\nfM2N7lqcnfQG3/uvXz6Nje7aWL42w3GGWSOGfn6X8x3Gpco5AkM/k0EwqbWH69vx4k07WjNvB7ov\nL87jw/e+ia+++LzilTFt9vYP4+vjD773Wfvso2/j008+rnhl1IUtWQBIEExqzfkO42KOgB9iS5ba\n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\nRmtmNm7duT942myK3sFRnLaWB+/Bq7g7eNoE3hJMAEgQTBpvo7sWZye9uLw4j8uL83j98mlsdNeq\nXtZYdVbasdg/HrwHt+N5dFbaVS8LasUZJrVTxfCJoR9DP1fxueC7BJNaMXxCXRgG4122ZKkVwyfU\nhWEw3iWYAJAgmNSK4RPqwjAY73KGeQPeHRR4//33DVMMwfAJdWHoh+8SzBG7alDgz2//cfz6/b+J\nCEMsAJPKluyIXTUo8J///WtDLAATTjABIEEwR+yqQYG/+os/McQCMOGcYQ4hO4xi6AdGy/ANdSCY\nSW6ggWq4cYe6sCWb5AYaqIYbd6gLwQSABMFMcgMNVMONO9SFM8whuIEGqmHohzoQTABIsCULAAmC\nCQAJggkACYIJAAmCCQAJggkACYIJAAmCCQAJggkACYIJAAmCCQAJggkACYIJAAmCCQAJggkACYIJ\nAAmCCQAJggkACbNVL4DJUZZl9A6OIiKis9KOubm5ilfUHEVRxObWTkREbHTXYmFhoeIVQfO0+v1+\nv+pFUH9lWcaDR7tx2lqOiIjF/nE8ebwqmmNQFEXcW92K+aVOREScnfTi2W5XNGHMbMmS0js4itPW\ncrRmZqM1Mxuv4u7gaZObtbm1E/NLncF7f+vO/cHTJjA+ggkACYJJSmelHYv947i8OI/Li/O4Hc+j\ns9KuelmNsNFdi7OT3uC9f/3yaWx016peFjSOM8wG+kOHdwz9VMfQD1fxuRgvwWwYwzswHQyDjZ8t\n2YYxvAPTwTDY+AkmACQIZsMY3oHpYBhs/JxhNpDhHerCZ3E47w75RIShnzESTKASBtCGY8inerZk\ngUoYQBuOIZ/qCSYAJAgmUAkDaMMx5FM9Z5gQhk8iqrk1xvs+HDf7VEswaTzDJwZKIMOWLI1n+MRA\nCWQIJgAkCCaNZ/jEQAlkOMOEMHwSYaAEfohgAkCCLVkASBBMAEiYrXoBDM9ZE8D4ecKcML/9BfMX\nb9rx4k077q1uRVEUVS8LYOoZ+pkwD9e348WbdrRm3m4OXF6cx4fvfRNfffF5xSsDmG6eMAEgwRPm\nhPntluytO/cjIuL1y6fu/AQYA8GcQIZ+AMZPMAEgwRkmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgm\nACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYA\nJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAk\nCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQI\nJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgm\nACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAkCCYAJAgmACQIJgAk/C/XpcB3wuIm0gAAAABJ\nRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10e1755c0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "param_values = saltelli_sample.sample(example_problem, 1000)\n",
    "plot_sample(param_values[:, 0], param_values[:, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10df45c50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "param_values = fast_sampler.sample(example_problem, 1000)\n",
    "plot_sample(param_values[:, 0], param_values[:, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n7RiJYhkoAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x11b71eef0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "param_values = morris_sample.sample(example_problem, 60, 6, 1/6)\n",
    "plot_sample(param_values[:, 0], param_values[:, 1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10d8605f8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def hammersley(n):\n",
    "    \"\"\"Yields n Hammersley points on the unit square in the xy plane.\n",
    "\n",
    "    From http://www.math.uiuc.edu/~gfrancis/illimath/windows/aszgard_mini/pylibs/cgkit/hammersley.py\n",
    "    \n",
    "    This function yields a sequence of n tuples (x,y) which\n",
    "    represent a point on the unit square. The sequence of points for\n",
    "    a particular n is always the same.  When n changes an entirely new\n",
    "    sequence will be generated.\n",
    "    \n",
    "    This function uses a base of 2.\n",
    "    \"\"\"\n",
    "    for k in range(n):\n",
    "        u = 0\n",
    "        p=0.5\n",
    "        kk = k\n",
    "        while kk>0:\n",
    "            if kk & 1:\n",
    "                u += p\n",
    "            p *= 0.5\n",
    "            kk >>= 1\n",
    "        v = (k+0.5)/n\n",
    "        yield (u, v)\n",
    "        \n",
    "hammersley_iterator = hammersley(1000)\n",
    "hammersley_values = np.array([next(hammersley_iterator) for _ in range(1000)])\n",
    "plot_sample(hammersley_values[:, 0], hammersley_values[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Analysis\n",
    "\n",
    "## Morris method of elementary effects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 34.777 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.029 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 31.934 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.332 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 31.925 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 31.622 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.017 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.056 sec\n"
     ]
    }
   ],
   "source": [
    "morris_values = morris_sample.sample(problem, 2, num_levels=4, grid_jump=2)\n",
    "morris_results = np.zeros(morris_values.shape[0])\n",
    "\n",
    "for row in range(morris_values.shape[0]):\n",
    "    morris_results[row] = objective_function(morris_values[row, :])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Parameter Mu Sigma Mu_Star Mu_Star_Conf\n",
      "charge rate -75.099992 65.351399 75.099992 63.364872\n",
      "battery capacity -5.015735 4.372564 5.015735 4.152109\n",
      "energy consumption 39623.433616 76.984437 39623.433616 75.231915\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'mu': array([ -7.50999920e+01,  -5.01573538e+00,   3.96234336e+04]),\n",
       " 'mu_star': array([  7.50999920e+01,   5.01573538e+00,   3.96234336e+04]),\n",
       " 'mu_star_conf': [63.364872166356314, 4.152109196125445, 75.231915167509598],\n",
       " 'names': ['charge rate', 'battery capacity', 'energy consumption'],\n",
       " 'sigma': array([ 65.35139858,   4.37256388,  76.98443734])}"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "morris_analyze.analyze(\n",
    "    problem, \n",
    "    morris_values, \n",
    "    morris_results,\n",
    "    print_to_console=True,\n",
    "    grid_jump=2,\n",
    "    num_levels=4\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sobol sequence"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 35.337 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.676 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 33.141 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.795 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.070 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 31.851 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 32.772 sec\n",
      "0%                          100%\n",
      "[##############################] | ETA[sec]: 0.000 \n",
      "Total time elapsed: 35.251 sec\n"
     ]
    }
   ],
   "source": [
    "sobol_values = saltelli_sample.sample(problem, 1)\n",
    "sobol_results = np.zeros(sobol_values.shape[0])\n",
    "\n",
    "for row in range(sobol_values.shape[0]):\n",
    "    sobol_results[row] = objective_function(sobol_values[row, :])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Parameter S1 S1_conf ST ST_conf\n",
      "charge rate -0.030786 0.000000 0.000003 0.000000\n",
      "battery capacity -0.007104 0.000000 0.000000 0.000000\n",
      "energy consumption 24.036327 0.000000 2.016670 0.000000\n",
      "\n",
      "Parameter_1 Parameter_2 S2 S2_conf\n",
      "charge rate battery capacity 0.105155 0.000000\n",
      "charge rate energy consumption 0.120143 0.000000\n",
      "battery capacity energy consumption 0.018379 0.000000\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'S1': array([ -3.07863712e-02,  -7.10410046e-03,   2.40363272e+01]),\n",
       " 'S1_conf': array([  2.05027445e-17,   0.00000000e+00,   6.99827013e-15]),\n",
       " 'S2': array([[        nan,  0.10515483,  0.12014306],\n",
       "        [        nan,         nan,  0.01837918],\n",
       "        [        nan,         nan,         nan]]),\n",
       " 'S2_conf': array([[             nan,   8.20109781e-17,   0.00000000e+00],\n",
       "        [             nan,              nan,   0.00000000e+00],\n",
       "        [             nan,              nan,              nan]]),\n",
       " 'ST': array([  3.30838132e-06,   1.76163831e-07,   2.01666968e+00]),\n",
       " 'ST_conf': array([  8.34258810e-22,   0.00000000e+00,   1.74956753e-15])}"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sobol_analyze.analyze(\n",
    "    problem, \n",
    "    sobol_results,\n",
    "    print_to_console=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
