Exemplo n.º 1
0
def pipeflow(net, sol_vec=None, **kwargs):
    """
    The main method used to start the solver to calculate the veatity, pressure and temperature\
    distribution for a given net. Different options can be entered for \\**kwargs, which control\
    the solver behaviour (see function init constants for more information).

    :param net: The pandapipes net for which to perform the pipeflow
    :type net: pandapipesNet
    :param sol_vec:
    :type sol_vec:
    :param kwargs: A list of options controlling the solver behaviour
    :return: No output

    EXAMPLE:
        pipeflow(net, mode="hydraulic")

    """
    local_params = dict(locals())

    # Inputs & initialization of variables
    # ------------------------------------------------------------------------------------------

    # Init physical constants and options
    init_options(net, local_params)

    create_lookups(net, NodeComponent, BranchComponent, BranchWInternalsComponent)
    node_pit, branch_pit = initialize_pit(net, Junction.table_name(),
                                          NodeComponent, NodeElementComponent,
                                          BranchComponent, BranchWInternalsComponent)
    calculation_mode = get_net_option(net, "mode")

    if get_net_option(net, "check_connectivity"):
        nodes_connected, branches_connected = check_connectivity(
            net, branch_pit, node_pit, check_heat=calculation_mode in ["heat", "all"])
    else:
        nodes_connected = node_pit[:, ACTIVE_ND].astype(np.bool)
        branches_connected = branch_pit[:, ACTIVE_BR].astype(np.bool)

    reduce_pit(net, node_pit, branch_pit, nodes_connected, branches_connected)

    if calculation_mode == "hydraulics":
        niter = hydraulics(net)
    elif calculation_mode == "heat":
        if net.user_pf_options["hyd_flag"]:
            node_pit = net["_active_pit"]["node"]
            node_pit[:, PINIT] = sol_vec[:len(node_pit)]
            branch_pit = net["_active_pit"]["branch"]
            branch_pit[:, VINIT] = sol_vec[len(node_pit):]
            niter = heat_transfer(net)
        else:
            logger.warning("Converged flag not set. Make sure that hydraulic calculation results "
                           "are available.")
    elif calculation_mode == "all":
        niter = hydraulics(net)
        niter = heat_transfer(net)
    else:
        logger.warning("No proper calculation mode chosen.")

    extract_results_active_pit(net, node_pit, branch_pit, nodes_connected, branches_connected)
    extract_all_results(net, Junction.table_name())
Exemplo n.º 2
0
def set_damping_factor(net, niter, error):
    """
    Set the value of the damping factor (factor for the newton step width) from current results.

    :param net: the net for which to perform the pipeflow
    :type net: pandapipesNet
    :param niter:
    :type niter:
    :param error: an array containing the current residuals of all field variables solved for
    :return: No Output.

    EXAMPLE:
        set_damping_factor(net, niter, [error_p, error_v])
    """
    error_x0 = error[0]
    error_x1 = error[1]

    error_x0_increased = error_x0[niter] > error_x0[niter - 1]
    error_x1_increased = error_x1[niter] > error_x1[niter - 1]
    current_alpha = get_net_option(net, "alpha")

    if error_x0_increased and error_x1_increased:
        set_net_option(net, "alpha", current_alpha / 10 if current_alpha >= 0.1 else current_alpha)
    else:
        set_net_option(net, "alpha", current_alpha * 10 if current_alpha <= 0.1 else 1.0)

    return error_x0_increased, error_x1_increased
Exemplo n.º 3
0
    def calculate_pressure_lift(cls, net, pump_pit, node_pit):
        """

        :param net: The pandapipes network
        :type net: pandapipesNet
        :param pump_pit:
        :type pump_pit:
        :param node_pit:
        :type node_pit:
        :return: power stroke
        :rtype: float
        """
        area = pump_pit[:, AREA]
        idx = pump_pit[:, STD_TYPE].astype(int)
        std_types = np.array(list(net.std_type['pump'].keys()))[idx]
        p_scale = get_net_option(net, "p_scale")
        from_nodes = pump_pit[:, FROM_NODE].astype(np.int32)
        to_nodes = pump_pit[:, TO_NODE].astype(np.int32)
        fluid = get_fluid(net)
        p_from = node_pit[from_nodes,
                          PAMB] + node_pit[from_nodes, PINIT] * p_scale
        p_to = node_pit[to_nodes, PAMB] + node_pit[to_nodes, PINIT] * p_scale
        numerator = NORMAL_PRESSURE * pump_pit[:, TINIT]
        v_mps = pump_pit[:, VINIT]
        if fluid.is_gas:
            mask = p_from != p_to
            p_mean = np.empty_like(p_to)
            p_mean[~mask] = p_from[~mask]
            p_mean[mask] = 2 / 3 * (p_from[mask] ** 3 - p_to[mask] ** 3) \
                           / (p_from[mask] ** 2 - p_to[mask] ** 2)
            normfactor_mean = numerator * fluid.get_property("compressibility", p_mean) \
                              / (p_mean * NORMAL_TEMPERATURE)
            v_mean = v_mps * normfactor_mean
        else:
            v_mean = v_mps
        vol = v_mean * area
        fcts = itemgetter(*std_types)(net['std_type']['pump'])
        fcts = [fcts] if not isinstance(fcts, tuple) else fcts
        pl = np.array(list(map(lambda x, y: x.get_pressure(y), fcts, vol)))
        pump_pit[:, PL] = pl
Exemplo n.º 4
0
    def extract_results(cls, net, options, node_name):
        placement_table, branch_pit, res_table = cls.prepare_result_tables(
            net, options, node_name)

        node_pit = net["_active_pit"]["node"]
        node_active_idx_lookup = get_lookup(net, "node",
                                            "index_active")[node_name]
        junction_idx_lookup = get_lookup(net, "node", "index")[node_name]
        from_junction_nodes = node_active_idx_lookup[junction_idx_lookup[net[
            cls.table_name()]["from_junction"].values[placement_table]]]
        to_junction_nodes = node_active_idx_lookup[junction_idx_lookup[net[
            cls.table_name()]["to_junction"].values[placement_table]]]

        from_nodes = branch_pit[:, FROM_NODE].astype(np.int32)
        to_nodes = branch_pit[:, TO_NODE].astype(np.int32)
        p_scale = get_net_option(net, "p_scale")
        fluid = get_fluid(net)

        v_mps = branch_pit[:, VINIT]

        t0 = node_pit[from_nodes, TINIT_NODE]
        t1 = node_pit[to_nodes, TINIT_NODE]
        mf = branch_pit[:, LOAD_VEC_NODES]
        vf = branch_pit[:, LOAD_VEC_NODES] / get_fluid(net).get_density(
            (t0 + t1) / 2)

        idx_active = branch_pit[:, ELEMENT_IDX]
        _, v_sum, mf_sum, vf_sum, internal_pipes = \
            _sum_by_group(idx_active, v_mps, mf, vf, np.ones_like(idx_active))

        if fluid.is_gas:
            # derived from the ideal gas law
            p_from = node_pit[from_nodes,
                              PAMB] + node_pit[from_nodes, PINIT] * p_scale
            p_to = node_pit[to_nodes,
                            PAMB] + node_pit[to_nodes, PINIT] * p_scale
            numerator = NORMAL_PRESSURE * branch_pit[:, TINIT]
            normfactor_from = numerator * fluid.get_property("compressibility", p_from) \
                              / (p_from * NORMAL_TEMPERATURE)
            normfactor_to = numerator * fluid.get_property("compressibility", p_to) \
                            / (p_to * NORMAL_TEMPERATURE)
            v_gas_from = v_mps * normfactor_from
            v_gas_to = v_mps * normfactor_to
            mask = ~np.isclose(p_from, p_to)
            p_mean = np.empty_like(p_to)
            p_mean[~mask] = p_from[~mask]
            p_mean[mask] = 2 / 3 * (p_from[mask] ** 3 - p_to[mask] ** 3) \
                           / (p_from[mask] ** 2 - p_to[mask] ** 2)
            normfactor_mean = numerator * fluid.get_property("compressibility", p_mean) \
                              / (p_mean * NORMAL_TEMPERATURE)
            v_gas_mean = v_mps * normfactor_mean

            _, _, _, v_gas_mean_sum, nf_from_sum, nf_to_sum, \
            internal_pipes = _sum_by_group(idx_active, v_gas_from, v_gas_to, v_gas_mean,
                                           normfactor_from, normfactor_to,
                                           np.ones_like(idx_active))

            v_gas_from_ordered = select_from_pit(from_nodes,
                                                 from_junction_nodes,
                                                 v_gas_from)
            v_gas_to_ordered = select_from_pit(to_nodes, to_junction_nodes,
                                               v_gas_to)

            res_table["v_from_m_per_s"].values[
                placement_table] = v_gas_from_ordered
            res_table["v_to_m_per_s"].values[
                placement_table] = v_gas_to_ordered
            res_table["v_mean_m_per_s"].values[
                placement_table] = v_gas_mean_sum / internal_pipes
            res_table["normfactor_from"].values[
                placement_table] = nf_from_sum / internal_pipes
            res_table["normfactor_to"].values[
                placement_table] = nf_to_sum / internal_pipes
        else:
            res_table["v_mean_m_per_s"].values[
                placement_table] = v_sum / internal_pipes

        res_table["p_from_bar"].values[placement_table] = node_pit[
            from_junction_nodes, PINIT]
        res_table["p_to_bar"].values[placement_table] = node_pit[
            to_junction_nodes, PINIT]
        res_table["t_from_k"].values[placement_table] = node_pit[
            from_junction_nodes, TINIT_NODE]
        res_table["t_to_k"].values[placement_table] = node_pit[
            to_junction_nodes, TINIT_NODE]
        res_table["mdot_to_kg_per_s"].values[
            placement_table] = -mf_sum / internal_pipes
        res_table["mdot_from_kg_per_s"].values[
            placement_table] = mf_sum / internal_pipes
        res_table["vdot_norm_m3_per_s"].values[
            placement_table] = vf_sum / internal_pipes
        idx_pit = branch_pit[:, ELEMENT_IDX]
        _, lambda_sum, reynolds_sum, = \
            _sum_by_group(idx_pit, branch_pit[:, LAMBDA], branch_pit[:, RE])
        res_table["lambda"].values[
            placement_table] = lambda_sum / internal_pipes
        res_table["reynolds"].values[
            placement_table] = reynolds_sum / internal_pipes
Exemplo n.º 5
0
    def extract_results(cls, net, options, node_name):
        """
        Function that extracts certain results.

        :param net: The pandapipes network
        :type net: pandapipesNet
        :param options:
        :type options:
        :return: No Output.
        """
        placement_table, heat_exchanger_pit, res_table = \
            super().extract_results(net, options, node_name)

        node_pit = net["_active_pit"]["node"]
        node_active_idx_lookup = get_lookup(net, "node", "index_active")[node_name]
        junction_idx_lookup = get_lookup(net, "node", "index")[node_name]
        from_junction_nodes = node_active_idx_lookup[junction_idx_lookup[
            net[cls.table_name()]["from_junction"].values[placement_table]]]
        to_junction_nodes = node_active_idx_lookup[junction_idx_lookup[
            net[cls.table_name()]["to_junction"].values[placement_table]]]
        p_scale = get_net_option(net, "p_scale")
        from_nodes = heat_exchanger_pit[:, FROM_NODE].astype(np.int32)
        to_nodes = heat_exchanger_pit[:, TO_NODE].astype(np.int32)
        fluid = get_fluid(net)

        v_mps = heat_exchanger_pit[:, VINIT]

        t0 = node_pit[from_nodes, TINIT_NODE]
        t1 = node_pit[to_nodes, TINIT_NODE]
        mf = heat_exchanger_pit[:, LOAD_VEC_NODES]
        vf = heat_exchanger_pit[:, LOAD_VEC_NODES] / get_fluid(net).get_density((t0 + t1) / 2)

        idx_active = heat_exchanger_pit[:, ELEMENT_IDX]
        idx_sort, v_sum, mf_sum, vf_sum = \
            _sum_by_group(idx_active, v_mps, mf, vf)

        if fluid.is_gas:
            # derived from the ideal gas law
            p_from = node_pit[from_nodes, PAMB] + node_pit[from_nodes, PINIT] * p_scale
            p_to = node_pit[to_nodes, PAMB] + node_pit[to_nodes, PINIT] * p_scale
            numerator = NORMAL_PRESSURE * heat_exchanger_pit[:, TINIT]
            normfactor_from = numerator * fluid.get_property("compressibility", p_from) \
                              / (p_from * NORMAL_TEMPERATURE)
            normfactor_to = numerator * fluid.get_property("compressibility", p_to) \
                            / (p_to * NORMAL_TEMPERATURE)
            v_gas_from = v_mps * normfactor_from
            v_gas_to = v_mps * normfactor_to
            mask = p_from != p_to
            p_mean = np.empty_like(p_to)
            p_mean[~mask] = p_from[~mask]
            p_mean[mask] = 2 / 3 * (p_from[mask] ** 3 - p_to[mask] ** 3) \
                           / (p_from[mask] ** 2 - p_to[mask] ** 2)
            normfactor_mean = numerator * fluid.get_property("compressibility", p_mean) \
                              / (p_mean * NORMAL_TEMPERATURE)
            v_gas_mean = v_mps * normfactor_mean

            idx_sort, v_gas_from_sum, v_gas_to_sum, v_gas_mean_sum, nf_from_sum, nf_to_sum, \
            internal_pipes = _sum_by_group(
                idx_active, v_gas_from, v_gas_to, v_gas_mean, normfactor_from, normfactor_to,
                np.ones_like(idx_active))

            res_table["v_from_m_per_s"].values[placement_table] = v_gas_from_sum / internal_pipes
            res_table["v_to_m_per_s"].values[placement_table] = v_gas_to_sum / internal_pipes
            res_table["v_mean_m_per_s"].values[placement_table] = v_gas_mean_sum / internal_pipes
            res_table["normfactor_from"].values[placement_table] = nf_from_sum / internal_pipes
            res_table["normfactor_to"].values[placement_table] = nf_to_sum / internal_pipes
        else:
            res_table["v_mean_m_per_s"].values[placement_table] = v_sum

        res_table["p_from_bar"].values[placement_table] = node_pit[from_junction_nodes, PINIT]
        res_table["p_to_bar"].values[placement_table] = node_pit[to_junction_nodes, PINIT]
        res_table["t_from_k"].values[placement_table] = node_pit[from_junction_nodes, TINIT_NODE]
        res_table["t_to_k"].values[placement_table] = node_pit[to_junction_nodes, TINIT_NODE]
        res_table["mdot_to_kg_per_s"].values[placement_table] = -mf_sum
        res_table["mdot_from_kg_per_s"].values[placement_table] = mf_sum
        res_table["vdot_norm_m3_per_s"].values[placement_table] = vf_sum
        idx_pit = heat_exchanger_pit[:, ELEMENT_IDX]
        idx_sort, lambda_sum, reynolds_sum, = \
            _sum_by_group(idx_pit, heat_exchanger_pit[:, LAMBDA], heat_exchanger_pit[:, RE])
        res_table["lambda"].values[placement_table] = lambda_sum
        res_table["reynolds"].values[placement_table] = reynolds_sum
Exemplo n.º 6
0
def heat_transfer(net):
    max_iter, nonlinear_method, tol_p, tol_v, tol_t, tol_res = get_net_options(
        net, "iter", "nonlinear_method", "tol_p", "tol_v", "tol_T", "tol_res")

    # Start of nonlinear loop
    # ---------------------------------------------------------------------------------------------

    if net.fluid.is_gas:
        logger.info("Caution! Temperature calculation does currently not affect hydraulic "
                    "properties!")

    error_t, error_t_out, residual_norm = [], [], None

    set_net_option(net, "converged", False)
    niter = 0

    # This loop is left as soon as the solver converged
    while not get_net_option(net, "converged") and niter <= max_iter:
        logger.info("niter %d" % niter)

        # solve_hydraulics is where the calculation takes place
        t_out, t_out_old, t_init, t_init_old, epsilon = solve_temperature(net)

        # Error estimation & convergence plot
        delta_t_init = np.abs(t_init - t_init_old)
        delta_t_out = np.abs(t_out - t_out_old)

        residual_norm = (linalg.norm(epsilon) / (len(epsilon)))
        error_t.append(linalg.norm(delta_t_init) / (len(delta_t_init)))
        error_t_out.append(linalg.norm(delta_t_out) / (len(delta_t_out)))

        # Control of damping factor
        if nonlinear_method == "automatic":
            error_x0_increased, error_x1_increased = set_damping_factor(net, niter,
                                                                        [error_t, error_t_out])
            if error_x0_increased:
                net["_active_pit"]["node"][:, TINIT] = t_init_old
            if error_x1_increased:
                net["_active_pit"]["branch"][:, T_OUT] = t_out_old
        elif nonlinear_method != "constant":
            logger.warning("No proper nonlinear method chosen. Using constant settings.")

        # Setting convergence flag
        if error_t[niter] <= tol_t and error_t_out[niter] <= tol_t \
                and residual_norm < tol_res:
            if nonlinear_method != "automatic":
                set_net_option(net, "converged", True)
            elif get_net_option(net, "alpha") == 1:
                set_net_option(net, "converged", True)

        logger.debug("errorT: %s" % error_t[niter])
        logger.debug("alpha: %s" % get_net_option(net, "alpha"))
        niter += 1
        logger.debug("F: %s" % epsilon.round(4))
        logger.debug("T_init_: %s" % t_init.round(4))
        logger.debug("T_out_: %s" % t_out.round(4))

    write_internal_results(net, iterations_T=niter, error_T=error_t[niter - 1],
                           residual_norm_T=residual_norm)
    logger.info("---------------------------------------------------------------------------------")
    if get_net_option(net, "converged") is False:
        logger.warning("Maximum number of iterations reached but heat transfer solver did not "
                       "converge.")
        logger.info("Norm of residual: %s" % residual_norm)
    else:
        logger.info("Calculation completed. Preparing results...")
        logger.info("Converged after %d iterations." % niter)
        logger.info("Norm of residual: %s" % residual_norm)
        logger.info("tol_T: %s" % get_net_option(net, "tol_T"))

        net['converged'] = True

    return niter
Exemplo n.º 7
0
def hydraulics(net):
    max_iter, nonlinear_method, tol_p, tol_v, tol_t, tol_res = get_net_options(
        net, "iter", "nonlinear_method", "tol_p", "tol_v", "tol_T", "tol_res")

    # Start of nonlinear loop
    # ---------------------------------------------------------------------------------------------
    niter = 0
    create_internal_results(net)
    net["_internal_data"] = dict()

    # This branch is used to stop the solver after a specified error tolerance is reached
    error_v, error_p, residual_norm = [], [], None

    # This loop is left as soon as the solver converged
    while not get_net_option(net, "converged") and niter <= max_iter:
        logger.info("niter %d" % niter)

        # solve_hydraulics is where the calculation takes place
        v_init, p_init, v_init_old, p_init_old, epsilon = solve_hydraulics(net)

        # Error estimation & convergence plot
        dv_init = np.abs(v_init - v_init_old)
        dp_init = np.abs(p_init - p_init_old)

        residual_norm = (linalg.norm(epsilon) / (len(epsilon)))
        error_v.append(linalg.norm(dv_init) / (len(dv_init)))
        error_p.append(linalg.norm(dp_init / (len(dp_init))))

        # Control of damping factor
        if nonlinear_method == "automatic":
            error_x0_increased, error_x1_increased = set_damping_factor(net, niter,
                                                                        [error_p, error_v])
            if error_x0_increased:
                net["_active_pit"]["node"][:, PINIT] = p_init_old
            if error_x1_increased:
                net["_active_pit"]["branch"][:, VINIT] = v_init_old
        elif nonlinear_method != "constant":
            logger.warning("No proper nonlinear method chosen. Using constant settings.")

        # Setting convergence flag
        if error_v[niter] <= tol_v and error_p[niter] <= tol_p and residual_norm < tol_res:
            if nonlinear_method != "automatic":
                set_net_option(net, "converged", True)
            elif get_net_option(net, "alpha") == 1:
                set_net_option(net, "converged", True)

        logger.debug("errorv: %s" % error_v[niter])
        logger.debug("errorp: %s" % error_p[niter])
        logger.debug("alpha: %s" % get_net_option(net, "alpha"))
        niter += 1
    write_internal_results(net, iterations=niter, error_p=error_p[niter - 1],
                           error_v=error_v[niter - 1], residual_norm=residual_norm)

    logger.info("---------------------------------------------------------------------------------")
    if get_net_option(net, "converged") is False:
        logger.warning("Maximum number of iterations reached but hydraulics solver did not "
                       "converge.")
        logger.info("Norm of residual: %s" % residual_norm)
    else:
        logger.info("Calculation completed. Preparing results...")
        logger.info("Converged after %d iterations." % niter)
        logger.info("Norm of residual: %s" % residual_norm)
        logger.info("tol_p: %s" % get_net_option(net, "tol_p"))
        logger.info("tol_v: %s" % get_net_option(net, "tol_v"))
        net['converged'] = True

    net.pop("_internal_data", None)
    set_user_pf_options(net, hyd_flag=True)

    return niter
Exemplo n.º 8
0
    def get_internal_results(cls, net, pipe):
        """

        :param net: The pandapipes network
        :type net: pandapipesNet
        :param pipe:
        :type pipe:
        :return: pipe_results
        :rtype:
        """
        internal_sections = cls.get_internal_pipe_number(net)
        internal_p_nodes = internal_sections - 1
        p_node_idx = np.repeat(pipe, internal_p_nodes[pipe])
        v_pipe_idx = np.repeat(pipe, internal_sections[pipe])
        pipe_results = dict()
        pipe_results["PINIT"] = np.zeros((len(p_node_idx), 2),
                                         dtype=np.float64)
        pipe_results["TINIT"] = np.zeros((len(p_node_idx), 2),
                                         dtype=np.float64)
        pipe_results["VINIT"] = np.zeros((len(v_pipe_idx), 2),
                                         dtype=np.float64)

        if np.all(internal_sections[pipe] >= 2):
            fluid = get_fluid(net)
            f, t = get_lookup(net, "branch", "from_to")[cls.table_name()]
            pipe_pit = net["_pit"]["branch"][f:t, :]
            node_pit = net["_pit"]["node"]
            int_p_lookup = net["_lookups"]["internal_nodes_lookup"]["TPINIT"]
            int_v_lookup = net["_lookups"]["internal_nodes_lookup"]["VINIT"]

            selected_indices_p = []
            selected_indices_v = []
            for i in pipe:
                selected_indices_p.append(
                    np.where(int_p_lookup[:, 0] == i, True, False))
                selected_indices_v.append(
                    np.where(int_v_lookup[:, 0] == i, True, False))

            selected_indices_p_final = np.logical_or.reduce(
                selected_indices_p[:])
            selected_indices_v_final = np.logical_or.reduce(
                selected_indices_v[:])
            # a = np.where(int_p_lookup[:,0] ==  True, False)
            # b = np.where(int_v_lookup[:, 0] == v_pipe_idx, True, False)
            p_nodes = int_p_lookup[:, 1][selected_indices_p_final]
            v_nodes = int_v_lookup[:, 1][selected_indices_v_final]

            v_pipe_data = pipe_pit[v_nodes, VINIT]
            p_node_data = node_pit[p_nodes, PINIT]
            t_node_data = node_pit[p_nodes, TINIT_NODE]

            gas_mode = fluid.is_gas

            if gas_mode:
                p_scale = get_net_option(net, "p_scale")

                from_nodes = pipe_pit[v_nodes, FROM_NODE].astype(np.int32)
                to_nodes = pipe_pit[v_nodes, TO_NODE].astype(np.int32)
                p_from = node_pit[from_nodes,
                                  PAMB] + node_pit[from_nodes, PINIT] * p_scale
                p_to = node_pit[to_nodes,
                                PAMB] + node_pit[to_nodes, PINIT] * p_scale
                p_mean = np.where(
                    p_from == p_to, p_from,
                    2 / 3 * (p_from**3 - p_to**3) / (p_from**2 - p_to**2))
                numerator = NORMAL_PRESSURE * node_pit[v_nodes, TINIT_NODE]
                normfactor = numerator * fluid.get_property("compressibility", p_mean) \
                             / (p_mean * NORMAL_TEMPERATURE)

                v_pipe_data = v_pipe_data * normfactor

            pipe_results["PINIT"][:, 0] = p_node_idx
            pipe_results["PINIT"][:, 1] = p_node_data
            pipe_results["TINIT"][:, 0] = p_node_idx
            pipe_results["TINIT"][:, 1] = t_node_data
            pipe_results["VINIT"][:, 0] = v_pipe_idx
            pipe_results["VINIT"][:, 1] = v_pipe_data
        else:
            logger.warning(
                "For at least one pipe no internal data is available.")

        return pipe_results
Exemplo n.º 9
0
def build_system_matrix(net, branch_pit, node_pit, heat_mode):
    """

    :param net: The pandapipes network
    :type net: pandapipesNet
    :param branch_pit:
    :type branch_pit:
    :param node_pit:
    :type node_pit:
    :param heat_mode:
    :type heat_mode:
    :return: system_matrix, load_vector
    :rtype:
    """
    update_option = get_net_option(net, "only_update_hydraulic_matrix")
    update_only = update_option and "hydraulic_data_sorting" in net["_internal_data"] \
                  and "hydraulic_matrix" in net["_internal_data"]

    len_b = len(branch_pit)
    len_n = len(node_pit)
    branch_matrix_indices = np.arange(len_b) + len_n
    fn_col, tn_col, ntyp_col, slack_type, num_der = (FROM_NODE, TO_NODE, NODE_TYPE, P, 3) \
        if not heat_mode else (FROM_NODE_T, TO_NODE_T, NODE_TYPE_T, T, 2)

    fn = branch_pit[:, fn_col].astype(np.int32)
    tn = branch_pit[:, tn_col].astype(np.int32)
    not_slack_fn_branch_mask = node_pit[fn, ntyp_col] != slack_type
    not_slack_tn_branch_mask = node_pit[tn, ntyp_col] != slack_type
    slack_nodes = np.where(node_pit[:, ntyp_col] == slack_type)[0]

    if not heat_mode:
        len_fn_not_slack = np.sum(not_slack_fn_branch_mask)
        len_tn_not_slack = np.sum(not_slack_tn_branch_mask)
        len_fn1 = num_der * len_b + len_fn_not_slack
        len_tn1 = len_fn1 + len_tn_not_slack
        full_len = len_tn1 + slack_nodes.shape[0]
    else:
        inc_flow_sum = np.zeros(len(node_pit[:, LOAD]))
        tn_unique_der, tn_sums_der = _sum_by_group(tn, branch_pit[:, JAC_DERIV_DT_NODE])
        inc_flow_sum[tn_unique_der] += tn_sums_der
        len_fn1 = num_der * len_b + len(tn_unique_der)
        len_tn1 = len_fn1 + len_b
        full_len = len_tn1 + slack_nodes.shape[0]


    system_data = np.zeros(full_len, dtype=np.float64)

    if not heat_mode:
        # pdF_dv
        system_data[:len_b] = branch_pit[:, JAC_DERIV_DV]
        # pdF_dpi
        system_data[len_b:2 * len_b] = branch_pit[:, JAC_DERIV_DP]
        # pdF_dpi1
        system_data[2 * len_b:3 * len_b] = branch_pit[:, JAC_DERIV_DP1]
        # jdF_dv_from_nodes
        system_data[3 * len_b:len_fn1] = branch_pit[not_slack_fn_branch_mask, JAC_DERIV_DV_NODE]
        # jdF_dv_to_nodes
        system_data[len_fn1:len_tn1] = branch_pit[not_slack_tn_branch_mask,
                                                  JAC_DERIV_DV_NODE] * (-1)
        # p_nodes
        system_data[len_tn1:] = 1
    else:
        system_data[:len_b] = branch_pit[:, JAC_DERIV_DT]
        # pdF_dpi1
        system_data[len_b:2 * len_b] = branch_pit[:, JAC_DERIV_DT1]
        # jdF_dv_from_nodes
        system_data[2 * len_b:len_fn1] = inc_flow_sum[tn_unique_der]
        # jdF_dv_to_nodes
        data = branch_pit[:, JAC_DERIV_DT_NODE] * (-1)
        rows = tn
        index = np.argsort(rows)
        data = data[index]
        system_data[len_fn1:len_fn1 + len_b] = data
        system_data[len_fn1 + len_b:] = 1



    if not update_only:
        system_cols = np.zeros(full_len, dtype=np.int32)
        system_rows = np.zeros(full_len, dtype=np.int32)

        if not heat_mode:
            # pdF_dv
            system_cols[:len_b] = branch_matrix_indices
            system_rows[:len_b] = branch_matrix_indices

            # pdF_dpi
            system_cols[len_b:2 * len_b] = fn
            system_rows[len_b:2 * len_b] = branch_matrix_indices

            # pdF_dpi1
            system_cols[2 * len_b:3 * len_b] = tn
            system_rows[2 * len_b:3 * len_b] = branch_matrix_indices

            # jdF_dv_from_nodes
            system_cols[3 * len_b:len_fn1] = branch_matrix_indices[not_slack_fn_branch_mask]
            system_rows[3 * len_b:len_fn1] = fn[not_slack_fn_branch_mask]

            # jdF_dv_to_nodes
            system_cols[len_fn1:len_tn1] = branch_matrix_indices[not_slack_tn_branch_mask]
            system_rows[len_fn1:len_tn1] = tn[not_slack_tn_branch_mask]

            # p_nodes
            system_cols[len_tn1:] = slack_nodes
            system_rows[len_tn1:] = slack_nodes
        else:
            # pdF_dTfromnode
            system_cols[:len_b] = fn
            system_rows[:len_b] = branch_matrix_indices

            # pdF_dTout
            system_cols[len_b:2 * len_b] = branch_matrix_indices
            system_rows[len_b:2 * len_b] = branch_matrix_indices

            # t_nodes
            system_cols[len_fn1 + len_b:] = slack_nodes
            system_rows[len_fn1 + len_b:] = np.arange(0, len(slack_nodes))

            # jdF_dTnode_
            tn_unique_idx = np.unique(tn, return_index=True)
            system_cols[2 * len_b:len_fn1] = tn_unique_idx[0]
            system_rows[2 * len_b:len_fn1] = len(slack_nodes) + np.arange(0, len(tn_unique_der))

            # jdF_dTout
            branch_order = np.argsort(tn)
            tn_uni, tn_uni_counts = np.unique(tn[branch_order], return_counts=True)
            row_index = np.repeat(np.arange(len(tn_uni)), tn_uni_counts)
            system_cols[len_fn1:len_fn1 + len_b] = branch_matrix_indices[branch_order]
            system_rows[len_fn1:len_fn1 + len_b] = len(slack_nodes) + row_index


        if not update_option:
            system_matrix = csr_matrix((system_data, (system_rows, system_cols)),
                                           shape=(len_n + len_b, len_n + len_b))



        else:
            data_order = np.lexsort([system_cols, system_rows])
            system_data = system_data[data_order]
            system_cols = system_cols[data_order]
            system_rows = system_rows[data_order]

            row_counter = np.zeros(len_b + len_n + 1, dtype=np.int32)
            unique_rows, row_counts = _sum_by_group_sorted(system_rows, np.ones_like(system_rows))
            row_counter[unique_rows + 1] += row_counts
            ptr = row_counter.cumsum()
            system_matrix = csr_matrix((system_data, system_cols, ptr),
                                       shape=(len_n + len_b, len_n + len_b))
            net["_internal_data"]["hydraulic_data_sorting"] = data_order
            net["_internal_data"]["hydraulic_matrix"] = system_matrix
    else:
        data_order = net["_internal_data"]["hydraulic_data_sorting"]
        system_data = system_data[data_order]
        system_matrix = net["_internal_data"]["hydraulic_matrix"]
        system_matrix.data = system_data

    if not heat_mode:
        load_vector = np.empty(len_n + len_b)
        load_vector[len_n:] = branch_pit[:, LOAD_VEC_BRANCHES]
        load_vector[:len_n] = node_pit[:, LOAD] * (-1)
        fn_unique, fn_sums = _sum_by_group(fn, branch_pit[:, LOAD_VEC_NODES])
        tn_unique, tn_sums = _sum_by_group(tn, branch_pit[:, LOAD_VEC_NODES])
        load_vector[fn_unique] -= fn_sums
        load_vector[tn_unique] += tn_sums
        load_vector[slack_nodes] = 0
    else:
        tn_unique, tn_sums = _sum_by_group(tn, branch_pit[:, LOAD_VEC_NODES_T])
        load_vector = np.zeros(len_n + len_b)
        load_vector[len(slack_nodes) + np.arange(0, len(tn_unique_der))] += tn_sums
        load_vector[len(slack_nodes) + np.arange(0, len(tn_unique_der))] -= tn_sums_der * node_pit[
            tn_unique_der, TINIT]
        load_vector[0:len(slack_nodes)] = 0.

        load_vector[len_n:] = branch_pit[:, LOAD_VEC_BRANCHES_T]




    return system_matrix, load_vector