docs: def _voltage_deviation(network, nodes, v_dev_allowed_upper,
                         v_dev_allowed_lower, voltage_level):
      """
      Checks for voltage stability issues in LV grids.
      Parameters
      ----------
      network : :class:`~.grid.network.Network`
      nodes : :obj:`list`
          List of nodes (of type :class:`~.grid.components.Generator`,
          :class:`~.grid.components.Load`, etc.) to check voltage deviation for.
      v_dev_allowed_upper : :pandas:`pandas.Series<series>`
          Series with time steps (of type :pandas:`pandas.Timestamp<timestamp>`)
          power flow analysis was conducted for and the allowed upper limit of
          voltage deviation for each time step as float.
      v_dev_allowed_lower : :pandas:`pandas.Series<series>`
          Series with time steps (of type :pandas:`pandas.Timestamp<timestamp>`)
          power flow analysis was conducted for and the allowed lower limit of
          voltage deviation for each time step as float.
      voltage_levels : :obj:`str`
          Specifies which voltage level to retrieve power flow analysis results
          for. Possible options are 'mv' and 'lv'.
  
      Returns
      -------
      :pandas:`pandas.DataFrame<dataframe>`
          Dataframe with critical nodes, sorted descending by voltage deviation.
          Index of the dataframe are all nodes (of type
          :class:`~.grid.components.Generator`, :class:`~.grid.components.Load`,
          etc.) with over-voltage issues. Columns are 'v_mag_pu' containing the
          maximum voltage deviation as float and 'time_index' containing the
          corresponding time step the over-voltage occured in as
          :pandas:`pandas.Timestamp<timestamp>`.
  
      """
  
      def _append_crit_node(series):
          return pd.DataFrame({'v_mag_pu': series.max(),
                               'time_index': series.idxmax()},
                              index=[node])
  
      crit_nodes_grid = pd.DataFrame()
  
      v_mag_pu_pfa = network.results.v_res(nodes=nodes, level=voltage_level)
  
      for node in nodes:
          # check for over- and under-voltage
          overvoltage = v_mag_pu_pfa[repr(node)][
              (v_mag_pu_pfa[repr(node)] > (v_dev_allowed_upper.loc[
                  v_mag_pu_pfa.index]))]
          undervoltage = v_mag_pu_pfa[repr(node)][
              (v_mag_pu_pfa[repr(node)] < (v_dev_allowed_lower.loc[
                  v_mag_pu_pfa.index]))]
  
          # write greatest voltage deviation to dataframe
          if not overvoltage.empty:
              overvoltage_diff = overvoltage - v_dev_allowed_upper.loc[
                  overvoltage.index]
              if not undervoltage.empty:
                  undervoltage_diff = v_dev_allowed_lower.loc[
                      undervoltage.index] - undervoltage
                  if overvoltage_diff.max() > undervoltage_diff.max():
                      crit_nodes_grid = crit_nodes_grid.append(
                          _append_crit_node(overvoltage_diff))
                  else:
                      crit_nodes_grid = crit_nodes_grid.append(
                          _append_crit_node(undervoltage_diff))
              else:
                  crit_nodes_grid = crit_nodes_grid.append(
                      _append_crit_node(overvoltage_diff))
          elif not undervoltage.empty:
              undervoltage_diff = v_dev_allowed_lower.loc[
                                      undervoltage.index] - undervoltage
              crit_nodes_grid = crit_nodes_grid.append(
                  _append_crit_node(undervoltage_diff))
  
      return crit_nodes_grid