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
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