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