{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Footprint Tracer\n",
    "\n",
    "## Problem Statement:\n",
    "\n",
    "Every day, an empty truck arrives at a construction site. The truck is then loaded with sand, before it is sent back to where it came from. We know the trajectory of the empty truck costs us 10 liters of fuel. For every additional tonne of sand that the truck carries, our cost increases with 1 liter. The amount of sand that's loaded in the truck follows a normal distribution with a mean of 9 tonnes and a standard derivation of 2 tonnes.\n",
    "\n",
    "We now want to know how many liters of fuel we have to pay for over the course of 30 days.\n",
    "\n",
    "_**Note:** This is a fictional problem._\n",
    "\n",
    "## Our Model:\n",
    "\n",
    "This problem can be modeled quite easily. We require a `ConstantGenerator` that indicates the arrival of the empty truck every day. Next, we will use the `Random` block to fill our truck, before sending it back (we don't require additional information, so we'll use the `Finish` block)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pypdevs.DEVS import CoupledDEVS\n",
    "\n",
    "from pypdevsbbl.generic.generators import ConstantGenerator\n",
    "from pypdevsbbl.generic.math import Random\n",
    "from pypdevsbbl.extra.rngstreams.distributions import normal\n",
    "from pypdevsbbl.generic.routing import Finish\n",
    "\n",
    "class Model(CoupledDEVS):\n",
    "    def __init__(self):\n",
    "        CoupledDEVS.__init__(self, \"Model\")\n",
    "        \n",
    "        self.arrivals = self.addSubModel(ConstantGenerator(\"Arrivals\"))             # -- [1]\n",
    "        self.filler = self.addSubModel(Random(\"Filler\", dist=normal, args=(9, 2)))  # -- [2]\n",
    "        self.departures = self.addSubModel(Finish(\"Departures\"))\n",
    "        \n",
    "        self.connectPorts(self.arrivals.output, self.filler.input)\n",
    "        self.connectPorts(self.filler.random, self.departures.input)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, this model is quite simple. There are only three blocks and two connections. Let's discuss some of the choices for this model.\n",
    "\n",
    "1. The smalles time unit in the problem is a 'day', so we will use _dt_ as a day.\n",
    "2. We will use seed 0 for our random numbers. Additionally, all weight measurements required are in tonnes, so we will use a distribution with $\\mu = 9$ and $\\sigma = 2$.\n",
    "\n",
    "Let's also create a simulator for our model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pypdevs.simulator import Simulator\n",
    "\n",
    "model = Model()\n",
    "sim = Simulator(model)\n",
    "sim.setClassicDEVS()\n",
    "sim.setTerminationTime(30)  # -- [3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. We need to trace the cost for 30 days.\n",
    "\n",
    "## The Tracer:\n",
    "\n",
    "Obviously, our problem is slightly more complicated than our simple model. We need to keep track of all the internal costs. Let's measure them in _liters_."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pypdevsbbl.tracers import setTracer, FootprintTracer\n",
    "\n",
    "setTracer(sim, FootprintTracer, None, \"Liters\", {  # -- [4]\n",
    "    model.arrivals.output: 10,                     # -- [5]\n",
    "    model.filler.random: lambda v: 10 + v          # -- [6]\n",
    "}, False)                                          # -- [7]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "4. Print the trace of the `\"Liters\"` tracer to the output.\n",
    "5. The empty trajectory costs us 10 liters.\n",
    "6. _For every additional tonne of sand that the truck carries, our cost increases with 1 liter._\n",
    "7. When `True`, all internal tracing information will be shown. We only need the total cost, hence `False`.\n",
    "\n",
    "Finally, all we need to do is simulate our model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FOOTPRINT OF 'Liters':\n",
      "======================\n",
      "\tTOTAL FOOTPRINT COST: 888.6493728785837\n",
      "\tCOST PER ITEM:\n",
      "\t\t0: 310.000000\n",
      "\t\t12.661339428579787: 22.661339\n",
      "\t\t11.611496620730488: 21.611497\n",
      "\t\t11.438578802792588: 21.438579\n",
      "\t\t10.875759253830818: 20.875759\n",
      "\t\t10.47305793133847: 20.473058\n",
      "\t\t10.386044699903229: 20.386045\n",
      "\t\t10.37135379435855: 20.371354\n",
      "\t\t9.928889954082193: 19.928890\n",
      "\t\t9.616303747841696: 19.616304\n",
      "\t\t9.558966383881389: 19.558966\n",
      "\t\t9.381071422452315: 19.381071\n",
      "\t\t9.167615655777576: 19.167616\n",
      "\t\t9.123326983674138: 19.123327\n",
      "\t\t8.903578810533597: 18.903579\n",
      "\t\t8.545239785211468: 18.545240\n",
      "\t\t8.25929472593254: 18.259295\n",
      "\t\t8.099858311498087: 18.099858\n",
      "\t\t8.056359598550848: 18.056360\n",
      "\t\t8.003682150705385: 18.003682\n",
      "\t\t7.945298065254054: 17.945298\n",
      "\t\t7.589826041678762: 17.589826\n",
      "\t\t7.466599757561997: 17.466600\n",
      "\t\t7.440189408322134: 17.440189\n",
      "\t\t7.360513065936512: 17.360513\n",
      "\t\t7.261479680428768: 17.261480\n",
      "\t\t7.180914643160888: 17.180915\n",
      "\t\t6.87050767813408: 16.870508\n",
      "\t\t6.802956936409858: 16.802957\n",
      "\t\t6.7187319125555245: 16.718732\n",
      "\t\t6.196463637428672: 16.196464\n",
      "\t\t5.354073990037405: 15.354074\n",
      "\n"
     ]
    }
   ],
   "source": [
    "sim.simulate()"
   ]
  }
 ],
 "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.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
