{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Solutions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import sys\n",
    "sys.path.append(\"../../cosimlibrary\")\n",
    "from cosimlibrary.loader import FMULoader"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 1: Give input to Mass Spring Damper System"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Repeat the above steps, but this time, at each co-simulation step, define the value of `fk` so that `fk(t)=sin(t)`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 13892 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Import sin function\n",
    "import math\n",
    "\n",
    "# Load msd1\n",
    "msd1 = FMULoader.load(\"../../fmus/MassSpringDamper1.fmu\", \"msd1\", None)\n",
    "\n",
    "# Instantiate FMU\n",
    "msd1.fmu.instantiate(loggingOn=False) \n",
    "\n",
    "# Setup experiment\n",
    "start_time = 0.0\n",
    "stop_time = 7.0\n",
    "msd1.fmu.setupExperiment(tolerance=1e-8, startTime=start_time, stopTime=stop_time)\n",
    "\n",
    "# Enter Initialization Mode\n",
    "msd1.fmu.enterInitializationMode()\n",
    "\n",
    "# Set Parameters and Initial Values\n",
    "msd1.fmu.setReal([msd1.vars['fk'].valueReference], [0.0])\n",
    "msd1.fmu.setReal([msd1.vars['c1'].valueReference], [1.0])\n",
    "msd1.fmu.setReal([msd1.vars['d1'].valueReference], [1.0])\n",
    "msd1.fmu.setReal([msd1.vars['m1'].valueReference], [1.0])\n",
    "msd1.fmu.setReal([msd1.vars['x1'].valueReference], [1.0])\n",
    "msd1.fmu.setReal([msd1.vars['v1'].valueReference], [0.0])\n",
    "\n",
    "# Exit Initialization Mode\n",
    "msd1.fmu.exitInitializationMode()\n",
    "\n",
    "# Define timestep \n",
    "H = 0.01\n",
    "\n",
    "# Will store current cosim time\n",
    "time = 0.0\n",
    "\n",
    "# Will store the timepoints\n",
    "t_range = [time]\n",
    "\n",
    "# Will store current position of the mass\n",
    "xs = [msd1.fmu.getReal([msd1.vars['x1'].valueReference])[0]]\n",
    "\n",
    "# Run cosim steps\n",
    "while (time+H)<=stop_time:\n",
    "    msd1.fmu.doStep(time, H)\n",
    "    # Set input\n",
    "    msd1.fmu.setReal([msd1.vars['fk'].valueReference], [math.sin(time)])\n",
    "    time = time + H\n",
    "    xs.append(msd1.fmu.getReal([msd1.vars['x1'].valueReference])[0])\n",
    "    t_range.append(time)\n",
    "\n",
    "# Terminate FMU and cleanup\n",
    "msd1.fmu.terminate()\n",
    "FMULoader.unload(msd1)\n",
    "\n",
    "# Plot\n",
    "plt.figure(1)\n",
    "plt.plot(t_range, xs, 'o', markersize=3, label='x1')\n",
    "plt.xlabel('t (s)')\n",
    "plt.ylabel('position (m)')\n",
    "\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 2: Create your Own (Virtual) FMU\n",
    "\n",
    "Create your own FMU of the mass spring damper system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from cosimlibrary.virtual_fmus import VirtualFMU\n",
    "from fmpy.fmi2 import fmi2True\n",
    "\n",
    "class MSD1(VirtualFMU):\n",
    "    def __init__(self, instanceName):\n",
    "        # Declare value references for variables\n",
    "        ref = 0\n",
    "        self.x1 = ref;\n",
    "        ref += 1\n",
    "        self.v1 = ref;\n",
    "        ref += 1\n",
    "        self.m1 = ref;\n",
    "        ref += 1\n",
    "        self.c1 = ref;\n",
    "        ref += 1\n",
    "        self.d1 = ref;\n",
    "        ref += 1\n",
    "        self.fk = ref;\n",
    "        ref += 1\n",
    "        super().__init__(instanceName, ref)\n",
    "\n",
    "    def doStep(self, currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint=fmi2True):\n",
    "        h = 1e-3 # Internal step size\n",
    "        t = currentCommunicationPoint\n",
    "        maxT = t + communicationStepSize # Time to terminate the doStep\n",
    "        while (t < maxT):\n",
    "            if t+h > maxT:\n",
    "                h = (maxT - t) # Make sure we leave the co-sim step at time maxT\n",
    "            # Compute derivative of x\n",
    "            der_x = self.state[self.v1]\n",
    "            # Compute derivative of v\n",
    "            der_v = (1.0 / self.state[self.m1]) * (- self.state[self.c1] * self.state[self.x1]\n",
    "                                                  - self.state[self.d1] * self.state[self.v1]\n",
    "                                                  + self.state[self.fk])\n",
    "\n",
    "            # TODO: Implement forward euler or midpoint step\n",
    "            self.state[self.x1] = self.state[self.x1] + der_x * h\n",
    "            self.state[self.v1] = self.state[self.v1] + der_v * h\n",
    "            \n",
    "            # Advance time\n",
    "            t=t+h\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 3: Run Co-simulations with Varying Step Sizes\n",
    "\n",
    "Define a function that runs a co-simulation with the previous virtual FMU, for a given communication step size.\n",
    "Then run multiple co-simulations with varying communication step sizes, and explain the results: \n",
    "- Why do the results differ despite the fact that the solver used is the same (and has the same step size)?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 22456 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Import sin function\n",
    "import math\n",
    "\n",
    "def run_virtual_cosim(H):\n",
    "    # Load msd1. No need to use Loader, as this is a virtual FMU.\n",
    "    msd1fmu = MSD1('msd1')\n",
    "\n",
    "    # Instantiate FMU\n",
    "    msd1fmu.instantiate(loggingOn=False) \n",
    "\n",
    "    # Setup experiment\n",
    "    msd1fmu.setupExperiment(tolerance=1e-8, startTime=start_time, stopTime=stop_time)\n",
    "\n",
    "    # Enter Initialization Mode\n",
    "    msd1fmu.enterInitializationMode()\n",
    "\n",
    "    # Set Parameters and Initial Values\n",
    "    msd1fmu.setReal([msd1fmu.fk], [0.0])\n",
    "    msd1fmu.setReal([msd1fmu.c1], [1.0])\n",
    "    msd1fmu.setReal([msd1fmu.d1], [1.0])\n",
    "    msd1fmu.setReal([msd1fmu.m1], [1.0])\n",
    "    msd1fmu.setReal([msd1fmu.x1], [0.0])\n",
    "    msd1fmu.setReal([msd1fmu.v1], [0.0])\n",
    "\n",
    "    # Exit Initialization Mode\n",
    "    msd1fmu.exitInitializationMode()\n",
    "\n",
    "    # Will store current cosim time\n",
    "    time = 0.0\n",
    "\n",
    "    # Will store the timepoints\n",
    "    t_range = [time]\n",
    "\n",
    "    # Will store current position of the mass\n",
    "    xs = [msd1fmu.getReal([msd1fmu.x1])[0]]\n",
    "\n",
    "    # Run cosim steps\n",
    "    while (time+H)<=stop_time:\n",
    "        msd1fmu.doStep(time, H)\n",
    "        # Set input\n",
    "        msd1fmu.setReal([msd1fmu.fk], [math.sin(time)])\n",
    "        time = time + H\n",
    "        xs.append(msd1fmu.getReal([msd1fmu.x1])[0])\n",
    "        t_range.append(time)\n",
    "\n",
    "    # Terminate FMU and cleanup\n",
    "    msd1fmu.terminate()\n",
    "    \n",
    "    return t_range, xs\n",
    "\n",
    "# Run cosims\n",
    "t0001, xs0001 = run_virtual_cosim(0.001)\n",
    "t001, xs001 = run_virtual_cosim(0.01)\n",
    "t01, xs01 = run_virtual_cosim(0.1)\n",
    "t05, xs05 = run_virtual_cosim(0.5)\n",
    "\n",
    "# Plot\n",
    "plt.figure(1)\n",
    "plt.plot(t0001, xs0001, 'o', markersize=3, label='xs0001')\n",
    "plt.plot(t001, xs001, 'o', markersize=3, label='xs001')\n",
    "plt.plot(t01, xs01, 'o', markersize=3, label='xs01')\n",
    "plt.plot(t05, xs05, 'o', markersize=3, label='xs05')\n",
    "plt.xlabel('t (s)')\n",
    "plt.ylabel('position (m)')\n",
    "\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  }
 ],
 "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.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
