{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\nExample demonstrating ways to use future loading. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from progpy.models import BatteryCircuit\nfrom statistics import mean\nfrom numpy.random import normal\n\ndef run_example(): \n m = BatteryCircuit()\n\n ## Example 1: Variable loading \n def future_loading(t, x=None):\n # Variable (piece-wise) future loading scheme \n if (t < 600):\n i = 2\n elif (t < 900):\n i = 1\n elif (t < 1800):\n i = 4\n elif (t < 3000):\n i = 2 \n else:\n i = 3\n return m.InputContainer({'i': i})\n \n # Simulate to threshold\n options = {\n 'save_freq': 100, # Frequency at which results are saved\n 'dt': 2 # Timestep\n }\n simulated_results = m.simulate_to_threshold(future_loading, **options)\n\n # Now lets plot the inputs and event_states\n simulated_results.inputs.plot(ylabel = 'Variable Load Current (amps)')\n simulated_results.event_states.plot(ylabel = 'Variable Load Event State')\n\n ## Example 2: Moving Average loading \n # This is useful in cases where you are running reoccuring simulations, and are measuring the actual load on the system, \n # but dont have a good way of predicting it, and you expect loading to be steady\n\n def future_loading(t, x=None):\n return future_loading.load\n future_loading.load = m.InputContainer({key : 0 for key in m.inputs})\n\n # Lets define another function to handle the moving average logic\n window = 10 # Number of elements in window\n def moving_avg(i):\n for key in m.inputs:\n moving_avg.loads[key].append(i[key])\n if len(moving_avg.loads[key]) > window:\n del moving_avg.loads[key][0] # Remove first item\n\n # Update future loading eqn\n future_loading.load = {key : mean(moving_avg.loads[key]) for key in m.inputs} \n moving_avg.loads = {key : [] for key in m.inputs} \n\n # OK, we've setup the logic of the moving average. \n # Now lets say you have some measured loads to add\n measured_loads = [10, 11.5, 12.0, 8, 2.1, 1.8, 1.99, 2.0, 2.01, 1.89, 1.92, 2.01, 2.1, 2.2]\n \n # We're going to feed these into the future loading eqn\n for load in measured_loads:\n moving_avg({'i': load})\n \n # Now the future_loading eqn is setup to use the moving average of whats been seen\n # Simulate to threshold\n simulated_results = m.simulate_to_threshold(future_loading, **options)\n\n # Now lets plot the inputs and event_states\n simulated_results.inputs.plot(ylabel = 'Moving Average Current (amps)')\n simulated_results.event_states.plot(ylabel = 'Moving Average Event State')\n\n # In this case, this estimate is wrong because loading will not be steady, but at least it would give you an approximation.\n\n # If more measurements are received, the user could estimate the moving average here and then run a new simulation. \n\n ## Example 3: Gaussian Distribution \n # In this example we will still be doing a variable loading like the first option, but we are going to use a \n # gaussian distribution for each input. \n\n def future_loading(t, x=None):\n # Variable (piece-wise) future loading scheme \n if (t < 600):\n i = 2\n elif (t < 900):\n i = 1\n elif (t < 1800):\n i = 4\n elif (t < 3000):\n i = 2 \n else:\n i = 3\n return m.InputContainer({'i': normal(i, future_loading.std)})\n future_loading.std = 0.2\n\n # Simulate to threshold\n simulated_results = m.simulate_to_threshold(future_loading, **options)\n\n # Now lets plot the inputs and event_states\n simulated_results.inputs.plot(ylabel = 'Variable Gaussian Current (amps)')\n simulated_results.event_states.plot(ylabel = 'Variable Gaussian Event State')\n\n # Example 4: Gaussian- increasing with time\n # For this we're using moving average. This is realistic because the further out from current time you get, \n # the more uncertainty there is in your prediction. \n\n def future_loading(t, x=None):\n std = future_loading.base_std + future_loading.std_slope * (t - future_loading.t)\n return {key : normal(future_loading.load[key], std) for key in future_loading.load.keys()}\n future_loading.load = {key : 0 for key in m.inputs} \n future_loading.base_std = 0.001\n future_loading.std_slope = 1e-4\n future_loading.t = 0\n\n # Lets define another function to handle the moving average logic\n window = 10 # Number of elements in window\n def moving_avg(i):\n for key in m.inputs:\n moving_avg.loads[key].append(i[key])\n if len(moving_avg.loads[key]) > window:\n del moving_avg.loads[key][0] # Remove first item\n\n # Update future loading eqn\n future_loading.load = {key : mean(moving_avg.loads[key]) for key in m.inputs} \n moving_avg.loads = {key : [] for key in m.inputs} \n\n # OK, we've setup the logic of the moving average. \n # Now lets say you have some measured loads to add\n measured_loads = [10, 11.5, 12.0, 8, 2.1, 1.8, 1.99, 2.0, 2.01, 1.89, 1.92, 2.01, 2.1, 2.2]\n \n # We're going to feed these into the future loading eqn\n for load in measured_loads:\n moving_avg({'i': load})\n\n # Simulate to threshold\n simulated_results = m.simulate_to_threshold(future_loading, **options)\n\n # Now lets plot the inputs and event_states\n simulated_results.inputs.plot(ylabel = 'Moving Average Current (amps)')\n simulated_results.event_states.plot(ylabel = 'Moving Average Event State')\n \n # In this example future_loading.t has to be updated with current time before each prediction.\n \n # Example 5 Function of state\n # here we're pretending that input is a function of SOC. It increases as we approach SOC\n\n def future_loading(t, x=None):\n if x is not None:\n event_state = future_loading.event_state(x)\n return m.InputContainer({'i': future_loading.start + (1-event_state['EOD']) * future_loading.slope}) # default\n return m.InputContainer({'i': future_loading.start})\n future_loading.t = 0\n future_loading.event_state = m.event_state\n future_loading.slope = 2 # difference between input with EOD = 1 and 0. \n future_loading.start = 0.5\n\n # Simulate to threshold\n simulated_results = m.simulate_to_threshold(future_loading, **options)\n\n # Now lets plot the inputs and event_states\n simulated_results.inputs.plot(ylabel = 'Moving Average Current (amps)')\n simulated_results.event_states.plot(ylabel = 'Moving Average Event State')\n\n # In this example future_loading.t has to be updated with current time before each prediction.\n\n # Show plots\n import matplotlib.pyplot as plt\n plt.show()\n\n# This allows the module to be executed directly \nif __name__ == '__main__':\n run_example()" ] } ], "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.8.9" } }, "nbformat": 4, "nbformat_minor": 0 }