docs: def lv_voltage_deviation(network, mode=None, voltage_levels='mv_lv'):
      """
      Checks for voltage stability issues in LV grids.
  
      Parameters
      ----------
      network : :class:`~.grid.network.Network`
      mode : None or String
          If None voltage at all nodes in LV grid is checked. If mode is set to
          'stations' only voltage at busbar is checked.
      voltage_levels : :obj:`str`
          Specifies which allowed voltage deviations to use. Possible options
          are:
  
          * 'mv_lv'
            This is the default. The allowed voltage deviation for nodes in the
            MV grid is the same as for nodes in the LV grid. Further load and
            feed-in case are not distinguished.
          * 'lv'
            Use this to handle allowed voltage deviations in the MV and LV grid
            differently. Here, load and feed-in case are differentiated as well.
  
      Returns
      -------
      :obj:`dict`
          Dictionary with :class:`~.grid.grids.LVGrid` as key and a
          :pandas:`pandas.DataFrame<dataframe>` with its critical nodes, sorted
          descending by voltage deviation, as value.
          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>`.
  
      Notes
      -----
      Over-voltage is determined based on allowed voltage deviations defined in
      the config file 'config_grid_expansion' in section
      'grid_expansion_allowed_voltage_deviations'.
  
      """
  
      crit_nodes = {}
  
      v_dev_allowed_per_case = {}
      if voltage_levels == 'mv_lv':
          offset = network.config[
              'grid_expansion_allowed_voltage_deviations']['hv_mv_trafo_offset']
          control_deviation = network.config[
              'grid_expansion_allowed_voltage_deviations'][
              'hv_mv_trafo_control_deviation']
          v_dev_allowed_per_case['feedin_case_upper'] = \
              1 + offset + control_deviation + network.config[
                  'grid_expansion_allowed_voltage_deviations'][
                  'mv_lv_feedin_case_max_v_deviation']
          v_dev_allowed_per_case['load_case_lower'] = \
              1 + offset - control_deviation - network.config[
                  'grid_expansion_allowed_voltage_deviations'][
                  'mv_lv_load_case_max_v_deviation']
  
          v_dev_allowed_per_case['feedin_case_lower'] = 0.9
          v_dev_allowed_per_case['load_case_upper'] = 1.1
  
          v_dev_allowed_upper = \
                  network.timeseries.timesteps_load_feedin_case.case.apply(
                  lambda _: v_dev_allowed_per_case['{}_upper'.format(_)])
          v_dev_allowed_lower = \
              network.timeseries.timesteps_load_feedin_case.case.apply(
                  lambda _: v_dev_allowed_per_case['{}_lower'.format(_)])
      elif voltage_levels == 'lv':
          pass
      else:
          raise ValueError(
              'Specified mode {} is not a valid option.'.format(voltage_levels))
  
      for lv_grid in network.mv_grid.lv_grids:
  
          if mode:
              if mode == 'stations':
                  nodes = [lv_grid.station]
              else:
                  raise ValueError(
                      "{} is not a valid option for input variable 'mode' in "
                      "function lv_voltage_deviation. Try 'stations' or "
                      "None".format(mode))
          else:
              nodes = lv_grid.graph.nodes()
  
          if voltage_levels == 'lv':
              if mode == 'stations':
                  # get voltage at primary side to calculate upper bound for
                  # feed-in case and lower bound for load case
                  v_lv_station_primary = network.results.v_res(
                      nodes=[lv_grid.station], level='mv').iloc[:, 0]
                  timeindex = v_lv_station_primary.index
                  v_dev_allowed_per_case['feedin_case_upper'] = \
                      v_lv_station_primary + network.config[
                          'grid_expansion_allowed_voltage_deviations'][
                          'mv_lv_station_feedin_case_max_v_deviation']
                  v_dev_allowed_per_case['load_case_lower'] = \
                      v_lv_station_primary - network.config[
                          'grid_expansion_allowed_voltage_deviations'][
                          'mv_lv_station_load_case_max_v_deviation']
              else:
                  # get voltage at secondary side to calculate upper bound for
                  # feed-in case and lower bound for load case
                  v_lv_station_secondary = network.results.v_res(
                      nodes=[lv_grid.station], level='lv').iloc[:, 0]
                  timeindex = v_lv_station_secondary.index
                  v_dev_allowed_per_case['feedin_case_upper'] = \
                      v_lv_station_secondary + network.config[
                          'grid_expansion_allowed_voltage_deviations'][
                          'lv_feedin_case_max_v_deviation']
                  v_dev_allowed_per_case['load_case_lower'] = \
                      v_lv_station_secondary - network.config[
                          'grid_expansion_allowed_voltage_deviations'][
                          'lv_load_case_max_v_deviation']
              v_dev_allowed_per_case['feedin_case_lower'] = pd.Series(
                  0.9, index=timeindex)
              v_dev_allowed_per_case['load_case_upper'] = pd.Series(
                  1.1, index=timeindex)
              # maximum allowed voltage deviation in each time step
              v_dev_allowed_upper = []
              v_dev_allowed_lower = []
              for t in timeindex:
                  case = \
                      network.timeseries.timesteps_load_feedin_case.loc[
                          t, 'case']
                  v_dev_allowed_upper.append(
                      v_dev_allowed_per_case[
                          '{}_upper'.format(case)].loc[t])
                  v_dev_allowed_lower.append(
                      v_dev_allowed_per_case[
                          '{}_lower'.format(case)].loc[t])
              v_dev_allowed_upper = pd.Series(v_dev_allowed_upper,
                                              index=timeindex)
              v_dev_allowed_lower = pd.Series(v_dev_allowed_lower,
                                              index=timeindex)
  
          crit_nodes_grid = _voltage_deviation(
              network, nodes, v_dev_allowed_upper, v_dev_allowed_lower,
              voltage_level='lv')
  
          if not crit_nodes_grid.empty:
              crit_nodes[lv_grid] = crit_nodes_grid.sort_values(
                  by=['v_mag_pu'], ascending=False)
  
      if crit_nodes:
          if mode == 'stations':
              logger.debug(
                  '==> {} LV station(s) has/have voltage issues.'.format(
                      len(crit_nodes)))
          else:
              logger.debug(
                  '==> {} LV grid(s) has/have voltage issues.'.format(
                      len(crit_nodes)))
      else:
          if mode == 'stations':
              logger.debug('==> No voltage issues in LV stations.')
          else:
              logger.debug('==> No voltage issues in LV grids.')
  
      return crit_nodes