{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following tutorial introduces dynamic simulations (RMS/EMT), plotting and exporting of results (to pandas or csv) using *powfacpy*.\n",
    "\n",
    "Let's again set up *PowerFactory* and create an instance of the class `PFPlotInterface`. This class inherits from `PFActiveProject` (so those methods are available). We use the '39-Bus New England' example from *PowerFactory* (see File->Examples)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "sys.path.append(r'C:\\Program Files\\DIgSILENT\\PowerFactory 2023 SP3\\Python\\3.11') # you may use a different directory\n",
    "# Get the PF app\n",
    "import powerfactory\n",
    "app = powerfactory.GetApplication()\n",
    "import powfacpy\n",
    "app.Show()\n",
    "app.ActivateProject(r\"powfacpy\\39-bus_new_engl_copy_where_tests_are_run\") # You may change the project path.\n",
    "pfpi = powfacpy.PFPlotInterface(app)\n",
    "study_case = pfpi.activate_study_case(r\"Study Cases\\2.1 Simulation Fault Bus 16 Stable\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Plotting inside *PowerFactory*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In *PowerFactory*, monitored result variables (to be later plotted or exported) need to be specified before the simulation. The easiest way to plot results using *powfacpy* is to simply use the method `plot` before the simulation, which automaticallly adds a variable to the monitored variables and plots it as soon as the simulation has run.\n",
    "\n",
    "First, we set the active plot (starting from a clean graphics board by clearing all plot pages)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfpi.clear_plot_pages() # Start from a clean graphics board\n",
    "pfpi.set_active_plot(\"Plot 1\", \"Plot page 1\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The method `set_active_plot` takes the name of the plot and the name of the page of the plot as input arguments. If the plot (or the page) does not exist, it is created. If there should be several plots on one page, the method is called several times with the same page name but different plot name (e.g. `pfpi.set_active_plot(\"Plot 2\", \"Plot page 1\")`). \n",
    "\n",
    "All plot commmands are applied to the active plot. We can now add curves to the plot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Curve of variable \"s:P1\"\n",
    "pfpi.plot(r\"Network Model\\Network Data\\Grid\\G 01\", \"s:P1\")\n",
    "\n",
    "# Curve of variable \"s:Q1\"\n",
    "g_01 = pfpi.get_unique_obj(r\"Network Model\\Network Data\\Grid\\G 01\")\n",
    "results_obj = pfpi.get_unique_obj(r\"Study Cases\\2.1 Simulation Fault Bus 16 Stable\\All calculations\")\n",
    "# Plot with keyword arguments\n",
    "pfpi.plot(g_01, # obj or str\n",
    "    \"s:Q1\", \n",
    "    linestyle = 3, \n",
    "    linewidth = 200, \n",
    "    color = 5,\n",
    "    label = \"Reactive power setpoint\",\n",
    "    results_obj = results_obj)"
   ]
  },
  {
   "attachments": {
    "image-2.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAADgAAAAXCAYAAABefIz9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGNSURBVFhHY/zw4cN/hmEMmKD0sAXD3oPwJPr48WMGWVlZsOBAgbicUigLFSya0g1lkQ5GY3AgACwmYTH34+dPhukLljEkRYYw8PPxgsWIBSTH4J8/fxgWrVoP5WGC+SvWgtVQC/z9+49h6rwlDOcvX2OYPGcRyWaT5MGPnz4zdEyexXD4xBmoCCbYf+QEQ8ekmWC11ADL1m1iuHj1Bpj98MkzIH8LmE0sICmJ9s+cDw5JYoChrhZDYXoilEcaQE+iIAASI6ewISkGMxOiGPS1NcBskGXYMAjoa2uC1Q4GQJQHQaEHwhzs7AwFaYkMrvbWUBlM4OZgA1STAFYL0zeQgORChpmZiSE2NADKwwQxIf5gNYMFEJUHKY0FWNIlFsDsQ9YHEiPVnHsPH5Meg0MJgKozkmKQGjFBDEDWt3j1BobdB4+C+SDgaGPBkBgRDOXhB4+fPR/8MRgV5AcvudWUFBhigXmcWCArJTn8m2qDyoMwj6EDUpM4MhjWhQwIwGNwuIJhH4OjHhzagIEBAJxCw7Zvf1aPAAAAAElFTkSuQmCC"
    },
    "image.png": {
     "image/png": "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJBSURBVEhLY/zw4cN/BhoDJihNUzB8LMEbJ79+/2bYf+QEw6VrNxiev3zNwMLCwiAjKcFgaqDLYG6sz8DERJwbcVpy7dYdhmnzlzJ8+vwFKoIKpCXEGXJT4hikJMSgIrgBVkuu3LjFMGHmArBPuLm5GJxtLBkU5KQZfvz8xXDt5m2G42cuMPz9+5dBgI+Xoaogk0FCTBSqEzvAsARkcFlTF8O79x8YVJUUGIoykhi4uTihshDw8MlThp5pcxk+fvrMoKmmzFCZlwGVwQ4wAvXQ8VNgC7g4ORmK0hMxLAABeRlphoz4KDD7+q27DLfuPQCzcQEMS67dvAum7a1MwUGFC2irq4AtA4FrN++AaVwAw5JnL1+CaTlpKTCNDyjIQix5/uoVmMYFMCxhZ2MD079+/QbT+MCPX7/ANCMQ4gMYlsBSylVgKsIFNu7YwxCXU8pw8uwFMP/Y6XNg/uzFK8F8dIBhiY25MZg+df4Sw20cEerr5sSgo6EG5UEAKJPGRwRBeagAwxKQZhVFeTC7Z/pchgtXrjH8/4+alUA5PSM+kkGAnw/MBwVxTnIsAxsrK5iPDrBmxg8fPzG0TpjO8PL1GzBfUlyMQU5GiuEnMDP6uDowqCkrgsVv3LnH0D5xBkNGXCSDpakhWAwbwFmsvP/wkWHp2k3gYEMGbg42DDEh/lAeAzhIQZkWH8BpCQw8evocmOHuMDx68oyBk5ODQVlBjsHSBLersQGCllADYEQ8LcCoJSQBuljC2D5rLY1TFwMDAH2iyU4ztIQJAAAAAElFTkSuQmCC"
    }
   },
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NOTE:** If the plot is not shown properly in *PowerFactory*, click on refresh ![image.png](attachment:image.png) and rescale ![image-2.png](attachment:image-2.png)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The method `plot` takes the PF object (or its path) and a results variable as first and second input. Furthermore, keyword arguments can be specified as shown for the second curve. \n",
    "\n",
    "Now we can execute a dynamic simulation using the dynamic simulation interface class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfds = powfacpy.PFDynSimInterface(app)\n",
    "pfds.initialize_sim({\"iopt_sim\": \"rms\"})\n",
    "pfds.run_sim({\"tstop\": 0.5})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The two methods initialize and run the simulation. Paramters for the ComInc object (initialization) and the ComSim (run simulation) can be specified in a dictionary. If no parameters need to be set, one can also use:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfds.initialize_and_run_sim()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In some cases, it might be more convenient to add the result variable first, then simulate and finally specifiy the plot:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfpi.clear_curves()\n",
    "pfds.initialize_sim({\"iopt_sim\": \"rms\"})\n",
    "pfds.add_results_variable(g_01, \"s:P1\")\n",
    "pfds.run_sim()\n",
    "pfpi.plot_monitored_variables(g_01, \"s:P1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So the method `add_results_variable` is used to add variables to the monitored vaiables to later export or plot them.\n",
    "\n",
    "*powfacpy* offers more functionality that are helpful in this context. The `ResVar` class offers enumeration subclasses for results variables of various simulation types (e.g. RMS balanced/unbalanced, EMT, load flow balanced/unbalanced). The names of *PowerFactory* result variables can be cryptic. The enumeration classes offer an easy way to get the names and also their short description (just hover over `s_P1` in `RMS_bal.ElmSym.s_P1` in the cell below). Code completion in your IDE helps you find the variable you want without having to look it up in *PowerFactory*. The syntax is\n",
    "\n",
    "`simulation_type`.`class_name`.`variable_name`.`value`\n",
    "\n",
    "e.g.\n",
    "\n",
    "- `RMS_bal.ElmSym.s_P1.value`\n",
    "- `RMS_bal.ElmSym.m_Psum_bus1.value` .\n",
    "\n",
    "In the variable name, colons (\":\") are replaced by underscores (\"_\") to comply with python naming restrictions. The `value` at the end is required to get the name string of the variable. It is assumed that you set `RMS_bal = ResVar.RMS_Bal` as in the cell below.\n",
    "\n",
    "\n",
    "Additionally, `create_dyn_sim_event` in the cell below creates an event, here we add a parameter event. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "from powfacpy.result_variables import ResVar\n",
    "RMS_bal = ResVar.RMS_Bal # we focus on balanced RMS simulation here\n",
    "\n",
    "pfpi.clear_curves()\n",
    "pfds.initialize_sim({\"iopt_sim\": \"rms\"})\n",
    "pfds.add_results_variable(g_01, RMS_bal.ElmSym.s_P1.value)\n",
    "pfds.create_dyn_sim_event(\"Set_turbine_power_of_g01.EvtParam\", \n",
    "                          {\"p_target\": g_01, \n",
    "                           \"variable\": \"pt\", \n",
    "                           \"value\": \"0.5\",\n",
    "                           \"time\": 0})\n",
    "pfds.run_sim()\n",
    "pfpi.plot_monitored_variables(g_01, RMS_bal.ElmSym.s_P1.value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sometimes we have created plots in one study case and want to have those plots also in another study case. This can be achieved by copying the graphics board content (or parts of it) from one study case to another:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [],
   "source": [
    "study_case_1 = pfpi.get_unique_obj(\n",
    "  r\"Study Cases\\2.1 Simulation Fault Bus 16 Stable\")\n",
    "study_case_2 = pfpi.get_unique_obj(\n",
    "  r\"Study Cases\\2.2 Simulation Fault Bus 16 Unstable\")\n",
    "pfpi.copy_graphics_board_content(study_case_1, study_case_2, \"*.GrpPage\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This copies all plot pages (class GrpPage) of Study Case 1 to Study Case 2. The target study case argument can also be a list of cases. Or we can use the method `copy_graphics_board_content_to_all_study_cases` to target all study cases or all cases in a certain folder.\n",
    "\n",
    "# Getting Objects from the Initial Conditions Calculation (ComInc)\n",
    "A common source of error is that users access wrong objects from the active study case without noticing. For example, they access a `Results Object (ElmRes)` which they assume to be used in their time-domain simulation, but actually multiple `ElmRes` objects exist in the study case and the `Initial Conditions Calculation (ComInc)` uses a different one to store simualion results.\n",
    "\n",
    "This can be avoided by getting the objects directly from the `ComInc`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [],
   "source": [
    "elmres = pfpi.get_results_obj_from_initial_conditions_calc()\n",
    "events_folder = pfpi.get_events_folder_from_initial_conditions_calc()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exporting results from *PowerFactory*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simulation results can be exported to a [Pandas DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html) via the `powfacpy.PFResultsInterface`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfri = powfacpy.PFResultsInterface(app)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Export to Pandas DataFrame\n",
    "Let's export dynamic simulation results to a [Pandas DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html). We'll hide the *PowerFactory* app to improve the performance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Grid\\G 01\\s:P1</th>\n",
       "      <th>Grid\\G 01\\s:Q1</th>\n",
       "      <th>Grid\\G 02\\s:P1</th>\n",
       "      <th>Grid\\G 02\\s:Q1</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>time</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>-0.10</th>\n",
       "      <td>1000.0</td>\n",
       "      <td>88.281415</td>\n",
       "      <td>520.811072</td>\n",
       "      <td>198.251784</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>-0.09</th>\n",
       "      <td>1000.0</td>\n",
       "      <td>88.281415</td>\n",
       "      <td>520.811072</td>\n",
       "      <td>198.251784</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       Grid\\G 01\\s:P1  Grid\\G 01\\s:Q1  Grid\\G 02\\s:P1  Grid\\G 02\\s:Q1\n",
       "time                                                                 \n",
       "-0.10          1000.0       88.281415      520.811072      198.251784\n",
       "-0.09          1000.0       88.281415      520.811072      198.251784"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "try:\n",
    "  pfds.app.Hide()\n",
    "  pfds.clear_elmres()\n",
    "  pfds.add_results_variable(\n",
    "    [r\"Network Model\\Network Data\\Grid\\G 01\",\n",
    "    r\"Network Model\\Network Data\\Grid\\G 02\"], \n",
    "    [RMS_bal.ElmSym.s_P1.value,\n",
    "    RMS_bal.ElmSym.s_Q1.value])\n",
    "  pfds.initialize_and_run_sim()\n",
    "  df_sim_res = pfri.export_to_pandas()\n",
    "finally:\n",
    "  pfds.app.Show()  \n",
    "df_sim_res.head(2)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The column headers are the path of the objects relative to the network data folder (by default) together with the result variable name. You can change the relative path by setting the attribute `pfri.truncate_paths_until`:  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>G 01\\s:P1</th>\n",
       "      <th>G 01\\s:Q1</th>\n",
       "      <th>G 02\\s:P1</th>\n",
       "      <th>G 02\\s:Q1</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>time</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>-0.10</th>\n",
       "      <td>1000.0</td>\n",
       "      <td>88.281415</td>\n",
       "      <td>520.811072</td>\n",
       "      <td>198.251784</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>-0.09</th>\n",
       "      <td>1000.0</td>\n",
       "      <td>88.281415</td>\n",
       "      <td>520.811072</td>\n",
       "      <td>198.251784</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       G 01\\s:P1  G 01\\s:Q1   G 02\\s:P1   G 02\\s:Q1\n",
       "time                                               \n",
       "-0.10     1000.0  88.281415  520.811072  198.251784\n",
       "-0.09     1000.0  88.281415  520.811072  198.251784"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pfri.truncate_paths_until = \"Network Model\\\\Network Data\\\\Grid\\\\\"\n",
    "df_sim_res = pfri.export_to_pandas()\n",
    "df_sim_res.head(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*powfacpy* offers methods to easily extract results from the exported pandas DataFrame using *PowerFactory* objects instead of columns headers (**note that changing the column headers of the DataFrame will break this functionality**):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>G 01\\s:P1</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>time</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>-0.10</th>\n",
       "      <td>1000.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>-0.09</th>\n",
       "      <td>1000.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       G 01\\s:P1\n",
       "time            \n",
       "-0.10     1000.0\n",
       "-0.09     1000.0"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 24200 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "p_g_01 = pfri.get_simulation_results_from_dataframe(\n",
    "    df_sim_res, \n",
    "    g_01, # accepts PF objects (or also their path)\n",
    "    RMS_bal.ElmSym.s_P1.value)\n",
    "plt.plot(p_g_01)\n",
    "\n",
    "p_g_01.head(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look a more complicated example where you want to examine the active and reactive power of the synchronous machines `G 01` and `G 02`.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "# SM objects\n",
    "sm_objs = [pfri.get_unique_obj(r\"Network Model\\Network Data\\Grid\\G 01\"),\n",
    "           pfri.get_unique_obj(r\"Network Model\\Network Data\\Grid\\G 02\")]\n",
    "\n",
    "# Results variables\n",
    "sm_res_var = [RMS_bal.ElmSym.s_P1.value, RMS_bal.ElmSym.s_Q1.value]\n",
    "\n",
    "pfri.clear_elmres()\n",
    "pfri.add_results_variable(sm_objs, sm_res_var)\n",
    "pfdi = powfacpy.PFDynSimInterface(app)\n",
    "pfdi.initialize_and_run_sim()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We want to add a legend to the plots. As the default variable and object names might not be very descriptive, *powfacpy* allows to define aliases, e.g. to be used later for labels in plots:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "pfri.variable_aliases = {\n",
    "  RMS_bal.ElmSym.s_P1.value: \"Active Power\", \n",
    "  RMS_bal.ElmSym.s_Q1.value: \"Reactive Power\",\n",
    "} # note that RMS_bal.ElmSym.s_P1.value gives 's:P1'\n",
    "\n",
    "pfri.obj_aliases = {\n",
    "  \"G 01\": \"Synch. Maschine 1 (large)\",\n",
    "  \"G 02\": \"Synch. Maschine 2\",\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see how the `variable_aliases` dictionary can then be used when plotting active and reactive power of `G 01` using `replace_variable_aliases`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x1c684f20f70>"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 36164 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "df_sim_res = pfri.export_to_pandas()\n",
    "for var in sm_res_var:\n",
    "  res = pfri.get_simulation_results_from_dataframe(\n",
    "    df_sim_res, \n",
    "    sm_objs[0], # object or its path\n",
    "    var)\n",
    "  label = pfri.replace_variable_aliases(var)\n",
    "  plt.plot(res, label=label)\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instead of `s:P1` or `s:Q1`, the replacements defined in the dictionaries above are used for the labels.\n",
    "\n",
    "Or in a similar fashion, using also the `object_aliases` to plot the reactive power of both machines:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Index(['G 01\\s:P1', 'G 01\\s:Q1', 'G 02\\s:P1', 'G 02\\s:Q1'], dtype='object')"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_sim_res.columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x1c683f2fa00>"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 38952 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "var = RMS_bal.ElmSym.s_P1.value\n",
    "for sm in sm_objs:\n",
    "  res = pfri.get_simulation_results_from_dataframe(\n",
    "    df_sim_res, \n",
    "    sm, \n",
    "    var)\n",
    "  label = pfri.replace_object_aliases(sm.loc_name) + \" | \" + pfri.replace_variable_aliases(var)\n",
    "  plt.plot(res, label=label)\n",
    "plt.legend()  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hence, the dictionaries are a clean option to define aliases and reuse them anywhere in your code.\n",
    "\n",
    "Note that `get_simulation_results_from_dataframe` also accepts a list of objects (as well as a list of variables). You can easily get various time series results and then process the data using pandas (e.g. calculate mean or average):  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x1c6919e1ae0>"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 74084 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "var = RMS_bal.ElmSym.s_P1.value\n",
    "res = pfri.get_simulation_results_from_dataframe(\n",
    "    df_sim_res, \n",
    "    sm_objs, # list with G 01 and G 02\n",
    "    var)\n",
    "\n",
    "mean = res.mean(1) # pandas method to get mean over axis 1\n",
    "sum = res.sum(1)\n",
    "\n",
    "# Plot active power of individual SMs\n",
    "for sm_num, sm_obj in enumerate(sm_objs):\n",
    "    label = pfri.replace_object_aliases(sm_obj.loc_name) + \" | \" + pfri.replace_variable_aliases(var)\n",
    "    plt.plot(res.iloc[:,sm_num], label=label)\n",
    "\n",
    "# Plot mean and sum\n",
    "label_sms = \", \".join([pfri.replace_object_aliases(sm.loc_name) for sm in sm_objs])\n",
    "label = \"MEAN \" + label_sms + \" | \" + pfri.replace_variable_aliases(var)\n",
    "plt.plot(mean, label=label)\n",
    "label = \"SUM \" + label_sms + \" | \" + pfri.replace_variable_aliases(var)\n",
    "plt.plot(sum, label=label)\n",
    "plt.legend(loc='lower center', bbox_to_anchor=(0.5, 1.0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the same code block can be used with other result variables (e.g. reactive power) just by changing the `var` assignment in the first line.\n",
    "\n",
    "By default, `export_to_pandas` exports all monitored variables of the first `ElmRes` object found in the active study case. Alternatively, selections of network elements and the respective variables as well as results objects (`ElmRes`) can be specified as lists:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>G 01\\s:P1</th>\n",
       "      <th>G 01\\s:Q1</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>time</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>-0.10</th>\n",
       "      <td>1000.0</td>\n",
       "      <td>88.281415</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>-0.09</th>\n",
       "      <td>1000.0</td>\n",
       "      <td>88.281415</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       G 01\\s:P1  G 01\\s:Q1\n",
       "time                       \n",
       "-0.10     1000.0  88.281415\n",
       "-0.09     1000.0  88.281415"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "elements =   [g_01, g_01]\n",
    "variables = [RMS_bal.ElmSym.s_P1.value, RMS_bal.ElmSym.s_Q1.value]\n",
    "elmres_list = [pfri.get_results_obj_from_initial_conditions_calc()]*len(variables)\n",
    "\n",
    "df_sim_res = pfri.export_to_pandas(list_of_results_objs=elmres_list, \n",
    "                           elements=elements, \n",
    "                           variables=variables)\n",
    "df_sim_res.head(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Export to CSV Format\n",
    "We can also export simulation results from PowerFactory to a csv file. The syntax is very simlar to the export to pandas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'d:\\\\User\\\\seberlein\\\\FraunhIEE-UniKassel-PowSysStability\\\\powfacpy\\\\tutorials\\\\results.csv'"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pfri.export_to_csv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This exports all variables from the first result object (`ElmRes`) found in the active study case to the directory from where the script is run. You can also specifiy a directory, a file name (default is \"results\") and lists for grid objects, variables and results objects (similar to the method `export_to_pandas`). \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'d:\\\\User\\\\seberlein\\\\FraunhIEE-UniKassel-PowSysStability\\\\powfacpy\\\\tutorials\\\\Active power of Synchronous Machine 01.csv'"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "elements =   [g_01, g_01]\n",
    "variables = [RMS_bal.ElmSym.s_P1.value, RMS_bal.ElmSym.s_Q1.value]\n",
    "elmres_list = [pfri.get_results_obj_from_initial_conditions_calc()]*len(variables)\n",
    "\n",
    "pfri.export_to_csv(\n",
    "  file_name=\"Active power of Synchronous Machine 01\",\n",
    "  list_of_results_objs=elmres_list, \n",
    "  elements=elements, \n",
    "  variables=variables)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The formatting of the exported csv file is as follows: the first line (header) specifies the path of the variables (object path + variable name), e.g. `Network Model\\Network Data\\test_plot_interface\\Grid 1\\WECC WT Control System Type 4A\\REEC_A Electrical Control Model\\s:Iqcmd`, in all columns except for the first one, which is `Time`. The remaining rows contain the values of the variables.\n",
    "\n",
    "The static method `plot_from_csv` is used to plot from files of this format:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 30872 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot = powfacpy.PFPlotInterface.plot_from_csv(\n",
    "    \"Active power of Synchronous Machine 01.csv\", \n",
    "    \"G 01\\s:P1\")  \n",
    "plot = powfacpy.PFPlotInterface.plot_from_csv(\n",
    "    \"Active power of Synchronous Machine 01.csv\", \n",
    "    \"G 01\\s:Q1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Pyplot* is used by default, but you can specify a different plot interface using the optional argument `plot_interface`."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.6 64-bit",
   "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.10.11"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "20b4f2ae9a71ebfc0af8cf0167d05f9c8f5c94fe23811db744e66ae1fdb328c7"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
