{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Solutions - Running Co-simulations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Load required libraries:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import sys\n",
    "sys.path.append(\"../../cosimlibrary\")\n",
    "from cosimlibrary.loader import FMULoader\n",
    "from cosimlibrary.scenario import Connection, VarType, SignalType, OutputConnection, CosimScenario\n",
    "from cosimlibrary.jacobi_runner import JacobiRunner"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 2: Implement the Virtual FMUS\n",
    "\n",
    "Copy your implementation of the virtual fmu MSD1, and implement the virtual fmu MSD2.\n",
    "\n",
    "Then, run a co-simulation with them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from cosimlibrary.virtual_fmus import VirtualFMU\n",
    "from fmpy.fmi2 import fmi2True\n",
    "\n",
    "# TODO The MSD1 Virtual FMU has been implemented in the previous exercise. Copy that code and paste it here.\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 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",
    "\n",
    "            \n",
    "# Implementation of the virtual fmu MSD2\n",
    "class MSD2(VirtualFMU):\n",
    "    def __init__(self, instanceName):\n",
    "        ref = 0\n",
    "        self.x2 = ref;\n",
    "        ref += 1\n",
    "        self.v2 = ref;\n",
    "        ref += 1\n",
    "        self.m2 = ref;\n",
    "        ref += 1\n",
    "        self.c2 = ref;\n",
    "        ref += 1\n",
    "        self.d2 = ref;\n",
    "        ref += 1\n",
    "        self.cc = ref;\n",
    "        ref += 1\n",
    "        self.dc = ref;\n",
    "        ref += 1\n",
    "        self.fk = ref;\n",
    "        ref += 1\n",
    "        self.x1 = ref;\n",
    "        ref += 1\n",
    "        self.v1 = ref;\n",
    "        ref += 1\n",
    "\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",
    "        \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",
    "            \n",
    "            # Compute fk\n",
    "            self.state[self.fk] = self.state[self.cc] * (self.state[self.x2] - self.state[self.x1]) \\\n",
    "                                  + self.state[self.dc] * (self.state[self.v2] - self.state[self.v1])\n",
    "\n",
    "            # TODO Compute derivative of x2\n",
    "            der_x = self.state[self.v2]\n",
    "            \n",
    "            # TODO Compute derivative of v2\n",
    "            der_v = (1.0 / self.state[self.m2]) * (- self.state[self.c2] * self.state[self.x2]\n",
    "                                                  - self.state[self.d2] * self.state[self.v2]\n",
    "                                                  - self.state[self.fk])\n",
    "\n",
    "            # TODO Implement forward euler step.\n",
    "            self.state[self.x2] = self.state[self.x2] + der_x * h\n",
    "            self.state[self.v2] = self.state[self.v2] + der_v * h\n",
    "            \n",
    "            # Advance time\n",
    "            t=t+h"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<cosimlibrary.scenario.CosimScenario at 0x24ca20f6240>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create cosim scenario with the two virtual FMUs\n",
    "msd1 = MSD1(\"msd1\")\n",
    "msd1.instantiate()\n",
    "msd2 = MSD2(\"msd2\")\n",
    "msd2.instantiate()\n",
    "\n",
    "# Output of msd1 connects to the input of msd2.\n",
    "msd1_out = Connection(value_type=VarType.REAL,\n",
    "                              signal_type=SignalType.CONTINUOUS,\n",
    "                              source_fmu=msd1,\n",
    "                              target_fmu=msd2,\n",
    "                              source_vr=[msd1.x1, msd1.v1],\n",
    "                              target_vr=[msd2.x1, msd2.v1])\n",
    "\n",
    "# Output of msd2 connects to the input of msd1\n",
    "msd1_in = Connection(value_type=VarType.REAL,\n",
    "                     signal_type=SignalType.CONTINUOUS,\n",
    "                     source_fmu=msd2,\n",
    "                     target_fmu=msd1,\n",
    "                     source_vr=[msd2.fk],\n",
    "                     target_vr=[msd1.fk])\n",
    "\n",
    "# This connection informs the library that we are also interested in plotting the values of msd2\n",
    "msd2_out = OutputConnection(value_type=VarType.REAL,\n",
    "                            signal_type=SignalType.CONTINUOUS,\n",
    "                            source_fmu=msd2,\n",
    "                            source_vr=[msd2.x2, msd2.v2])\n",
    "\n",
    "connections = [msd1_out, msd1_in]\n",
    "output_connections = [msd1_out, msd1_in, msd2_out] # Controls which signals are plotted\n",
    "\n",
    "# Sets initial values and parameters\n",
    "real_parameters = {msd1: \n",
    "                ([msd1.c1, \n",
    "                  msd1.d1, \n",
    "                  msd1.m1, \n",
    "                  msd1.x1, \n",
    "                  msd1.v1],\n",
    "                  [5.0, 1.0, 1.0, 1.0, 0.0]),\n",
    "                  msd2: \n",
    "                  ([msd2.c2, \n",
    "                    msd2.d2, \n",
    "                    msd2.m2, \n",
    "                    msd2.cc, \n",
    "                    msd2.dc, \n",
    "                    msd2.x2, \n",
    "                    msd2.v2],\n",
    "                  [0.1, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0]),}\n",
    "\n",
    "scenario = CosimScenario(\n",
    "            fmus=[msd1, msd2],\n",
    "            connections=connections,\n",
    "            step_size=0.01,\n",
    "            print_interval=0.1,\n",
    "            stop_time=7.0,\n",
    "            outputs=output_connections,\n",
    "            real_parameters=real_parameters)\n",
    "\n",
    "scenario\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate FMUs and run Jacobi cosim\n",
    "msd1.instantiate(loggingOn=False)\n",
    "msd2.instantiate(loggingOn=False)\n",
    "\n",
    "jacobi = JacobiRunner()\n",
    "\n",
    "results = jacobi.run_cosim(scenario, None)\n",
    "\n",
    "msd1.terminate()\n",
    "msd2.terminate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 12996 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot\n",
    "plt.figure(1)\n",
    "plt.xlabel(\"time\")\n",
    "plt.ylabel('position (m)')\n",
    "plt.plot(results.timestamps, results.signals[msd1.instanceName][msd1.x1], 'o', markersize=3, label=\"x1_jacobi\")\n",
    "plt.plot(results.timestamps, results.signals[msd2.instanceName][msd2.x2], 'o', markersize=3, label=\"x2_jacobi\")\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
}
