docs: def tech_constraints_satisfied(self):
          """ Check route validity according to technical constraints (voltage and current rating)
          
          It considers constraints as
          
          * current rating of cable/line
          * voltage stability at all nodes
  
          Notes
          -----
              The validation is done for every tested MV grid configuration during CVRP algorithm. The current rating is
              checked using load factors from [#]_. Due to the high amount of steps the voltage rating cannot be checked
              using load flow calculation. Therefore we use a simple method which determines the voltage change between
              two consecutive nodes according to [#]_.
              Furthermore it is checked if new route has got more nodes than allowed (typ. 2*10 according to [#]_).
  
          References
          ----------
              
          .. [#] Deutsche Energie-Agentur GmbH (dena), "dena-Verteilnetzstudie. Ausbau- und Innovationsbedarf der
              Stromverteilnetze in Deutschland bis 2030.", 2012
          .. [#] M. Sakulin, W. Hipp, "Netzaspekte von dezentralen Erzeugungseinheiten,
              Studie im Auftrag der E-Control GmbH", TU Graz, 2004
          .. [#] Klaus Heuck et al., "Elektrische Energieversorgung", Vieweg+Teubner, Wiesbaden, 2007
          .. [#] FGH e.V.: "Technischer Bericht 302: Ein Werkzeug zur Optimierung der Störungsbeseitigung
              für Planung und Betrieb von Mittelspannungsnetzen", Tech. rep., 2008
          """
  
          # load parameters
          load_area_count_per_ring = float(cfg_ding0.get('mv_routing',
                                                         'load_area_count_per_ring'))
  
          max_half_ring_length = float(cfg_ding0.get('mv_routing',
                                                     'max_half_ring_length'))
  
          if self._problem._branch_kind == 'line':
              load_factor_normal = float(cfg_ding0.get('assumptions',
                                                       'load_factor_mv_line_lc_normal'))
              load_factor_malfunc = float(cfg_ding0.get('assumptions',
                                                        'load_factor_mv_line_lc_malfunc'))
          elif self._problem._branch_kind == 'cable':
              load_factor_normal = float(cfg_ding0.get('assumptions',
                                                       'load_factor_mv_cable_lc_normal'))
              load_factor_malfunc = float(cfg_ding0.get('assumptions',
                                                        'load_factor_mv_cable_lc_malfunc'))
          else:
              raise ValueError('Grid\'s _branch_kind is invalid, could not use branch parameters.')
  
          mv_max_v_level_lc_diff_normal = float(cfg_ding0.get('mv_routing_tech_constraints',
                                                              'mv_max_v_level_lc_diff_normal'))
          mv_max_v_level_lc_diff_malfunc = float(cfg_ding0.get('mv_routing_tech_constraints',
                                                               'mv_max_v_level_lc_diff_malfunc'))
          cos_phi_load = cfg_ding0.get('assumptions', 'cos_phi_load')
  
  
          # step 0: check if route has got more nodes than allowed
          if len(self._nodes) > load_area_count_per_ring:
              return False
  
          # step 1: calc circuit breaker position
          position = self.calc_circuit_breaker_position()
  
          # step 2: calc required values for checking current & voltage
          # get nodes of half-rings
          nodes_hring1 = [self._problem._depot] + self._nodes[0:position]
          nodes_hring2 = list(reversed(self._nodes[position:len(self._nodes)] + [self._problem._depot]))
          # get all nodes of full ring for both directions
          nodes_ring1 = [self._problem._depot] + self._nodes
          nodes_ring2 = list(reversed(self._nodes + [self._problem._depot]))
          # factor to calc reactive from active power
          Q_factor = tan(acos(cos_phi_load))
          # line/cable params per km
          r = self._problem._branch_type['R']  # unit for r: ohm/km
          x = self._problem._branch_type['L'] * 2*pi * 50 / 1e3  # unit for x: ohm/km
  
          # step 3: check if total lengths of half-rings exceed max. allowed distance
          if (self.length_from_nodelist(nodes_hring1) > max_half_ring_length or
              self.length_from_nodelist(nodes_hring2) > max_half_ring_length):
              return False
  
          # step 4a: check if current rating of default cable/line is violated
          # (for every of the 2 half-rings using load factor for normal operation)
          demand_hring_1 = sum([node.demand() for node in self._nodes[0:position]])
          demand_hring_2 = sum([node.demand() for node in self._nodes[position:len(self._nodes)]])
          peak_current_sum_hring1 = demand_hring_1 / (3**0.5) / self._problem._v_level  # units: kVA / kV = A
          peak_current_sum_hring2 = demand_hring_2 / (3**0.5) / self._problem._v_level  # units: kVA / kV = A
  
          if (peak_current_sum_hring1 > (self._problem._branch_type['I_max_th'] * load_factor_normal) or
              peak_current_sum_hring2 > (self._problem._branch_type['I_max_th'] * load_factor_normal)):
              return False
  
          # step 4b: check if current rating of default cable/line is violated
          # (for full ring using load factor for malfunction operation)
          peak_current_sum_ring = self._demand / (3**0.5) / self._problem._v_level  # units: kVA / kV = A
          if peak_current_sum_ring > (self._problem._branch_type['I_max_th'] * load_factor_malfunc):
              return False
  
          # step 5a: check voltage stability at all nodes
          # (for every of the 2 half-rings using max. voltage difference for normal operation)
  
          # get operation voltage level from station
          v_level_hring1 =\
              v_level_hring2 =\
              v_level_ring_dir1 =\
              v_level_ring_dir2 =\
              v_level_op =\
              self._problem._v_level * 1e3
  
          # set initial r and x
          r_hring1 =\
              r_hring2 =\
              x_hring1 =\
              x_hring2 =\
              r_ring_dir1 =\
              r_ring_dir2 =\
              x_ring_dir1 =\
              x_ring_dir2 = 0
  
          for n1, n2 in zip(nodes_hring1[0:len(nodes_hring1)-1], nodes_hring1[1:len(nodes_hring1)]):
              r_hring1 += self._problem.distance(n1, n2) * r
              x_hring1 += self._problem.distance(n1, n2) * x
              v_level_hring1 -= n2.demand() * 1e3 * (r_hring1 + x_hring1*Q_factor) / v_level_op
              if (v_level_op - v_level_hring1) > (v_level_op * mv_max_v_level_lc_diff_normal):
                  return False
  
          for n1, n2 in zip(nodes_hring2[0:len(nodes_hring2)-1], nodes_hring2[1:len(nodes_hring2)]):
              r_hring2 += self._problem.distance(n1, n2) * r
              x_hring2 += self._problem.distance(n1, n2) * x
              v_level_hring2 -= n2.demand() * 1e3 * (r_hring2 + x_hring2 * Q_factor) / v_level_op
              if (v_level_op - v_level_hring2) > (v_level_op * mv_max_v_level_lc_diff_normal):
                  return False
  
          # step 5b: check voltage stability at all nodes
          # (for full ring calculating both directions simultaneously using max. voltage diff. for malfunction operation)
          for (n1, n2), (n3, n4) in zip(zip(nodes_ring1[0:len(nodes_ring1)-1], nodes_ring1[1:len(nodes_ring1)]),
                                        zip(nodes_ring2[0:len(nodes_ring2)-1], nodes_ring2[1:len(nodes_ring2)])):
              r_ring_dir1 += self._problem.distance(n1, n2) * r
              r_ring_dir2 += self._problem.distance(n3, n4) * r
              x_ring_dir1 += self._problem.distance(n1, n2) * x
              x_ring_dir2 += self._problem.distance(n3, n4) * x
              v_level_ring_dir1 -= (n2.demand() * 1e3 * (r_ring_dir1 + x_ring_dir1 * Q_factor) / v_level_op)
              v_level_ring_dir2 -= (n4.demand() * 1e3 * (r_ring_dir2 + x_ring_dir2 * Q_factor) / v_level_op)
              if ((v_level_op - v_level_ring_dir1) > (v_level_op * mv_max_v_level_lc_diff_malfunc) or
                  (v_level_op - v_level_ring_dir2) > (v_level_op * mv_max_v_level_lc_diff_malfunc)):
                  return False
  
          return True