diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 03c46a70..66bf33bb 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,6 +1,11 @@ name: CI -on: [push] +on: + push: + branches: + - 'main' + - 'release-v**' + pull_request: jobs: build: diff --git a/.gitignore b/.gitignore index 342ed83d..5015bfa0 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,18 @@ org.eclipse.core.resources.prefs org.eclipse.jdt.core.prefs org.eclipse.m2e.core.prefs org.eclipse.jdt.groovy.core.prefs +/ampl_files/ampl_network_batteries.txt +/ampl_files/ampl_network_branches.txt +/ampl_files/ampl_network_buses.txt +/ampl_files/ampl_network_generators.txt +/ampl_files/ampl_network_hvdc.txt +/ampl_files/ampl_network_lcc_converter_stations.txt +/ampl_files/ampl_network_limits.txt +/ampl_files/ampl_network_loads.txt +/ampl_files/ampl_network_ptc.txt +/ampl_files/ampl_network_rtc.txt +/ampl_files/ampl_network_shunts.txt +/ampl_files/ampl_network_static_var_compensators.txt +/ampl_files/ampl_network_substations.txt +/ampl_files/ampl_network_tct.txt +/ampl_files/ampl_network_vsc_converter_stations.txt diff --git a/README.md b/README.md index 0ad5590b..bf392735 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,4 @@ [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=com.powsybl%3Apowsybl-incubator&metric=alert_status)](https://sonarcloud.io/dashboard?id=com.powsybl%3Apowsybl-incubator) [![MPL-2.0 License](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](https://www.mozilla.org/en-US/MPL/2.0/) [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/powsybl) +[![Slack](https://img.shields.io/badge/slack-powsybl-blueviolet.svg?logo=slack)](https://join.slack.com/t/powsybl/shared_invite/zt-rzvbuzjk-nxi0boim1RKPS5PjieI0rA) diff --git a/ampl_files/ampl_computed_data.mod b/ampl_files/ampl_computed_data.mod new file mode 100644 index 00000000..15f66f14 --- /dev/null +++ b/ampl_files/ampl_computed_data.mod @@ -0,0 +1,57 @@ +#--- COMPUTED DATA --- +# We are assuming that the "variant" input data always equals 1 +# VARIANTS check +set VARIANTS = union{(i,j) in VARIANTS_BUSES} {i}; +if card(VARIANTS) > 1 then { + printf "Two different variants in data files !\n"; + quit; +} else + printf "Correct using of variant input\n"; + +# PV/PQ BUSES definitions and checks +param set_target_V default 0; +param generator_controling default 0; +set BUSES = union{(i,j) in VARIANTS_BUSES} {j}; +set GENS = union{(i,j) in VARIANTS_GENS} {j}; +set PV_BUSES = {i in BUSES: card({j in GENS: gen_bus[1, j] == i and gen_vregul[1, j] == "true"}) >= 1}; +set PQ_BUSES = {i in BUSES: card({j in GENS: gen_bus[1, j] == i and gen_vregul[1, j] == "true"}) == 0}; +param targetV{PV_BUSES}; +for{i in PV_BUSES} { + let set_target_V := 0; + for{j in GENS: gen_bus[1,j]==i} { + if gen_conbus[1,j] != i then { + # FIX ME + printf "Control of generator %d is not local.\nHowever it will be turned to local in this simulation.\n", j; + } + if set_target_V == 1 then { + if targetV[i] != gen_targetv[1,j] then printf"Inconsistent target V between generators %d and %d at bus %d.\nGenerator %d is chosen as reference.\n", j, generator_controling, i, generator_controling; + } else { + let targetV[i] := gen_targetv[1,j]; + let set_target_V := 1; + let generator_controling := j; + } + } +} + +#BRANCHES and LOADS definitions +set BRANCHES = union{(i,j) in VARIANTS_BRANCHES} {j}; +set LOADS = union{(i,j) in VARIANTS_LOADS} {j}; + + +# Compute slack node +param slack_bus default 0; +param voltage_level_slack default 0; +param neighbours_slack default 0; +for{i in PV_BUSES} { + if ss_nomv[1,bus_substations[1,i]] >= voltage_level_slack then { + if card({j in BUSES: exists {k in BRANCHES} ((br_bus1[1,k]==i and br_bus2[1,k]==j) or (br_bus1[1,k]==j and br_bus2[1,k]==i))}) > neighbours_slack then { + let slack_bus := i; + let voltage_level_slack := ss_nomv[1,bus_substations[1,i]]; + let neighbours_slack := card({j in BUSES: exists {k in BRANCHES} ((br_bus1[1,k]==i and br_bus2[1,k]==j) or (br_bus1[1,k]==j and br_bus2[1,k]==i))}); + } + } +} + +# Branches data computations +param br_y{i in BRANCHES} := 1/sqrt(br_r[1,i]*br_r[1,i] + br_x[1,i]*br_x[1,i]); +param br_ksi{i in BRANCHES} := atan2(br_r[1,i], br_x[1,i]); \ No newline at end of file diff --git a/ampl_files/ampl_data.dat b/ampl_files/ampl_data.dat new file mode 100644 index 00000000..f5c099c9 --- /dev/null +++ b/ampl_files/ampl_data.dat @@ -0,0 +1,11 @@ +data; + +param: VARIANTS_BUSES: bus_substations bus_cc bus_v bus_theta bus_p bus_q bus_fault bus_curative bus_id := include ampl_network_buses.txt; + +param: VARIANTS_GENS: gen_bus gen_conbus gen_substation gen_minp gen_maxp gen_minqmaxp gen_minq0 gen_minqminp gen_maxqmaxp gen_maxq0 gen_maxqminp gen_vregul gen_targetv gen_targetp gen_targetq gen_fault gen_curative gen_id gen_desc gen_p gen_q := include ampl_network_generators.txt; + +param: VARIANTS_BRANCHES: br_bus1 br_bus2 br_3wtnum br_sub1 br_sub2 br_r br_x br_g1 br_g2 br_b1 br_b2 br_cstr br_ratiotc br_phasetc br_p1 br_p2 br_q1 br_q2 br_patl1 br_patl2 br_merged br_fault br_curative br_id br_desc := include ampl_network_branches.txt; + +param: VARIANTS_SS: ss_unused1 ss_unused2 ss_nomv ss_minv ss_maxv ss_fault ss_curative ss_country ss_id ss_desc := include ampl_network_substations.txt; + +param: VARIANTS_LOADS: ld_bus ld_ss ld_p0 ld_q0 ld_fault ld_curative ld_id ld_desc ld_p ld_q := include ampl_network_loads.txt; \ No newline at end of file diff --git a/ampl_files/ampl_dataread_model.mod b/ampl_files/ampl_dataread_model.mod new file mode 100644 index 00000000..b720a3f2 --- /dev/null +++ b/ampl_files/ampl_dataread_model.mod @@ -0,0 +1,91 @@ +#--- READ DATA --- +set VARIANTS_BUSES dimen 2; + +param bus_substations{VARIANTS_BUSES}; +param bus_cc{VARIANTS_BUSES}; +param bus_v{VARIANTS_BUSES}; +param bus_theta{VARIANTS_BUSES}; +param bus_p{VARIANTS_BUSES}; +param bus_q{VARIANTS_BUSES}; +param bus_fault{VARIANTS_BUSES} binary; +param bus_curative{VARIANTS_BUSES} binary; +param bus_id{VARIANTS_BUSES} symbolic; + +set VARIANTS_GENS dimen 2; +param gen_bus{VARIANTS_GENS}; +param gen_conbus{VARIANTS_GENS}; +param gen_substation{VARIANTS_GENS}; +param gen_minp{VARIANTS_GENS}; +param gen_maxp{VARIANTS_GENS}; +param gen_minqmaxp{VARIANTS_GENS}; +param gen_minq0{VARIANTS_GENS}; +param gen_minqminp{VARIANTS_GENS}; +param gen_maxqmaxp{VARIANTS_GENS}; +param gen_maxq0{VARIANTS_GENS}; +param gen_maxqminp{VARIANTS_GENS}; +param gen_vregul{VARIANTS_GENS} symbolic; +param gen_targetv{VARIANTS_GENS}; +param gen_targetp{VARIANTS_GENS}; +param gen_targetq{VARIANTS_GENS}; +param gen_fault{VARIANTS_GENS} binary; +param gen_curative{VARIANTS_GENS} binary; +param gen_id{VARIANTS_GENS} symbolic; +param gen_desc{VARIANTS_GENS} symbolic; +param gen_p{VARIANTS_GENS}; +param gen_q{VARIANTS_GENS}; + +set VARIANTS_BRANCHES dimen 2; +param br_bus1{VARIANTS_BRANCHES}; +param br_bus2{VARIANTS_BRANCHES}; +param br_3wtnum{VARIANTS_BRANCHES}; +param br_sub1{VARIANTS_BRANCHES}; +param br_sub2{VARIANTS_BRANCHES}; +param br_r{VARIANTS_BRANCHES}; +param br_x{VARIANTS_BRANCHES}; +param br_g1{VARIANTS_BRANCHES}; +param br_g2{VARIANTS_BRANCHES}; +param br_b1{VARIANTS_BRANCHES}; +param br_b2{VARIANTS_BRANCHES}; +param br_cstr{VARIANTS_BRANCHES}; +param br_ratiotc{VARIANTS_BRANCHES}; +param br_phasetc{VARIANTS_BRANCHES}; +param br_p1{VARIANTS_BRANCHES}; +param br_p2{VARIANTS_BRANCHES}; +param br_q1{VARIANTS_BRANCHES}; +param br_q2{VARIANTS_BRANCHES}; +param br_patl1{VARIANTS_BRANCHES}; +param br_patl2{VARIANTS_BRANCHES}; +param br_merged{VARIANTS_BRANCHES} symbolic; +param br_fault{VARIANTS_BRANCHES} binary; +param br_curative{VARIANTS_BRANCHES} binary; +param br_id{VARIANTS_BRANCHES} symbolic; +param br_desc{VARIANTS_BRANCHES} symbolic; + +set VARIANTS_SS dimen 2; +param ss_unused1{VARIANTS_SS} symbolic; +param ss_unused2{VARIANTS_SS}; +param ss_nomv{VARIANTS_SS}; +param ss_minv{VARIANTS_SS}; +param ss_maxv{VARIANTS_SS}; +param ss_fault{VARIANTS_SS} binary; +param ss_curative{VARIANTS_SS} binary; +param ss_country{VARIANTS_SS} symbolic; +param ss_id{VARIANTS_SS} symbolic; +param ss_desc{VARIANTS_SS} symbolic; + +set VARIANTS_LOADS dimen 2; +param ld_bus{VARIANTS_LOADS}; +param ld_ss{VARIANTS_LOADS}; +param ld_p0{VARIANTS_LOADS}; +param ld_q0{VARIANTS_LOADS}; +param ld_fault{VARIANTS_LOADS} binary; +param ld_curative{VARIANTS_LOADS} binary; +param ld_id{VARIANTS_LOADS} symbolic; +param ld_desc{VARIANTS_LOADS} symbolic; +param ld_p{VARIANTS_LOADS}; +param ld_q{VARIANTS_LOADS}; + +param v_min{VARIANTS_BUSES} default 0.8; +param v_max{VARIANTS_BUSES} default 1.2; + +param p_pu default 100; \ No newline at end of file diff --git a/ampl_files/ampl_model.mod b/ampl_files/ampl_model.mod new file mode 100644 index 00000000..a582d17d --- /dev/null +++ b/ampl_files/ampl_model.mod @@ -0,0 +1,39 @@ +# Variables +var V{i in BUSES} default if bus_v[1,i] > 0 then bus_v[1,i] else 1; +var Phi{BUSES} default 0; +var Q{PV_BUSES} default 0; + +# Objectif function +minimize Quadratic_Error: + sum {i in PQ_BUSES: bus_v[1,i] > 0} ( (V[i] - bus_v[1,i]) * (V[i] - bus_v[1,i]) ); + +# Constraints +subject to Active_power_balance{i in BUSES: i != slack_bus}: + sum {j in BRANCHES: br_bus1[1,j] == i and br_bus2[1,j] != -1} ( br_cstr[1,j] * V[i] * ( br_g1[1,j] * br_cstr[1,j] * V[i] + br_y[j] * br_cstr[1,j] * V[i] * sin(br_ksi[j]) - br_y[j] * 1 * V[br_bus2[1,j]] * sin(br_ksi[j] - 0 + 0 - Phi[i] + Phi[br_bus2[1,j]]) ) ) + + sum {j in BRANCHES: br_bus2[1,j] == i and br_bus1[1,j] != -1} ( 1 * V[i] * ( br_g2[1,j] * 1 * V[i] - br_y[j] * br_cstr[1,j] * V[br_bus1[1,j]] * sin(br_ksi[j] + 0 - 0 + Phi[br_bus1[1,j]] - Phi[i]) + br_y[j] * 1 * V[i] * sin(br_ksi[j]) ) ) + == (sum {j in GENS: gen_bus[1,j] == i} gen_targetp[1,j] - sum {j in LOADS: ld_bus[1,j] == i} ld_p0[1,j])/p_pu; + +subject to Null_phase_slack{i in BUSES: i == slack_bus}: + Phi[i] == 0; + +subject to Rective_power_balance_PQ{i in PQ_BUSES}: + sum {j in BRANCHES: br_bus1[1,j] == i and br_bus2[1,j] != -1} ( br_cstr[1,j] * V[i] * (- br_b1[1,j] * br_cstr[1,j] * V[i] + br_y[j] * br_cstr[1,j] * V[i] * cos(br_ksi[j]) - br_y[j] * 1 * V[br_bus2[1,j]] * cos(br_ksi[j] - 0 + 0 - Phi[i] + Phi[br_bus2[1,j]]) ) ) + + sum {j in BRANCHES: br_bus2[1,j] == i and br_bus1[1,j] != -1} ( 1 * V[i] * ( - br_b2[1,j] * 1 * V[i] - br_y[j] * br_cstr[1,j] * V[br_bus1[1,j]] * cos(br_ksi[j] + 0 - 0 + Phi[br_bus1[1,j]] - Phi[i]) + br_y[j] * 1 * V[i] * cos(br_ksi[j]) ) ) + == (sum {j in GENS: gen_bus[1,j] == i} gen_targetq[1,j] - sum {j in LOADS: ld_bus[1,j] == i} ld_q0[1,j])/p_pu; + +subject to Rective_power_balance_PV{i in PV_BUSES}: + sum {j in BRANCHES: br_bus1[1,j] == i and br_bus2[1,j] != -1} ( br_cstr[1,j] * V[i] * (- br_b1[1,j] * br_cstr[1,j] * V[i] + br_y[j] * br_cstr[1,j] * V[i] * cos(br_ksi[j]) - br_y[j] * 1 * V[br_bus2[1,j]] * cos(br_ksi[j] - 0 + 0 - Phi[i] + Phi[br_bus2[1,j]]) ) ) + + sum {j in BRANCHES: br_bus2[1,j] == i and br_bus1[1,j] != -1} ( 1 * V[i] * ( - br_b2[1,j] * 1 * V[i] - br_y[j] * br_cstr[1,j] * V[br_bus1[1,j]] * cos(br_ksi[j] + 0 - 0 + Phi[br_bus1[1,j]] - Phi[i]) + br_y[j] * 1 * V[i] * cos(br_ksi[j]) ) ) + == Q[i]/p_pu; + +subject to Reactive_power_inf{i in PV_BUSES}: + sum {j in GENS: gen_bus[1,j] == i} gen_minq0[1,j] - sum {j in LOADS: ld_bus[1,j] == i} ld_q0[1,j] <= Q[i]; + +subject to Reactive_power_sup{i in PV_BUSES}: + Q[i] <= sum {j in GENS: gen_bus[1,j] == i} gen_maxq0[1,j] - sum {j in LOADS: ld_bus[1,j] == i} ld_q0[1,j]; + +subject to Voltage_limit_inf{i in BUSES}: + sum{(j,k) in VARIANTS_SS: k == bus_substations[1,i] and j == 1} ss_minv[j,k] <= V[i]; + +subject to Voltage_limit_sup{i in BUSES}: + V[i] <= sum{(j,k) in VARIANTS_SS: k == bus_substations[1,i] and j == 1} ss_maxv[j,k]; \ No newline at end of file diff --git a/ampl_files/ampl_model.run b/ampl_files/ampl_model.run new file mode 100644 index 00000000..26a0dd30 --- /dev/null +++ b/ampl_files/ampl_model.run @@ -0,0 +1,45 @@ +model ampl_dataread_model.mod; +data ampl_data.dat; +model ampl_computed_data.mod; +model ampl_model.mod; +option solver knitroampl; +option knitro_options "feastol=1e-3"; +/* +display BUSES; +display PV_BUSES; +display PQ_BUSES; +display br_x; +display ss_country; +display BRANCHES; +display slack_bus; +display ld_p0; +display br_y; +display br_ksi; +*/ +problem Initialize_voltage_plan: V, Phi, Q, Quadratic_Error, Active_power_balance, Null_phase_slack, Rective_power_balance_PQ, Rective_power_balance_PV, Reactive_power_inf, Reactive_power_sup, Voltage_limit_inf, Voltage_limit_sup; + +solve Initialize_voltage_plan; +/* +display V; +display Phi; +display Active_power_balance; + +for{i in BUSES: i != slack_bus} { + display i; + display sum {j in BRANCHES: br_bus1[1,j] == i} ( br_cstr[1,j] * V[i] * ( br_g1[1,j] * br_cstr[1,j] * V[i] + br_y[j] * br_cstr[1,j] * V[i] * sin(br_ksi[j]) - br_y[j] * 1 * V[br_bus2[1,j]] * sin(br_ksi[j] - 0 + 0 - Phi[i] + Phi[br_bus2[1,j]]) ) ) + + sum {j in BRANCHES: br_bus2[1,j] == i} ( 1 * V[i] * ( br_g2[1,j] * 1 * V[i] - br_y[j] * br_cstr[1,j] * V[br_bus1[1,j]] * sin(br_ksi[j] + 0 - 0 + Phi[br_bus1[1,j]] - Phi[i]) + br_y[j] * 1 * V[i] * sin(br_ksi[j]) ) ); + display sum {j in GENS: gen_bus[1,j] == i} gen_targetp[1,j] - sum {j in LOADS: ld_bus[1,j] == i} ld_p0[1,j]; + display sum {j in BRANCHES: br_bus1[1,j] == i} ( br_cstr[1,j] * V[i] * (- br_b1[1,j] * br_cstr[1,j] * V[i] + br_y[j] * br_cstr[1,j] * V[i] * cos(br_ksi[j]) - br_y[j] * 1 * V[br_bus2[1,j]] * cos(br_ksi[j] - 0 + 0 - Phi[i] + Phi[br_bus2[1,j]]) ) ) + + sum {j in BRANCHES: br_bus2[1,j] == i} ( 1 * V[i] * ( - br_b2[1,j] * 1 * V[i] - br_y[j] * br_cstr[1,j] * V[br_bus1[1,j]] * cos(br_ksi[j] + 0 - 0 + Phi[br_bus1[1,j]] - Phi[i]) + br_y[j] * 1 * V[i] * cos(br_ksi[j]) ) ); + display sum {j in GENS: gen_bus[1,j] == i} gen_targetq[1,j] - sum {j in LOADS: ld_bus[1,j] == i} ld_q0[1,j]; +} + +for{i in BUSES} { + if sum{(j,k) in VARIANTS_SS: k == bus_substations[1,i] and j == 1} ss_minv[j,k] > V[i] or sum{(j,k) in VARIANTS_SS: k == bus_substations[1,i] and j == 1} ss_maxv[j,k] < V[i] then printf "Bus %d : %f < %f < %f\n", i, sum{(j,k) in VARIANTS_SS: k == bus_substations[1,i] and j == 1} ss_minv[j,k], V[i], sum{(j,k) in VARIANTS_SS: k == bus_substations[1,i] and j == 1} ss_maxv[j,k]; +} + +printf "slack node (i=1) \n"; +display sum {j in BRANCHES: br_bus1[1,j] == 1} ( br_cstr[1,j] * V[1] * ( br_g1[1,j] * br_cstr[1,j] * V[1] + br_y[j] * br_cstr[1,j] * V[1] * sin(br_ksi[j]) - br_y[j] * 1 * V[br_bus2[1,j]] * sin(br_ksi[j] - 0 + 0 - Phi[1] + Phi[br_bus2[1,j]]) ) ) + + sum {j in BRANCHES: br_bus2[1,j] == 1} ( 1 * V[1] * ( br_g2[1,j] * 1 * V[1] - br_y[j] * br_cstr[1,j] * V[br_bus1[1,j]] * sin(br_ksi[j] + 0 - 0 + Phi[br_bus1[1,j]] - Phi[1]) + br_y[j] * 1 * V[1] * sin(br_ksi[j]) ) ); +display Q[1]; +*/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8e30ae86..6ebd1d02 100644 --- a/pom.xml +++ b/pom.xml @@ -56,19 +56,20 @@ + simulator 11 - 4.9.2 + 1.2.2 com.powsybl - powsybl-core - ${powsybl-core.version} + powsybl-dependencies + ${powsybl-dependencies.version} pom import diff --git a/pyomo/init_voltage_plan/script.py b/pyomo/init_voltage_plan/script.py new file mode 100644 index 00000000..08cf8f6d --- /dev/null +++ b/pyomo/init_voltage_plan/script.py @@ -0,0 +1,143 @@ +import time +import pypowsybl as pp +import pyomo.environ as pyo +from pyomo.opt import SolverFactory +from utils import * + +debug = True +if debug: + tic = time.perf_counter() + print("Beginning...") + +# Import network +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Import network...") +p_pu = 100 # pu is 100MW by default +n = pp.network.load(network_path) +#n = pp.per_unit.per_unit_view(n, p_pu) T be used when per unit will be released + +# Create pyomo model +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Create pyomo model...") +model = pyo.ConcreteModel() + +# Define bus types +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Define bus types...") +buses = n.get_buses().index.tolist() +PV_buses = [] +PQ_buses = [] +for id_gen, row in n.get_generators().iterrows(): + if row["voltage_regulator_on"]: + if row["bus_id"] not in PV_buses: # TO DO: Work only when regulations are local + PV_buses.append(row["bus_id"]) + +for bus in buses: + if bus not in PV_buses: + PQ_buses.append(bus) + +# Define slack node +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Define slack node...") +slack_bus = "" +voltage_level_slack = 0 +neighbours_slack = 0 +PV_neighbours = nb_neighboursPV(n, PV_buses) +for id_b, row in n.get_buses().iterrows(): + if id_b not in PV_buses: + continue + if n.get_voltage_levels().at[row["voltage_level_id"], "nominal_v"] >= voltage_level_slack: + if PV_neighbours[id_b] > neighbours_slack: + slack_bus = id_b + voltage_level_slack = n.get_voltage_levels().at[row["voltage_level_id"], "nominal_v"] + neighbours_slack = PV_neighbours[id_b] + +# Create variables +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Create variables...") +model.Buses = pyo.Set(initialize=buses) +model.PVbuses = pyo.Set(initialize=PV_buses) +model.V = pyo.Var(model.Buses, domain=pyo.NonNegativeReals, bounds=voltage_bounds(n), initialize=voltage_init(n)) +model.Phi = pyo.Var(model.Buses, domain=pyo.Reals, initialize=0) +model.Q = pyo.Var(model.PVbuses, domain=pyo.Reals, bounds=reactive_power_bounds(n), initialize=reactive_init(n)) + +# Objective function +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Objective function...") +model.OBJ = pyo.Objective(rule=obj_expression(n, PQ_buses)) + +# Constraints +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("Constraints...") +## Active power balance +if debug: + toc = time.perf_counter() + print(f"\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("\tActive power balance...") +buses_wo_slack = [] +for bus in buses: + if bus == slack_bus: + continue + buses_wo_slack.append(bus) + +model.Buses_wo_slack = pyo.Set(initialize=buses_wo_slack) +model.ActivePowerBalance = pyo.Constraint(model.Buses_wo_slack, rule=active_power_balance_expr(n, buses, p_pu)) + +## Null phase for slack +if debug: + toc = time.perf_counter() + print(f"\t\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("\tNull phase...") +model.NullPhaseSlack = pyo.Constraint(expr = model.Phi[slack_bus] == 0) + +## Reactive power balance for PQ buses +if debug: + toc = time.perf_counter() + print(f"\t\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("\tReactive power balance PQ...") +model.PQbuses = pyo.Set(initialize=PQ_buses) +model.ReactivePowerBalancePQ = pyo.Constraint(model.PQbuses,rule=reactive_power_balancePQ_expr(n, buses, p_pu)) + +## Reactive power balance for PV buses +if debug: + toc = time.perf_counter() + print(f"\t\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("\tReactive power balance PV...") +model.ReactivePowerBalancePV = pyo.Constraint(model.PVbuses,rule=reactive_power_balancePV_expr(n, buses, p_pu)) + +# End +if debug: + toc = time.perf_counter() + print(f"\t\tin {toc-tic:0.4f} seconds") + tic = time.perf_counter() + print("End") + +# Solve +executable = 'knitroampl.exe' +opt = SolverFactory("knitro", executable=executable, solver_io='nl') +results = opt.solve(model) \ No newline at end of file diff --git a/pyomo/init_voltage_plan/utils.py b/pyomo/init_voltage_plan/utils.py new file mode 100644 index 00000000..8d163d20 --- /dev/null +++ b/pyomo/init_voltage_plan/utils.py @@ -0,0 +1,317 @@ +import numpy as np +import pyomo.environ as pyo + +# Count neighbours function +def nb_neighboursPV(net, PV_buses): + neighbours_count = dict() + buses = net.get_buses().index.tolist() + for id_line, row in net.get_lines().iterrows(): + b1 = row["bus1_id"] + b2 = row["bus2_id"] + if b1 in PV_buses and b2 in buses: + if b1 not in neighbours_count: + neighbours_count[b1] = 1 + else: + neighbours_count[b1] += 1 + if b2 in PV_buses and b1 in buses: + if b2 not in neighbours_count: + neighbours_count[b2] = 1 + else: + neighbours_count[b2] += 1 + for id_tr, row in net.get_2_windings_transformers().iterrows(): + b1 = row["bus1_id"] + b2 = row["bus2_id"] + if b1 in PV_buses and b2 in buses: + if b1 not in neighbours_count: + neighbours_count[b1] = 1 + else: + neighbours_count[b1] += 1 + if b2 in PV_buses and b1 in buses: + if b2 not in neighbours_count: + neighbours_count[b2] = 1 + else: + neighbours_count[b2] += 1 + return neighbours_count + +# Objectif function +def obj_expression(n, PQ_buses): + def obj_expression_m(m): + res = 0 + for id_b, row in n.get_buses().iterrows(): + if id_b not in PQ_buses: + continue + if not np.isnan(row["v_mag"]): + v_pu_coeff = n.get_voltage_levels().at[row["voltage_level_id"], "nominal_v"] + bus_v_pu = row["v_mag"] / v_pu_coeff # TO DO: to be replaced with pu getters + res += (m.V[id_b] - bus_v_pu) * (m.V[id_b] - bus_v_pu) + return res + return obj_expression_m + +# Active power balance +def active_power_balance_expr(n, buses, p_pu): + def active_power_balance_expr_m(m,i): + v_pu_coeff_i = n.get_voltage_levels().at[n.get_buses().at[i, "voltage_level_id"], "nominal_v"] + lhs = 0 + rhs = 0 + is_empty = True + for id_line, row in n.get_lines().iterrows(): + if row["bus1_id"] == i and row["bus2_id"] in buses: + is_empy = False + j = row["bus2_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu = 1 + line_g1_pu = row[ "g1"] / sus_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += ratio_tr_pu * m.V[i] * ( line_g1_pu * ratio_tr_pu * m.V[i] + line_y_pu * ratio_tr_pu * m.V[i] * pyo.sin(line_ksi) - line_y_pu * 1 * m.V[j] * pyo.sin(line_ksi - 0 + 0 - m.Phi[i] + m.Phi[j]) ) + elif row["bus2_id"] == i and row["bus1_id"] in buses: + is_empy = False + j = row["bus1_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu = 1 + line_g2_pu = row["g2"] / sus_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += 1 * m.V[i] * ( line_g2_pu * 1 * m.V[i] - line_y_pu * ratio_tr_pu * m.V[j] * pyo.sin(line_ksi + 0 - 0 + m.Phi[j] - m.Phi[i]) + line_y_pu * 1 * m.V[i] * pyo.sin(line_ksi) ) + for id_tr, row in n.get_2_windings_transformers().iterrows(): + if row["bus1_id"] == i and row["bus2_id"] in buses: + is_empy = False + j = row["bus2_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu_coeff = v_pu_coeff_j / v_pu_coeff_i + ratio_tr_pu = row["rated_u2"] / row["rated_u1"] / ratio_tr_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += ratio_tr_pu * m.V[i] * (line_y_pu * ratio_tr_pu * m.V[i] * pyo.sin(line_ksi) - line_y_pu * 1 * m.V[j] * pyo.sin(line_ksi - 0 + 0 - m.Phi[i] + m.Phi[j]) ) + elif row["bus2_id"] == i and row["bus1_id"] in buses: + is_empy = False + j = row["bus1_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu_coeff = v_pu_coeff_i / v_pu_coeff_j + ratio_tr_pu = row["rated_u2"] / row["rated_u1"] / ratio_tr_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += 1 * m.V[i] * (- line_y_pu * ratio_tr_pu * m.V[j] * pyo.sin(line_ksi + 0 - 0 + m.Phi[j] - m.Phi[i]) + line_y_pu * 1 * m.V[i] * pyo.sin(line_ksi) ) + for id_gen, row in n.get_generators().iterrows(): + if row["bus_id"] == i: + rhs += row["target_p"] / p_pu # TO DO: to be replaced with pu getters + for id_ld, row in n.get_loads().iterrows(): + if row["bus_id"] == i: + rhs -= row["p0"] / p_pu # TO DO: to be replaced with pu getters + if is_empty: + return pyo.Constraint.Skip + else: + return lhs == rhs + return active_power_balance_expr_m + +# Reactive power balance for PQ buses +def reactive_power_balancePQ_expr(n, buses, p_pu): + def reactive_power_balancePQ_expr_m(m,i): + v_pu_coeff_i = n.get_voltage_levels().at[n.get_buses().at[i, "voltage_level_id"], "nominal_v"] + lhs = 0 + rhs = 0 + is_empty = True + for id_line, row in n.get_lines().iterrows(): + if row["bus1_id"] == i and row["bus2_id"] in buses: + is_empy = False + j = row["bus2_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu = 1 + line_b1_pu = row[ "b1"] / sus_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += ratio_tr_pu * m.V[i] * (-line_b1_pu * ratio_tr_pu * m.V[i] + line_y_pu * ratio_tr_pu * m.V[i] * pyo.cos(line_ksi) - line_y_pu * 1 * m.V[j] * pyo.cos(line_ksi - 0 + 0 - m.Phi[i] + m.Phi[j]) ) + elif row["bus2_id"] == i and row["bus1_id"] in buses: + is_empy = False + j = row["bus1_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu = 1 + line_b2_pu = row["b2"] / sus_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += 1 * m.V[i] * ( - line_b2_pu * 1 * m.V[i] - line_y_pu * ratio_tr_pu * m.V[j] * pyo.cos(line_ksi + 0 - 0 + m.Phi[j] - m.Phi[i]) + line_y_pu * 1 * m.V[i] * pyo.cos(line_ksi) ) + for id_tr, row in n.get_2_windings_transformers().iterrows(): + if row["bus1_id"] == i and row["bus2_id"] in buses: + is_empy = False + j = row["bus2_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu_coeff = v_pu_coeff_j / v_pu_coeff_i + ratio_tr_pu = row["rated_u2"] / row["rated_u1"] / ratio_tr_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += ratio_tr_pu * m.V[i] * (line_y_pu * ratio_tr_pu * m.V[i] * pyo.cos(line_ksi) - line_y_pu * 1 * m.V[j] * pyo.cos(line_ksi - 0 + 0 - m.Phi[i] + m.Phi[j]) ) + elif row["bus2_id"] == i and row["bus1_id"] in buses: + is_empy = False + j = row["bus1_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu_coeff = v_pu_coeff_i / v_pu_coeff_j + ratio_tr_pu = row["rated_u2"] / row["rated_u1"] / ratio_tr_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += 1 * m.V[i] * ( - line_y_pu * ratio_tr_pu * m.V[j] * pyo.cos(line_ksi + 0 - 0 + m.Phi[j] - m.Phi[i]) + line_y_pu * 1 * m.V[i] * pyo.cos(line_ksi) ) + for id_gen, row in n.get_generators().iterrows(): + if row["bus_id"] == i: + rhs += row["target_q"] / p_pu # TO DO: to be replaced with pu getters + for id_ld, row in n.get_loads().iterrows(): + if row["bus_id"] == i: + rhs -= row["q0"] / p_pu # TO DO: to be replaced with pu getters + if is_empty: + return pyo.Constraint.Skip + else: + return lhs == rhs + return reactive_power_balancePQ_expr_m + +# Reactive power balance for PV buses +def reactive_power_balancePV_expr(n, buses, p_pu): + def reactive_power_balancePV_expr_m(m,i): + v_pu_coeff_i = n.get_voltage_levels().at[n.get_buses().at[i, "voltage_level_id"], "nominal_v"] + lhs = 0 + rhs = 0 + is_empty = True + for id_line, row in n.get_lines().iterrows(): + if row["bus1_id"] == i and row["bus2_id"] in buses: + is_empy = False + j = row["bus2_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu = 1 + line_b1_pu = row[ "b1"] / sus_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += ratio_tr_pu * m.V[i] * (-line_b1_pu * ratio_tr_pu * m.V[i] + line_y_pu * ratio_tr_pu * m.V[i] * pyo.cos(line_ksi) - line_y_pu * 1 * m.V[j] * pyo.cos(line_ksi - 0 + 0 - m.Phi[i] + m.Phi[j]) ) + elif row["bus2_id"] == i and row["bus1_id"] in buses: + is_empy = False + j = row["bus1_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu = 1 + line_b2_pu = row["b2"] / sus_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += 1 * m.V[i] * ( - line_b2_pu * 1 * m.V[i] - line_y_pu * ratio_tr_pu * m.V[j] * pyo.cos(line_ksi + 0 - 0 + m.Phi[j] - m.Phi[i]) + line_y_pu * 1 * m.V[i] * pyo.cos(line_ksi) ) + for id_tr, row in n.get_2_windings_transformers().iterrows(): + if row["bus1_id"] == i and row["bus2_id"] in buses: + is_empy = False + j = row["bus2_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu_coeff = v_pu_coeff_j / v_pu_coeff_i + ratio_tr_pu = row["rated_u2"] / row["rated_u1"] / ratio_tr_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += ratio_tr_pu * m.V[i] * (line_y_pu * ratio_tr_pu * m.V[i] * pyo.cos(line_ksi) - line_y_pu * 1 * m.V[j] * pyo.cos(line_ksi - 0 + 0 - m.Phi[i] + m.Phi[j]) ) + elif row["bus2_id"] == i and row["bus1_id"] in buses: + is_empy = False + j = row["bus1_id"] + v_pu_coeff_j = n.get_voltage_levels().at[n.get_buses().at[j, "voltage_level_id"], "nominal_v"] + sus_pu_coeff = p_pu / v_pu_coeff_i / v_pu_coeff_j #POWER/U/U + ratio_tr_pu_coeff = v_pu_coeff_i / v_pu_coeff_j + ratio_tr_pu = row["rated_u2"] / row["rated_u1"] / ratio_tr_pu_coeff # TO DO: to be replaced with pu getters + line_r = row["r"] + line_x = row["x"] + line_y_pu = 1 / np.sqrt(line_r * line_r + line_x * line_x) / sus_pu_coeff # TO DO: to be replaced with pu getters + line_ksi = np.arctan2(line_r,line_x) + lhs += 1 * m.V[i] * ( - line_y_pu * ratio_tr_pu * m.V[j] * pyo.cos(line_ksi + 0 - 0 + m.Phi[j] - m.Phi[i]) + line_y_pu * 1 * m.V[i] * pyo.cos(line_ksi) ) + rhs += m.Q[i] / p_pu + if is_empty: + return pyo.Constraint.Skip + else: + return lhs == rhs + return reactive_power_balancePV_expr_m + +# Reactive power bounds +def reactive_power_bounds(n): + def reactive_power_bounds_m(m, i): + lb = 0 + ub = 0 + for id_gen, row in n.get_generators().iterrows(): + if row["bus_id"] == i: + target_p = row["target_p"] + dist = np.nan + for id_curve, row in n.get_reactive_capability_curve_points().iterrows(): + if id_curve[0] == id_gen: + if np.isnan(dist): + dist = abs(target_p-row["p"]) + minq = row["min_q"] + maxq = row["max_q"] + else: + if abs(target_p-row["p"]) < dist: + dist = abs(target_p-row["p"]) + minq = row["min_q"] + maxq = row["max_q"] + lb += minq + ub += maxq + for id_ld, row in n.get_loads().iterrows(): + if row["bus_id"] == i: + lb -= row["q0"] + ub -= row["q0"] + return (lb, ub) + return reactive_power_bounds_m + +# Reactive power initialization +def reactive_init(n): + def reactive_init_m(m,i): + q_init = 0 + if q_init < m.Q[i].lb: + q_init = m.Q[i].lb + if q_init > m.Q[i].ub: + q_init = m.Q[i].ub + return q_init + return reactive_init_m + +# Voltage bounds +def voltage_bounds(n): + def voltage_bounds_m(m, i): + voltage_level_id = n.get_buses().at[i, "voltage_level_id"] + v_pu_coeff = n.get_voltage_levels().at[voltage_level_id, "nominal_v"] + v_min_pu = n.get_voltage_levels().at[voltage_level_id, "low_voltage_limit"] / v_pu_coeff # TO DO: to be replaced with pu getters + v_max_pu = n.get_voltage_levels().at[voltage_level_id, "high_voltage_limit"] / v_pu_coeff # TO DO: to be replaced with pu getters + return (v_min_pu, v_max_pu) + return voltage_bounds_m + +# Voltage initialization +def voltage_init(n): + def voltage_init_m(m,i): + voltage_level_id = n.get_buses().at[i, "voltage_level_id"] + v_init = n.get_buses().at[i,"v_mag"] + if np.isnan(v_init): + v_init_pu = 1 + else: + v_pu_coeff = n.get_voltage_levels().at[voltage_level_id, "nominal_v"] + v_init_pu = n.get_buses().at[i,"v_mag"] / v_pu_coeff # TO DO: to be replaced with pu getters + if v_init_pu < m.V[i].lb: + v_init_pu = m.V[i].lb + if v_init_pu > m.V[i].ub: + v_init_pu = m.V[i].ub + return v_init_pu + return voltage_init_m \ No newline at end of file diff --git a/simulator/network-reduction/pom.xml b/simulator/network-reduction/pom.xml new file mode 100644 index 00000000..c80d498f --- /dev/null +++ b/simulator/network-reduction/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-incubator-simulator + 1.0.0-SNAPSHOT + + + powsybl-incubator-simulator-network-reduction + Network reduction base on Ward injection equivalent + + + + com.powsybl + powsybl-incubator-simulator-util + ${project.version} + + + + com.powsybl + powsybl-incubator-simulator-util + ${project.version} + test-jar + test + + + com.powsybl + powsybl-config-test + test + + + com.powsybl + powsybl-ieee-cdf-converter + test + + + com.powsybl + powsybl-iidm-impl + test + + + ch.qos.logback + logback-classic + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + diff --git a/simulator/network-reduction/src/main/java/com/powsybl/incubator/simulator/networkreduction/ReductionEngine.java b/simulator/network-reduction/src/main/java/com/powsybl/incubator/simulator/networkreduction/ReductionEngine.java new file mode 100644 index 00000000..a9efa003 --- /dev/null +++ b/simulator/network-reduction/src/main/java/com/powsybl/incubator/simulator/networkreduction/ReductionEngine.java @@ -0,0 +1,681 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.networkreduction; + +import com.powsybl.incubator.simulator.util.AdmittanceEquationSystem; +import com.powsybl.incubator.simulator.util.AdmittanceMatrix; +import com.powsybl.incubator.simulator.util.EquationType; +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.VariableType; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import com.powsybl.openloadflow.equations.*; +import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; +import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.impl.LfNetworkLoaderImpl; +import com.powsybl.openloadflow.network.util.VoltageInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ReductionEngine { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReductionEngine.class); + + private final Network network; + + private final List lfNetworks; + + private final ReductionParameters parameters; + + public List getLfNetworks() { + return lfNetworks; + } + + private ReductionHypotheses reductionHypo; + + public ReductionHypotheses getReductionHypo() { + return reductionHypo; + } + + private ReductionResults results; + + public ReductionResults getReductionResults() { + return results; + } + + public enum ReductionType { + WARD_INJ, // all external nodal injections that does not come from branches are considered as current injectors (including shunts elements) + WARD_SHUNT, // all external nodal injections that does not come from branches are considered as current injectors (but not shunt elements) + WARD_ADMIT; // all external nodal injections are transformed into passive shunt elements included in the Y matrix (then [Ie] should be [0]) + } + + public class ReductionResults { + + private Matrix minusYeq; + + private Map busNumToRealIeq; + + private Map busNumToImagIeq; + + private Map yeqRowNumToBusNum; //gives the bus number from the row number of the Y submatrix in input + + private Map yeqRowNumToBusType; //gives the type of equation from the row number of the Y submatrix in input + + private Map yeqColNumToBusNum; //gives the bus number from the row number of the Y submatrix in input + + private Map yeqColNumToBusType; //gives the type of variable from the row number of the Y submatrix in input + + ReductionResults(Matrix minusYeq) { + this.minusYeq = minusYeq; + busNumToRealIeq = new HashMap<>(); + busNumToImagIeq = new HashMap<>(); + yeqRowNumToBusNum = new HashMap<>(); + yeqRowNumToBusType = new HashMap<>(); + yeqColNumToBusNum = new HashMap<>(); + yeqColNumToBusType = new HashMap<>(); + } + + public Matrix getMinusYeq() { + return minusYeq; + } + + public Map getBusNumToRealIeq() { + return busNumToRealIeq; + } + + public Map getBusNumToImagIeq() { + return busNumToImagIeq; + } + + public Map getYeqRowNumToBusNum() { + return yeqRowNumToBusNum; + } + + public Map getYeqRowNumToBusType() { + return yeqRowNumToBusType; + } + + public Map getYeqColNumToBusNum() { + return yeqColNumToBusNum; + } + + public Map getYeqColNumToBusType() { + return yeqColNumToBusType; + } + + void printReductionResults() { + for (Map.Entry i : getBusNumToRealIeq().entrySet()) { + int nb = i.getKey(); + System.out.println("Bus = " + nb + " has Ieq real = " + i.getValue()); + } + for (Map.Entry i : getBusNumToImagIeq().entrySet()) { + int nb = i.getKey(); + System.out.println("Bus = " + nb + " has Ieq imag = " + i.getValue()); + } + + for (int i = 0; i < minusYeq.getRowCount(); i++) { + System.out.println("RowYeq[" + i + "] = bus Num " + getYeqRowNumToBusNum().get(i) + " bus Eq type " + getYeqRowNumToBusType().get(i)); + } + for (int i = 0; i < minusYeq.getRowCount(); i++) { + System.out.println("ColYeq[" + i + "] = bus Num " + getYeqColNumToBusNum().get(i) + " bus Var type " + getYeqColNumToBusType().get(i)); + } + + System.out.println("===> -Yeq ="); + minusYeq.print(System.out); + } + + } + + public class ReductionHypotheses { + + private List yijBlocks; + + public List eqBranches; + + public List eqShunts; + + public List eqLoads; + + private class YijBlock { + + private int bus1; + private int bus2; + private double y1r2r; + private double y1r2i; + private double y1i2r; + private double y1i2i; + private double y2r1r; + private double y2r1i; + private double y2i1i; + private double y2i1r; + + YijBlock(int busi, int busj) { + + this.bus1 = busi; + this.bus2 = busj; + y1r2r = 0; + y1r2i = 0; + y1i2r = 0; + y1i2i = 0; + y2r1r = 0; + y2r1i = 0; + y2i1i = 0; + y2i1r = 0; + + } + } + + private YijBlock createYijBlock(int busi, int busj) { + YijBlock yb = new YijBlock(busi, busj); + return yb; + } + + ReductionHypotheses() { + yijBlocks = new ArrayList(); + eqBranches = new ArrayList(); + eqShunts = new ArrayList(); + eqLoads = new ArrayList(); + } + + private YijBlock getYijBlock(int i, int j) { + + if (yijBlocks.isEmpty()) { + return null; + } +/* + ListIterator itYb = yijBlocks.listIterator(); + { + yb0 = itYb.next(); + } + while (itYb.hasNext() && ((itYb.next().busi == i && itYb.next().busj == j) || (itYb.next().busi == j && itYb.next().busj == i) )) +*/ + YijBlock yb0 = null; + for (YijBlock yb : yijBlocks) { + if ((yb.bus1 == i && yb.bus2 == j) || (yb.bus1 == j && yb.bus2 == i)) { + yb0 = yb; + break; + } + } + + return yb0; + } + + public class EquivalentBranch { + + public double rEq; + public double xEq; + public double alphaEq; + public int bus1Eq; + public int bus2Eq; + + EquivalentBranch(int busi, int busj, double r, double x, double alpha) { + this.bus1Eq = busi; + this.bus2Eq = busj; + this.rEq = r; + this.xEq = x; + this.alphaEq = alpha; + } + } + + private EquivalentBranch createEquivalentBranch(int busi, int busj, double r, double x, double alpha) { + EquivalentBranch eqBr = new EquivalentBranch(busi, busj, r, x, alpha); + return eqBr; + } + + public class EquivalentShunt { + public double gEq; + public double bEq; + public int busEq; + + EquivalentShunt(int bus, double g, double b) { + this.busEq = bus; + this.gEq = g; + this.bEq = b; + } + } + + private EquivalentShunt createEquivalentShunt(int bus, double g, double b) { + EquivalentShunt eqSh = new EquivalentShunt(bus, g, b); + return eqSh; + } + + public class EquivalentLoad { + public double pEq; + public double qEq; + public int busEq; + + EquivalentLoad(int bus, double p, double q) { + this.busEq = bus; + this.pEq = p; + this.qEq = q; + } + } + + private EquivalentLoad createEquivalentLoad(int bus, double p, double q) { + EquivalentLoad eqLoad = new EquivalentLoad(bus, p, q); + return eqLoad; + } + } + + private Set extBusses; + + private Set borderBusses; + + public ReductionEngine(Network network, ReductionParameters parameters) { + this.network = Objects.requireNonNull(network); + this.lfNetworks = LfNetwork.load(network, new LfNetworkLoaderImpl(), new LfNetworkParameters(new FirstSlackBusSelector())); + this.parameters = Objects.requireNonNull(parameters); + extBusses = new HashSet<>(); + borderBusses = new HashSet<>(); + } + + public void run() { + //TODO: put type reduction in parameter + runWard(parameters.getReductionType()); + } + + public void runWard(ReductionType reductionType) { + LfNetwork lfNetwork = lfNetworks.get(0); + + // List and tag external + boarder + internal nodes from external voltagelevels list given in input + defineZones(lfNetwork); + + VoltageInitializer voltageInitializer = parameters.getVoltageInitializer(); + AdmittanceEquationSystem.AdmittanceVoltageProfileType admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED; + + AdmittanceEquationSystem.AdmittanceType admittanceType = AdmittanceEquationSystem.AdmittanceType.ADM_INJ; + if (reductionType == ReductionType.WARD_SHUNT) { + admittanceType = AdmittanceEquationSystem.AdmittanceType.ADM_SHUNT; + } else if (reductionType == ReductionType.WARD_ADMIT) { + admittanceType = AdmittanceEquationSystem.AdmittanceType.ADM_ADMIT; + } + + OpenLoadFlowParameters loadflowParametersExt = OpenLoadFlowParameters.get(parameters.getLoadFlowParameters()); + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, parameters.getLoadFlowParameters(), loadflowParametersExt, parameters.getMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + + EquationSystem equationSystem = AdmittanceEquationSystem.create(lfNetwork, new VariableSet<>(), admittanceType, admittanceVoltageProfileType, acLoadFlowParameters); + + // Reduction problem: + // The Grid is decomposed in 3 zones: internal (i), border (b), external (e) which is to be reduced + // Matrix decomposition gives: + // [[Yii] [Yib] [ 0 ]] [Vi] [Ii] + // [Y]*[V] = [I] <=> [[Ybi] [Ybb] [Ybe]] * [Vb] = [Ib] + // [[ 0 ] [Yeb] [Yee]] [Ve] [Ie] + // + // Use Gaussian elimination to remove external part (e): + // [[Yii] [Yib ]] [Ii ] + // [[Ybi] [Ybb']] = [Ib'] + // with + // Ybb' = Ybb + Yeq where Yeq = -Ybe * inv(Yee) * Yeb + // Ib' = Ib + Ieq where Ieq = -Ybe * inv(Yee) * Ie + // Resolving the reduction problem is equivalent to find Ieq and Yeq + + AdmittanceMatrix yeb = new AdmittanceMatrix(equationSystem, parameters.getMatrixFactory(), extBusses, borderBusses, lfNetwork); + AdmittanceMatrix yee = new AdmittanceMatrix(equationSystem, parameters.getMatrixFactory(), extBusses, extBusses, lfNetwork); + AdmittanceMatrix ybe = new AdmittanceMatrix(equationSystem, parameters.getMatrixFactory(), borderBusses, extBusses, lfNetwork); + AdmittanceMatrix ybb = new AdmittanceMatrix(equationSystem, parameters.getMatrixFactory(), borderBusses, borderBusses, lfNetwork); + + // Step 1: Get [Ie]: + // [Ie] = [Yeb]*[Vb] + [Yee]*[Ve] or t[Ie] = t[Vb]*t[Yeb] + t[Ve]*t[Yee] + Matrix tmYeb = yeb.getMatrix(); //tmYeb is the transposed of [Yeb] + Matrix tmVb = yeb.getVoltageVector(lfNetwork, voltageInitializer); //tmVb is the transposed of the column vector [Vb] + + Matrix tmYee = yee.getMatrix(); //tmYee is the transposed of [Yee] + Matrix tmVe = yee.getVoltageVector(lfNetwork, voltageInitializer); //tmVe is the transposed of the column vector [Ve] + + Matrix tmYebVb = tmVb.times(tmYeb); + Matrix tmYeeVe = tmVe.times(tmYee); + + Matrix tmIe = addRowVector(tmYebVb, tmYeeVe); + + //Step 2: Solve for [W] the following equation: [Yee]*[W] = [Yeb] or t[W]*t[Yee] = t[Yeb] + DenseMatrix dTmYeb = tmYeb.toDense(); + + DenseMatrix dYeb = yeb.transpose(); + + yee.solveTransposed(dYeb); + + DenseMatrix tmW = dYeb.transpose(); + + //Step 3: Compute -[Yeq] = [Ybe]*[W] or -t[Yeq] = t[W]*t[Ybe] + Matrix tmYbe = ybe.getMatrix(); //tmYbe is the transposed of [Ybe] + Matrix tmMinusYeq = tmW.times(tmYbe); + + //Step 4: Solve for [X] the following equation [Yee]*[X] = [Ie] or t[X]*t[Yee] = t[Ie] + double[] x = rowVectorToDouble(tmIe); + yee.solveTransposed(x); + + //Step 5: Compute -[Ieq] = [Ybe]*[X] or -t[Ieq] = t[X]*t[Ybe] + Matrix mX = Matrix.createFromRow(x, parameters.getMatrixFactory()); + Matrix tmMinusIeq = mX.times(tmYbe); + + // Reintegration of the Matrix results into the admittance system: + // [Ieq] and [Yeq] are of the dimension of [Ib] and [Ybb] respectively, we use Ybb admittance system as the structure for the reintegration of the results + results = new ReductionResults(tmMinusYeq); + processResults(ybb, rowVectorToDouble(tmMinusIeq), results); + + results.printReductionResults(); + + generateReductionHypotheses(); + } + + public void processResults(AdmittanceMatrix y, double[] minusIeq, ReductionEngine.ReductionResults rRes) { + + if (!y.getAdmSys().isSubAdmittance) { + throw new IllegalArgumentException("Result reintegration of a reduction problem are only relevant for a subsystem defined by the border area"); + } + + int yRowCount = y.getAdmSys().eqToRowNum.size(); //number of rows of Y (and number of columns of the "matrix" which is the transposed of Y) + int yColumnCount = y.getAdmSys().varToColNum.size(); //number of columns of Y (and number of rows of the "matrix" which is the transposed of Y) + + if (yRowCount != minusIeq.length) { + throw new IllegalArgumentException("Ieq must be of the same dimension than the number of rows of the Y admittance matrix"); + } + for (var eq : y.getEquationSystem().getIndex().getSortedEquationsToSolve()) { + int numBus = eq.getElementNum(); + if (y.getAdmSys().eqToRowNum.containsKey(eq)) { + int row = y.getAdmSys().eqToRowNum.get(eq); + rRes.getYeqRowNumToBusNum().put(row, numBus); + if (eq.getType() == EquationType.BUS_YR) { + rRes.getBusNumToRealIeq().put(numBus, -minusIeq[row]); + rRes.getYeqRowNumToBusType().put(row, EquationType.BUS_YR); + } else if (eq.getType() == EquationType.BUS_YI) { + rRes.getBusNumToImagIeq().put(numBus, -minusIeq[row]); + rRes.getYeqRowNumToBusType().put(row, EquationType.BUS_YI); + } + } + } + + if (yRowCount != yColumnCount) { + throw new IllegalArgumentException("Reduction algorithm must provide a square Yeq Matrix"); + } + for (Variable v : y.getEquationSystem().getIndex().getSortedVariablesToFind()) { + if (y.getAdmSys().varToColNum.containsKey(v)) { + int col = y.getAdmSys().varToColNum.get(v); + rRes.getYeqColNumToBusNum().put(col, v.getElementNum()); + if (v.getType() == VariableType.BUS_VR) { + rRes.getYeqColNumToBusType().put(col, VariableType.BUS_VR); + } else if (v.getType() == VariableType.BUS_VI) { + rRes.getYeqColNumToBusType().put(col, VariableType.BUS_VI); + } + } + } + } + + private Matrix addRowVector(Matrix m1, Matrix m2) { + DenseMatrix m1d = m1.toDense(); + DenseMatrix m2d = m2.toDense(); + for (int j = 0; j < m1d.getColumnCount(); j++) { + m1d.set(0, j, m1d.get(0, j) + m2d.get(0, j)); + } + return m1d; + } + + public double[] rowVectorToDouble(Matrix m1) { + DenseMatrix m1d = m1.toDense(); + double[] v = new double[m1d.getColumnCount()]; + for (int j = 0; j < m1d.getColumnCount(); j++) { + v[j] = m1d.get(0, j); + } + return v; + } + + private void defineZones(LfNetwork network) { + for (LfBranch br : network.getBranches()) { + if (br.getBus1() != null && br.getBus2() == null) { + if (parameters.getExternalVoltageLevels().contains(br.getBus1().getVoltageLevelId())) { + extBusses.add(br.getBus1()); + } + } else if (br.getBus2() != null && br.getBus1() == null) { + if (parameters.getExternalVoltageLevels().contains(br.getBus2().getVoltageLevelId())) { + extBusses.add(br.getBus2()); + } + } else if (br.getBus2() != null && br.getBus1() != null) { + LfBus b1 = br.getBus1(); + LfBus b2 = br.getBus2(); + boolean isB1Ext = parameters.getExternalVoltageLevels().contains(b1.getVoltageLevelId()); + boolean isB2Ext = parameters.getExternalVoltageLevels().contains(b2.getVoltageLevelId()); + if (isB1Ext && isB2Ext) { + extBusses.add(b1); + extBusses.add(b2); + } else if (isB1Ext) { + extBusses.add(b1); + borderBusses.add(b2); + } else if (isB2Ext) { + extBusses.add(b2); + borderBusses.add(b1); + } + } + } + + //check built zones + for (LfBus b : extBusses) { + System.out.println("Bus = " + b.getId() + " is ext "); + } + + for (LfBus b : borderBusses) { + System.out.println("Bus = " + b.getId() + " is border "); + } + + } + + // Example to compute full sytem nodal current injectors I = Y*V + /*public void computeCurrentInjections(AdmittanceEquationSystem.AdmittanceType admittanceType) { + //test with full sytem compute I = Y*V + + LfNetwork lfNetwork = lfNetworks.get(0); + + OpenLoadFlowParameters loadflowParametersExt = OpenLoadFlowParameters.get(parameters.getLoadFlowParameters()); + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, parameters.getLoadFlowParameters(), loadflowParametersExt, parameters.getMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + AdmittanceEquationSystem.AdmittanceVoltageProfileType admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED; + + EquationSystem equationSystem = AdmittanceEquationSystem.create(lfNetwork, parameters.getMatrixFactory(), new VariableSet<>(), admittanceType, admittanceVoltageProfileType, acLoadFlowParameters); + + VoltageInitializer voltageInitializer = parameters.getVoltageInitializer(); + + AdmittanceMatrix a = new AdmittanceMatrix(equationSystem, parameters.getMatrixFactory(), lfNetwork); + Matrix a1 = a.getMatrix(); + Matrix mV = a.getVoltageVector(lfNetwork, voltageInitializer); + System.out.println("v = "); + mV.print(System.out); + + Matrix mI = mV.times(a1); + System.out.println("i = "); + mI.print(System.out); + }*/ + + public void generateReductionHypotheses() { + + LfNetwork network = lfNetworks.get(0); + + reductionHypo = new ReductionHypotheses(); + DenseMatrix m = results.minusYeq.toDense(); + + //step 1: extract values of [Yeq] binding upper and under extra-diagonal terms together if they have the same nodes + // + // [I1r] [ y1r1r y1r1i y1r2r y1r2i ] [ g1+g12 -b1-b12 -g12 b12 ] [V1r] + // [I1i] [ y1i1r y1i1i y1i2r y1i2i ] [ b1+b12 g1+g12 -b12 -g12 ] [V1i] + // [I2r] = [ y2r1r y2r1i y2r2r y2r2i ] * [V] = [ -g21 b21 g2+g21 -b2-b21 ] * [V2r] + // [I2i] [ y2i1r y2i1i y2i2r y2i2i ] [ -b21 -g21 b2+b21 g2+g21 ] [V2i] + // + // + HashMap busNumtToDiagonalBlock = new HashMap<>(); //use for a direct access to diagonal blocks from bus num + for (int mi = 0; mi < results.minusYeq.getRowCount(); mi++) { + + int busi = results.yeqRowNumToBusNum.get(mi); + EquationType typei = results.yeqRowNumToBusType.get(mi); + + for (int mj = 0; mj < results.minusYeq.getRowCount(); mj++) { + int busj = results.yeqColNumToBusNum.get(mj); + + if (Math.abs(m.get(mj, mi)) > 0.00001) { //no block created or modified if matrix term is zero + // mi and mj inverted because "m" is the transposed of Y standard admittance matrix + + VariableType typej = results.yeqColNumToBusType.get(mj); + + //Create or update Yij block + ReductionHypotheses.YijBlock yb = reductionHypo.getYijBlock(busi, busj); + if (yb == null) { + yb = reductionHypo.createYijBlock(busi, busj); + reductionHypo.yijBlocks.add(yb); + if (busi == busj) { + busNumtToDiagonalBlock.put(busi, yb); + } + } + if (busi == yb.bus1) { //in the case where busi = busj only y1r2r,y1r2i,y1i2i,y1i2r are useful + if (typei == EquationType.BUS_YR && typej == VariableType.BUS_VR) { + yb.y1r2r = -m.get(mj, mi); //mi and mj inverted as "m" is the transposed of Y + } else if (typei == EquationType.BUS_YR && typej == VariableType.BUS_VI) { + yb.y1r2i = -m.get(mj, mi); + } else if (typei == EquationType.BUS_YI && typej == VariableType.BUS_VI) { + yb.y1i2i = -m.get(mj, mi); + } else if (typei == EquationType.BUS_YI && typej == VariableType.BUS_VR) { + yb.y1i2r = -m.get(mj, mi); + } + } else if (busi == yb.bus2) { + if (typei == EquationType.BUS_YR && typej == VariableType.BUS_VR) { + yb.y2r1r = -m.get(mj, mi); + } else if (typei == EquationType.BUS_YR && typej == VariableType.BUS_VI) { + yb.y2r1i = -m.get(mj, mi); + } else if (typei == EquationType.BUS_YI && typej == VariableType.BUS_VI) { + yb.y2i1i = -m.get(mj, mi); + } else if (typei == EquationType.BUS_YI && typej == VariableType.BUS_VR) { + yb.y2i1r = -m.get(mj, mi); + } + + } + } + } + } + + //step 2: check the consistency of the blocks since some terms must be equal to deduce an equivalent branch + //step 3: deduce g12, b12, g21, b21 and then g1, b1, g2, b2 + for (ReductionHypotheses.YijBlock yb : reductionHypo.yijBlocks) { + //step 2 + double epsilon = 0.00001; + if (Math.abs(yb.y1r2r - yb.y1i2i) > epsilon) { + throw new IllegalArgumentException("Admittance block values y1r2r and y1i2i of nodes num {" + yb.bus1 + ";" + yb.bus2 + "} have inconsitant values y1r2r= " + yb.y1r2r + " y1i2i=" + yb.y1i2i); + } + if (Math.abs(yb.y1i2r + yb.y1r2i) > epsilon) { + throw new IllegalArgumentException("Admittance block values y1i2r and y1r2i of nodes num {" + yb.bus1 + ";" + yb.bus2 + "} have inconsitant values"); + } + if (Math.abs(yb.y2r1r - yb.y2i1i) > epsilon) { + throw new IllegalArgumentException("Admittance block values y2r1r and y2i1i of nodes num {" + yb.bus1 + ";" + yb.bus2 + "} have inconsitant values"); + } + if (Math.abs(yb.y2i1r + yb.y2r1i) > epsilon) { + throw new IllegalArgumentException("Admittance block values y2i1r and y2r1i of nodes num {" + yb.bus1 + ";" + yb.bus2 + "} have inconsitant values"); + } + } + + for (ReductionHypotheses.YijBlock yb : reductionHypo.yijBlocks) { + //step 3 + double g12 = 0; + double b12 = 0; + double g21 = 0; + double b21 = 0; + + if (yb.bus1 != yb.bus2) { + g12 = -yb.y1r2r; + b12 = yb.y1r2i; + g21 = -yb.y2r1r; + b21 = yb.y2r1i; + + //remove g12 from diagonal term y1r2r = g1 + sum(g1i) (for a diagonal block y1r2r is equivalent to y1r1r since bus1 = bus2) + busNumtToDiagonalBlock.get(yb.bus1).y1r2r = busNumtToDiagonalBlock.get(yb.bus1).y1r2r - g12; + //remove b12 from diagonal term y1i2r = b1 + sum(b1i) + busNumtToDiagonalBlock.get(yb.bus1).y1i2r = busNumtToDiagonalBlock.get(yb.bus1).y1i2r - b12; + //remove g21 from diagonal term y1r2r = g2 + sum(g2i) + busNumtToDiagonalBlock.get(yb.bus2).y1r2r = busNumtToDiagonalBlock.get(yb.bus2).y1r2r - g21; + //remove b21 from diagonal term y1i2r = b2 + sum(b2i) + busNumtToDiagonalBlock.get(yb.bus2).y1i2r = busNumtToDiagonalBlock.get(yb.bus2).y1i2r - b21; + + //deduce branch characteristics from g12, b12, g21, b21 + // we use the hypothesis that rho = 1 but in case nominal voltage values of voltage levels are not the same, we will have to create a transformer with rho = 1 + double denom = g21 * g12 + b21 * b12; + double alpha = 0.; + if (denom > 0.0001) { + alpha = 0.5 * Math.atan((b21 * g12 - b12 * g21) / denom); + } + + double r = (g12 * Math.cos(alpha) - b12 * Math.sin(alpha)) / (g12 * g12 + b12 * b12); + double x = -(b12 * Math.cos(alpha) + g12 * Math.sin(alpha)) / (g12 * g12 + b12 * b12); + + ReductionHypotheses.EquivalentBranch eqBr = reductionHypo.createEquivalentBranch(yb.bus1, yb.bus2, r, x, alpha); + reductionHypo.eqBranches.add(eqBr); + System.out.println("Equivalent branch r=" + r + " x=" + x + " at busses = " + yb.bus1 + ";" + yb.bus2); + } + } + + //remove g1 + g12 of branches linking E and B zones + for (LfBranch branch : network.getBranches()) { + LfBus bus1 = branch.getBus1(); + LfBus bus2 = branch.getBus2(); + PiModel piModel = branch.getPiModel(); + if (extBusses.contains(bus1) && borderBusses.contains(bus2)) { + double b2 = 0.; // g2g21sum = r * zInvSquare + gPi2; + double g2 = 0.; // b2b21sum = -x * zInvSquare + bPi2; + g2 = piModel.getR() / (piModel.getZ() * piModel.getZ()) + piModel.getG2(); + b2 = -piModel.getX() / (piModel.getZ() * piModel.getZ()) + piModel.getB2(); + + busNumtToDiagonalBlock.get(bus2.getNum()).y1r2r = busNumtToDiagonalBlock.get(bus2.getNum()).y1r2r + g2; + busNumtToDiagonalBlock.get(bus2.getNum()).y1i2r = busNumtToDiagonalBlock.get(bus2.getNum()).y1i2r + b2; + + } else if (extBusses.contains(bus2) && borderBusses.contains(bus1)) { + double b1 = 0.; //g1g12sum = rho * rho * (gPi1 + r * zInvSquare); + double g1 = 0.; //b1b12sum = rho * rho * (bPi1 - x * zInvSquare); + g1 = piModel.getR1() * piModel.getR1() * (piModel.getG1() + piModel.getR() / (piModel.getZ() * piModel.getZ())); + b1 = piModel.getR1() * piModel.getR1() * (piModel.getB1() - piModel.getX() / (piModel.getZ() * piModel.getZ())); + + busNumtToDiagonalBlock.get(bus1.getNum()).y1r2r = busNumtToDiagonalBlock.get(bus1.getNum()).y1r2r + g1; + busNumtToDiagonalBlock.get(bus1.getNum()).y1i2r = busNumtToDiagonalBlock.get(bus1.getNum()).y1i2r + b1; + } + } + + //step4: generate hypotheses of creation of equivalent shunts for remaining g1, b1 and g2, b2 + //check if we need to remove EB lines from g1 and b1??? + for (Map.Entry diagBlock : busNumtToDiagonalBlock.entrySet()) { + int busNum = diagBlock.getKey(); + ReductionHypotheses.YijBlock yb = diagBlock.getValue(); + double g1 = 0; + double b1 = 0; + g1 = yb.y1r2r; + b1 = yb.y1i2r; + if (Math.abs(g1) > 0.000001 || Math.abs(b1) > 0.000001) { + ReductionHypotheses.EquivalentShunt eqSh = reductionHypo.createEquivalentShunt(busNum, g1, b1); + reductionHypo.eqShunts.add(eqSh); + System.out.println("Equivalent shunt g=" + g1 + " b=" + b1 + " at bus num=" + busNum); + } + } + + //step5: generation of equivalent load injections from equivalent currents + for (Map.Entry ieq : results.busNumToRealIeq.entrySet()) { + int busNum = ieq.getKey(); + double ir = ieq.getValue(); + double ii = results.busNumToImagIeq.get(busNum); + + double vr = parameters.getVoltageInitializer().getMagnitude(network.getBus(busNum)) * Math.cos(Math.toRadians(parameters.getVoltageInitializer().getAngle(network.getBus(busNum)))); + double vi = parameters.getVoltageInitializer().getMagnitude(network.getBus(busNum)) * Math.sin(Math.toRadians(parameters.getVoltageInitializer().getAngle(network.getBus(busNum)))); + + double pEq = -(vr * ir + vi * ii); + double qEq = ii * vr - vi * ir; + + if (Math.abs(pEq) > 0.00001 || Math.abs(qEq) > 0.00001) { + ReductionHypotheses.EquivalentLoad eqLoad = reductionHypo.createEquivalentLoad(busNum, pEq, qEq); + reductionHypo.eqLoads.add(eqLoad); + System.out.println("Equivalent load P=" + pEq + " Q=" + qEq + " at bus num=" + busNum); + } + } + } +} diff --git a/simulator/network-reduction/src/main/java/com/powsybl/incubator/simulator/networkreduction/ReductionParameters.java b/simulator/network-reduction/src/main/java/com/powsybl/incubator/simulator/networkreduction/ReductionParameters.java new file mode 100644 index 00000000..c9bab1b9 --- /dev/null +++ b/simulator/network-reduction/src/main/java/com/powsybl/incubator/simulator/networkreduction/ReductionParameters.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.networkreduction; + +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.network.util.PreviousValueVoltageInitializer; +import com.powsybl.openloadflow.network.util.VoltageInitializer; + +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ReductionParameters { + + private final LoadFlowParameters loadFlowParameters; + + private final MatrixFactory matrixFactory; + + private VoltageInitializer voltageInitializer = new PreviousValueVoltageInitializer(); //TODO: check why previous does not work + + private final List externalVoltageLevels; + + private final ReductionEngine.ReductionType reductionType; + + public ReductionParameters(LoadFlowParameters loadFlowParameters, MatrixFactory matrixFactory, List externalVoltageLevels, ReductionEngine.ReductionType reductionType) { + this.loadFlowParameters = Objects.requireNonNull(loadFlowParameters); + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.externalVoltageLevels = Objects.requireNonNull(externalVoltageLevels); + this.reductionType = Objects.requireNonNull(reductionType); + } + + public LoadFlowParameters getLoadFlowParameters() { + return loadFlowParameters; + } + + public MatrixFactory getMatrixFactory() { + return matrixFactory; + } + + public VoltageInitializer getVoltageInitializer() { + return voltageInitializer; + } + + public List getExternalVoltageLevels() { + return externalVoltageLevels; + } + + public ReductionEngine.ReductionType getReductionType() { + return reductionType; + } +} diff --git a/simulator/network-reduction/src/test/java/com/powsybl/incubator/simulator/networkreduction/ReductionTest.java b/simulator/network-reduction/src/test/java/com/powsybl/incubator/simulator/networkreduction/ReductionTest.java new file mode 100644 index 00000000..436be6fd --- /dev/null +++ b/simulator/network-reduction/src/test/java/com/powsybl/incubator/simulator/networkreduction/ReductionTest.java @@ -0,0 +1,257 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.networkreduction; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.ieeecdf.converter.IeeeCdfNetworkFactory; +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import com.powsybl.openloadflow.equations.EquationSystem; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.util.VoltageInitializer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +class ReductionTest { + + private LoadFlowParameters loadFlowParameters; + + private OpenLoadFlowParameters loadFlowParametersExt; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + loadFlowParameters = new LoadFlowParameters(); + loadFlowParametersExt = OpenLoadFlowParameters.get(loadFlowParameters) + .setAddRatioToLinesWithDifferentNominalVoltageAtBothEnds(false); + loadFlowParameters.addExtension(OpenLoadFlowParameters.class, loadFlowParametersExt); + + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + /** + * Check behaviour of the load flow for simple manipulations on eurostag example 1 network. + * - line opening + * - load change + */ + @Test + void computeCurrentInjectorTest() { + Network network = IeeeCdfNetworkFactory.create14(); + + List voltageLevels = new ArrayList<>(); + + ReductionParameters reductionParameters = new ReductionParameters(loadFlowParameters, matrixFactory, voltageLevels, ReductionEngine.ReductionType.WARD_INJ); + + ReductionEngine re = new ReductionEngine(network, reductionParameters); + LfNetwork lfNetwork = re.getLfNetworks().get(0); + + OpenLoadFlowParameters loadflowParametersExt = OpenLoadFlowParameters.get(loadFlowParameters); + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, loadFlowParameters, loadflowParametersExt, matrixFactory, new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + AdmittanceEquationSystem.AdmittanceVoltageProfileType admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED; + //AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(); + AdmittanceEquationSystem.AdmittancePeriodType admittancePeriodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_TRANSIENT; + EquationSystemFeeders feeders = new EquationSystemFeeders(); + EquationSystem equationSystem = AdmittanceEquationSystem.create(lfNetwork, new VariableSet<>(), AdmittanceEquationSystem.AdmittanceType.ADM_INJ, admittanceVoltageProfileType, admittancePeriodType, false, feeders, acLoadFlowParameters); + + VoltageInitializer voltageInitializer = reductionParameters.getVoltageInitializer(); + + AdmittanceMatrix a = new AdmittanceMatrix(equationSystem, reductionParameters.getMatrixFactory(), lfNetwork); + Matrix a1 = a.getMatrix(); + Matrix mV = a.getVoltageVector(lfNetwork, voltageInitializer); + //System.out.println("===> v ="); + //mV.print(System.out); + + Matrix mI = mV.times(a1); + //System.out.println("===> i ="); + //mI.print(System.out); + + double[] x = re.rowVectorToDouble(mI); + + assertEquals(2.1919, x[0], 0.01); + assertEquals(0.1581, x[1], 0.01); + assertEquals(0.1506, x[2], 0.01); + assertEquals(-0.2990, x[3], 0.01); + } + + @Test + void computeCurrentInjectorWithLfResultsTest() { + Network network = IeeeCdfNetworkFactory.create14(); + + List voltageLevels = new ArrayList<>(); + + LoadFlowResult resultLf = loadFlowRunner.run(network, loadFlowParameters); + + ReductionParameters reductionParameters = new ReductionParameters(loadFlowParameters, matrixFactory, voltageLevels, ReductionEngine.ReductionType.WARD_INJ); + + ReductionEngine re = new ReductionEngine(network, reductionParameters); + LfNetwork lfNetwork = re.getLfNetworks().get(0); + + OpenLoadFlowParameters loadflowParametersExt = OpenLoadFlowParameters.get(loadFlowParameters); + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, loadFlowParameters, loadflowParametersExt, matrixFactory, new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + AdmittanceEquationSystem.AdmittanceVoltageProfileType admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED; + AdmittanceEquationSystem.AdmittancePeriodType admittancePeriodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_TRANSIENT; + //AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(); + EquationSystemFeeders feeders = new EquationSystemFeeders(); + EquationSystem equationSystem = AdmittanceEquationSystem.create(lfNetwork, new VariableSet<>(), AdmittanceEquationSystem.AdmittanceType.ADM_INJ, admittanceVoltageProfileType, admittancePeriodType, false, feeders, acLoadFlowParameters); + + VoltageInitializer voltageInitializer = reductionParameters.getVoltageInitializer(); + + AdmittanceMatrix a = new AdmittanceMatrix(equationSystem, reductionParameters.getMatrixFactory(), lfNetwork); + Matrix a1 = a.getMatrix(); + Matrix mV = a.getVoltageVector(lfNetwork, voltageInitializer); + //System.out.println("===> v ="); + //mV.print(System.out); + + Matrix mI = mV.times(a1); + //System.out.println("===> i ="); + //mI.print(System.out); + + double[] x = re.rowVectorToDouble(mI); + + assertEquals(2.192, x[0], 0.001); + assertEquals(0.156, x[1], 0.001); + assertEquals(0.148, x[2], 0.001); + assertEquals(-0.309, x[3], 0.001); + } + + @Test + void ieee14ReductionTest() { + + Network network = IeeeCdfNetworkFactory.create14(); + + List voltageLevels = new ArrayList<>(); + voltageLevels.add("VL12"); + voltageLevels.add("VL13"); + + LoadFlowResult resultLf = loadFlowRunner.run(network, loadFlowParameters); + + ReductionParameters reductionParameters = new ReductionParameters(loadFlowParameters, matrixFactory, voltageLevels, ReductionEngine.ReductionType.WARD_INJ); + + ReductionEngine re = new ReductionEngine(network, reductionParameters); + + re.run(); + + ReductionEngine.ReductionResults results = re.getReductionResults(); + + DenseMatrix m = results.getMinusYeq().toDense(); + + assertEquals(2, results.getBusNumToRealIeq().size(), 0); + assertEquals(3.730432312, m.get(0, 0), 0.000001); + /*assertEquals(-0.9770922266610, m.get(1, 0), 0.000001); + assertEquals(-4.0518208119503, m.get(2, 0), 0.000001); + assertEquals(-1.1284861339383, m.get(3, 0), 0.000001); + assertEquals(0.9770922266610, m.get(0, 1), 0.000001); + assertEquals(-10.640432247821, m.get(1, 1), 0.000001); + assertEquals(1.1284861339383, m.get(2, 1), 0.000001); + assertEquals(-4.051820811950, m.get(3, 1), 0.000001); + assertEquals(-3.2261022158310, m.get(0, 2), 0.000001); + assertEquals(-0.3281562136182, m.get(1, 2), 0.000001); + assertEquals(-1.3282719530734, m.get(2, 2), 0.000001); + assertEquals(-0.36576402166078, m.get(3, 2), 0.000001); + assertEquals(0.3281562136182, m.get(0, 3), 0.000001); + assertEquals(-3.2261022158310, m.get(1, 3), 0.000001); + assertEquals(0.3657640216607, m.get(2, 3), 0.000001); + assertEquals(-1.328271953073, m.get(3, 3), 0.000001);*/ + + ReductionEngine.ReductionHypotheses hypo = re.getReductionHypo(); + assertEquals(0.159817620898, hypo.eqLoads.get(0).pEq, 0.00001); + /*assertEquals(0.062286236316, hypo.eqLoads.get(0).qEq, 0.00001); + assertEquals(13.86653446365, hypo.eqShunts.get(0).gEq, 0.0001); + assertEquals(1.305248440279, hypo.eqShunts.get(0).bEq, 0.0001); + assertEquals(-0.3030327598342, hypo.eqBranches.get(0).rEq, 0.0001); + assertEquals(0.05718010758327, hypo.eqBranches.get(0).xEq, 0.0001);*/ + + } + + @Test + void example4nReductionTest() { + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + //Create a list of voltage of the external network + List voltageLevels = new ArrayList<>(); + voltageLevels.add("VL_4"); + + Network nt4 = Networks.create4n(); + LoadFlowResult resultnt4 = loadFlowRunner.run(nt4, loadFlowParameters); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + ReductionParameters reductionParameters = new ReductionParameters(loadFlowParameters, matrixFactory, voltageLevels, ReductionEngine.ReductionType.WARD_INJ); + ReductionEngine re = new ReductionEngine(nt4, reductionParameters); + + re.run(); + + ReductionEngine.ReductionResults results = re.getReductionResults(); + ReductionEngine.ReductionHypotheses hypo = re.getReductionHypo(); + + assertEquals(0.045, hypo.eqBranches.get(0).xEq, 0.00001); + } + + @Test + void example4nShuntReductionTest() { + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + //Create a list of voltage of the external network + List voltageLevels = new ArrayList<>(); + voltageLevels.add("VL_4"); + + Network nt4Shunt = Networks.create4nShunt(); + LoadFlowResult resultnt4Shunt = loadFlowRunner.run(nt4Shunt, loadFlowParameters); + + ReductionParameters reductionParameters = new ReductionParameters(loadFlowParameters, matrixFactory, voltageLevels, ReductionEngine.ReductionType.WARD_SHUNT); + ReductionEngine re = new ReductionEngine(nt4Shunt, reductionParameters); + + re.run(); + + ReductionEngine.ReductionResults results = re.getReductionResults(); + ReductionEngine.ReductionHypotheses hypo = re.getReductionHypo(); + + assertEquals(0.0450525, hypo.eqBranches.get(0).xEq, 0.0000001); // line between B1 and B3 + assertEquals(-0.046612, hypo.eqShunts.get(0).bEq, 0.000001); // shunt at bus 1 + assertEquals(-0.058265, hypo.eqShunts.get(1).bEq, 0.000001); // shunt at bus 3 + + //we compare the previous network with the next one removing all equiments of BUS 4 and adding the previous hypotheses + Network nt4ShuntEq = Networks.create4nShuntEq(); + LoadFlowResult resultnt4Eq = loadFlowRunner.run(nt4ShuntEq, loadFlowParameters); + + double v1 = nt4Shunt.getBusBreakerView().getBus("B1").getV(); + double v2 = nt4Shunt.getBusBreakerView().getBus("B2").getV(); + double v3 = nt4Shunt.getBusBreakerView().getBus("B3").getV(); + + double v1Eq = nt4ShuntEq.getBusBreakerView().getBus("B1").getV(); + double v2Eq = nt4ShuntEq.getBusBreakerView().getBus("B2").getV(); + double v3Eq = nt4ShuntEq.getBusBreakerView().getBus("B3").getV(); + + assertEquals(v1, v1Eq, 0.000001); + assertEquals(v2, v2Eq, 0.000001); + assertEquals(v3, v3Eq, 0.000001); + + } +} diff --git a/simulator/pom.xml b/simulator/pom.xml new file mode 100644 index 00000000..46245fe4 --- /dev/null +++ b/simulator/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-incubator + 1.0.0-SNAPSHOT + + + powsybl-incubator-simulator + 1.0.0-SNAPSHOT + pom + + Incubator for simulators + + + network-reduction + util + short-circuit + + + + 11 + 5.8.2 + + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + + diff --git a/simulator/short-circuit/pom.xml b/simulator/short-circuit/pom.xml new file mode 100644 index 00000000..9670e14c --- /dev/null +++ b/simulator/short-circuit/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + powsybl-incubator-simulator + com.powsybl + 1.0.0-SNAPSHOT + + + powsybl-incubator-simulator-short-circuit + Short circuit calculation + + + + com.powsybl + powsybl-shortcircuit-api + + + com.powsybl + powsybl-incubator-simulator-util + ${project.version} + + + + com.powsybl + powsybl-config-test + test + + + com.powsybl + powsybl-iidm-impl + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.slf4j + slf4j-simple + test + + + com.powsybl + powsybl-incubator-simulator-util + ${project.version} + test-jar + test + + + \ No newline at end of file diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/AbstractShortCircuitCalculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/AbstractShortCircuitCalculator.java new file mode 100644 index 00000000..e65cfd3c --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/AbstractShortCircuitCalculator.java @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public abstract class AbstractShortCircuitCalculator { + + Matrix mIo; + Matrix mId; + Matrix mIi; + + Matrix mIk; //contains the shortcircuit values + + double rdf; + double xdf; + + double xof; + double rof; + + double rg; + double xg; + + double initVx; + double initVy; + + MatrixFactory mf; + + public AbstractShortCircuitCalculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf) { + this.rdf = rdf; + this.xdf = xdf; + this.rof = rof; + this.xof = xof; + this.rg = rg; + this.xg = xg; + this.initVx = initVx; + this.initVy = initVy; + this.mf = mf; + + } + + Matrix getmIo() { + return mIo; + } + + Matrix getmId() { + return mId; + } + + Matrix getmIi() { + return mIi; + } + + public abstract void computeCurrents(); + + public enum BlocType { + A, + A2, + Id, + J + } + + public static Matrix getMatrixByType(BlocType bt, double scalar, MatrixFactory mf) { + Matrix m = mf.create(2, 2, 2); + addMatrixBlocByType(m, bt, scalar); + return m; + } + + public static void addMatrixBlocByType(Matrix m, BlocType bt, double scalar) { + if (bt == BlocType.A) { + addMatrixBloc(m, 1, -1. / 2. * scalar, -Math.sqrt(3.) / 2. * scalar, Math.sqrt(3.) / 2. * scalar, -1. / 2. * scalar); + } else if (bt == BlocType.A2) { + addMatrixBloc(m, 1, 1. / 2. * scalar, -Math.sqrt(3.) / 2. * scalar, Math.sqrt(3.) / 2. * scalar, 1. / 2. * scalar); + } else if (bt == BlocType.Id) { + addMatrixBloc(m, 1, scalar, 0., 0., scalar); + } else if (bt == BlocType.J) { + addMatrixBloc(m, 1, 0., -scalar, scalar, 0.); + } else { + throw new IllegalArgumentException("Bloc type unknown "); + } + } + + public static void addMatrixBloc(Matrix m, int numBloc, double m11, double m12, double m21, double m22) { + + if (numBloc != 1 && numBloc != 2) { + throw new IllegalArgumentException("Bloc number must be either 1 or 2 "); + } + + int iIndex = 0; + if (numBloc == 2) { + iIndex = 2; + } + + m.add(iIndex, 0, m11); + m.add(iIndex, 1, m12); + m.add(iIndex + 1, 0, m21); + m.add(iIndex + 1, 1, m22); + } + + public static DenseMatrix addMatrices22(DenseMatrix m1, DenseMatrix m2, MatrixFactory mf) { + Matrix m = mf.create(2, 2, 2); + + m.add(0, 0, m1.get(0, 0) + m2.get(0, 0)); + m.add(0, 1, m1.get(0, 1) + m2.get(0, 1)); + m.add(1, 0, m1.get(0, 1) + m2.get(1, 0)); + m.add(1, 1, m1.get(0, 1) + m2.get(1, 1)); + + return m.toDense(); + } + + public static Matrix getInvZt(double r, double x, MatrixFactory mf) { + double detZ = r * r + x * x; + Matrix invZ = mf.create(2, 2, 2); + invZ.add(0, 0, r / detZ); + invZ.add(0, 1, x / detZ); + invZ.add(1, 0, -x / detZ); + invZ.add(1, 1, r / detZ); + + return invZ; + } + + public static Matrix getInvZt(Matrix m, MatrixFactory mf) { + double r = m.toDense().get(0, 0); + double x = m.toDense().get(1, 0); + + return getInvZt(r, x, mf); + } + + public static Matrix getZ(double r, double x, MatrixFactory mf) { + Matrix z = mf.create(2, 2, 2); + z.add(0, 0, r); + z.add(0, 1, -x); + z.add(1, 0, x); + z.add(1, 1, r); + + return z; + } + +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/AbstractShortCircuitEngine.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/AbstractShortCircuitEngine.java new file mode 100644 index 00000000..cc544942 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/AbstractShortCircuitEngine.java @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.incubator.simulator.util.AdmittanceEquationSystem; +import com.powsybl.incubator.simulator.util.CalculationLocation; +import com.powsybl.incubator.simulator.util.extensions.ShortCircuitExtensions; +import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; +import com.powsybl.openloadflow.network.FirstSlackBusSelector; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.LfNetworkParameters; +import com.powsybl.openloadflow.network.impl.LfNetworkLoaderImpl; +import org.apache.commons.math3.util.Pair; + +import java.util.*; + +/** + * @author Jean-Baptiste Heyberger + */ +public abstract class AbstractShortCircuitEngine { + protected final Network network; + + protected final ShortCircuitEngineParameters parameters; + + protected final List lfNetworks; + + public Map resultsPerFault = new HashMap<>(); + + protected List solverFaultList; // list of faults provided to the solver (not including biphased common support faults) + + protected List solverBiphasedFaultList; // list of biphased common support faults provided to the solver + + protected final AcLoadFlowParameters acLoadFlowParameters; + + public AbstractShortCircuitEngine(Network network, ShortCircuitEngineParameters parameters) { + this.network = Objects.requireNonNull(network); + this.parameters = Objects.requireNonNull(parameters); + this.lfNetworks = LfNetwork.load(network, new LfNetworkLoaderImpl(), new LfNetworkParameters(new FirstSlackBusSelector())); + this.acLoadFlowParameters = getAcLoadFlowParametersFromParam(); + ShortCircuitExtensions.add(network, lfNetworks, parameters.getAdditionalDataInfo()); + } + + protected AcLoadFlowParameters getAcLoadFlowParametersFromParam() { + OpenLoadFlowParameters loadflowParametersExt = OpenLoadFlowParameters.get(parameters.getLoadFlowParameters()); + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(parameters.getLoadFlowParameters(), loadflowParametersExt, parameters.getMatrixFactory(), new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP, false, false); + return acLoadFlowParameters; + } + + protected AdmittanceEquationSystem.AdmittancePeriodType getAdmittancePeriodTypeFromParam() { + AdmittanceEquationSystem.AdmittancePeriodType admittancePeriodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_TRANSIENT; + if (parameters.getPeriodType() == ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT) { + admittancePeriodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_SUB_TRANSIENT; + } else if (parameters.getPeriodType() == ShortCircuitEngineParameters.PeriodType.STEADY_STATE) { + admittancePeriodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_STEADY_STATE; + } + return admittancePeriodType; + } + + protected AdmittanceEquationSystem.AdmittanceVoltageProfileType getAdmittanceVoltageProfileTypeFromParam() { + AdmittanceEquationSystem.AdmittanceVoltageProfileType admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.NOMINAL; + if (parameters.getVoltageProfileType() == ShortCircuitEngineParameters.VoltageProfileType.CALCULATED) { + admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED; + } + return admittanceVoltageProfileType; + } + + protected void buildSystematicList(ShortCircuitFault.ShortCircuitType type) { + List scfSystematic = new ArrayList<>(); + parameters.setVoltageUpdate(false); + for (Bus bus : network.getBusBreakerView().getBuses()) { + ShortCircuitFault sc = new ShortCircuitFault(bus.getId(), false, bus.getId(), 0., 0., type); //TODO : check validity of voltage levels if no connexity + scfSystematic.add(sc); + } + parameters.setShortCircuitFaults(scfSystematic); + } + + protected Pair, List> buildFaultListsFromInputs() { + // We handle a pre-treatement of faults given in input: + // - filtering faults because of some inconsistencies on the bus identification + // - addition of info in each fault to ease the identification in LfNetwork of iidm info + + List faultList = new ArrayList<>(); + List biphasedFaultList = new ArrayList<>(); + Map> tmpListBus1 = new HashMap<>(); + for (ShortCircuitFault scfe : parameters.getShortCircuitFaults()) { + String busName = scfe.getBusLocation(); + String bus2Name = scfe.getBusLocationBiPhased(); + + if (bus2Name.isEmpty()) { // TODO : adapt + // TODO : put a condition that it is an unbalanced shortCircuit + if (scfe.getType() == ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT) { + throw new IllegalArgumentException(" short circuit fault : " + busName + " must have a second voltage level defined because it is a common support fault"); + } + Pair branchFaultInfo = buildFaultBranchFromBusId(busName, network); // creates additional info for fault, identifying location through iidm branches instead of iidm busses to easily get lf busses + scfe.setIidmBusInfo(branchFaultInfo); // the short circuit fault info is now enriched with the couple iidmBranchId + iidmBranchSide and not only the iidm bus name in order to be able to identify the busses in the LfNetwork + faultList.add(scfe); + + } else { + if (scfe.getType() != ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT) { + throw new IllegalArgumentException(" short circuit fault : " + busName + " has a second bus defined : " + bus2Name + " but is not a common support fault"); + } + + // Step 1 : get info at bus 1 initialization of bus 2 list + if (!tmpListBus1.containsKey(busName)) { + Pair branchBus1FaultInfo = buildFaultBranchFromBusId(busName, network); + tmpListBus1.put(busName, branchBus1FaultInfo); + } + + // step 2 : get info at bus 2 + Pair branchBus2FaultInfo = buildFaultBranchFromBusId(bus2Name, network); + Pair branchBus1FaultInfo = tmpListBus1.get(busName); + + scfe.setIidmBusInfo(branchBus1FaultInfo); + scfe.setIidmBus2Info(branchBus2FaultInfo); + biphasedFaultList.add(scfe); + } + } + + return new Pair<>(faultList, biphasedFaultList); + } + + protected static Pair buildFaultBranchFromBusId(String busId, Network tmpNetwork) { + Pair branchFaultInfo = buildFaultDipoleFromBusId(busId, tmpNetwork); + if (branchFaultInfo.getKey().equals("")) { + // Bus not found in branches, try three windings transformers + branchFaultInfo = buildFaultT3WbranchFromBusId(busId, tmpNetwork); + } + return branchFaultInfo; + } + + protected static Pair buildFaultDipoleFromBusId(String busId, Network tmpNetwork) { + // TODO : improve with direct correspondence between iidm busses and lfBusses when available, because this loop is not very efficient + Bus bus = tmpNetwork.getBusBreakerView().getBus(busId); + String branchId = ""; + int branchSide = 0; + boolean isFound = false; + for (Branch branch : tmpNetwork.getBranches()) { + Bus bus1 = branch.getTerminal1().getBusBreakerView().getBus(); + Bus bus2 = branch.getTerminal2().getBusBreakerView().getBus(); + if (bus == bus1) { + branchId = branch.getId(); + branchSide = 1; + isFound = true; + break; + } else if (bus == bus2) { + branchId = branch.getId(); + branchSide = 2; + isFound = true; + break; + } + } + + if (!isFound) { + System.out.println(" input CC Bus " + busId + " could not be associated with a bipole"); + } + + return new Pair<>(branchId, branchSide); + } + + protected static Pair buildFaultT3WbranchFromBusId(String busId, Network tmpNetwork) { + // TODO : improve with direct correspondence between iidm busses and lfBusses when available, because this loop is not very efficient + Bus bus = tmpNetwork.getBusBreakerView().getBus(busId); + String branchId = ""; + int legNum = 0; + boolean isFound = false; + for (ThreeWindingsTransformer t3w : tmpNetwork.getThreeWindingsTransformers()) { + Bus bus1 = t3w.getLeg1().getTerminal().getBusBreakerView().getBus(); + Bus bus2 = t3w.getLeg2().getTerminal().getBusBreakerView().getBus(); + Bus bus3 = t3w.getLeg3().getTerminal().getBusBreakerView().getBus(); + + if (bus == bus1) { + branchId = t3w.getId(); + legNum = 1; + isFound = true; + break; + } else if (bus == bus2) { + branchId = t3w.getId(); + legNum = 2; + isFound = true; + break; + } else if (bus == bus3) { + branchId = t3w.getId(); + legNum = 3; + isFound = true; + break; + } + } + + if (!isFound) { + System.out.println(" Bus " + busId + " could not be associated with a tripole"); + } + + return new Pair<>(branchId, legNum); + } + + public abstract void run(); + +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1A2Calculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1A2Calculator.java new file mode 100644 index 00000000..2c940f1b --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1A2Calculator.java @@ -0,0 +1,190 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class BiphasedC1A2Calculator extends BiphasedCommonSupportShortCircuitCalculator { + + public BiphasedC1A2Calculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf, + double v2dxInit, double v2dyInit, + double ro12, double xo12, double ro22, double xo22, double ro21, double xo21, + double rd12, double xd12, double rd22, double xd22, double rd21, double xd21) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf, v2dxInit, v2dyInit, ro12, xo12, ro22, xo22, ro21, xo21, rd12, xd12, rd22, xd22, rd21, xd21); + + //Description of the fault (short circuit between c1 and a2) : + // a1 ---------------x------------------ by definition : Ia1 = Ib1 = Ib2 = Ic2 = 0 + // b1 ---------------x------------------ Ic1 = -Ia2 + // c1 ---------------+------------------ Vc1 = Zf * Ic1 + Va2 + // | + // Zf + // | + // a2 ---------------+------------------ + // b2 ---------------x------------------ + // c2 ---------------x------------------ + + //Problem to solve: + // Given the equalities above, we need to solve for V and I the following problem: + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = -tM * inv(Yd) * M * [ Idf ] + tM * [ V(init) ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + // Where: + // - Yo and Yd are the full network admittance matrices (zero and direct fortescue components) + // - M is the extraction matrix to get the subvectors V1, V2, I1 and I2 of the full network vectors [V] and [I] + // + // Step 1: express the currents in fortescue basis : + // + // [ I1o ] [ 1 1 1 ] [ 0 ] [ 1 ] + // [ I1d ] = 1/3 * [ 1 a a²] * [ 0 ] = 1/3 * Ic1 * [ a²] + // [ I1i ] [ 1 a² a ] [ Ic1] [ a ] + // + // [ I2o ] [ 1 ] + // [ I2d ] = -1/3 *Ic1 * [ 1 ] + // [ I2i ] [ 1 ] + // + // Step 2: get Ic1 : + // Given: Vc1 = V1o + a * V1d + a² * V1i and Va2 = V2o + V2d + V2i + // and replacing them in: Vc1 = Zf * Ic1 + Va2 + // we get + // a * V1d(init) - V2d(init) + // Ic1 = ----------------------------------------------------------------------------------------------------------------- + // Zf + 1/3*(Zd_11 - a*Zd_12 + Zd_22-a²*Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - a²*Zi_12 + Zi_11 - a*Zi_21) + // + // + // Where Zo_ij and Zd_ij are complex impedance matrix elements at nodes 1 and 2: + // Zo = tM * inv(Yo) * M and Zd = tM * inv(Yd) * M + // + // Then, for example, we have with complex variables : + // [ V1of ] [ I1of ] [ Zo_11 Zo_12 ] [ I1of ] + // [ V2of ] = -Zof * [ I2of ] = - [ Zo_21 Zo_22 ] * [ I2of ] + // + // Step 3: compute the short circuit voltages: + // From computed Ic1 we get complex values : I1o, I1d, I1i, I2o, I2d, I2i using step 1 formulas expressed with Ic1 + // Then compute the voltages from current values + + computeZt(); // computes rt and xt + computeIc(); // computes Ic of common support from rt and xt + computeCurrents(); // computes Io, Id, Ii for both supports from computed Ic + computeVoltages(); // computes Vo, Vd, Vi from computed currents and computed terms of the impedance matrix + + } + + @Override + public void computeIc() { + + // Compute Ic1 : + // Complex expression : + // a * V1d(init) - V2d(init) + // Ic1 = ------------------------------------------------------------------------------------------------------------------ + // Zf + 1/3*(Zd_11 - a*Zd_12 + Zd_22-a²*Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - a²*Zi_12 + Zi_11 - a*Zi_21) + // + // + // The equivalent cartesian matrix expression of Ic : + // 1 + // [ic1x] = ------------------------ * [ rt xt ] * ( [ -1/2 -sqrt(3)/2 ] * [vd1x] - [vd2x] ) + // [ic1y] (rt² + xt²) [-xt rt ] ( [ sqrt(3)/2 -1/2 ] [vd1y] [vd2y] ) + // + + //compute the numerator matrix = a * V1d(init) - V2d(init) + Matrix ma = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1.0, mf); + + Matrix mVd1Init = mf.create(2, 1, 2); + mVd1Init.add(0, 0, initVx); + mVd1Init.add(1, 0, initVy); + + Matrix maVd = ma.times(mVd1Init); + + maVd.add(0, 0, -v2dxInit); + maVd.add(1, 0, -v2dyInit); + + // get Ic by multiplying the numerator to inv(Zt) + Matrix invZt = getInvZt(rt, xt, mf); + mIc = invZt.times(maVd); + } + + @Override + public void computeZt() { + // Zf + 1/3*(Zd_11 - a*Zd_12 + Zd_22 -a²*Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - a²*Zi_12 + Zi_11 - a*Zi_21) + Matrix a2 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A2, 1.0, mf); + Matrix a = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1.0, mf); + Matrix minusId = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, -1.0, mf); + Matrix idDiv3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, 1. / 3., mf); + + // td12 = - a*Zd_12 + Matrix tmpd12 = a.times(zdf12); + Matrix td12 = minusId.times(tmpd12); + + // td21 = -a²*Zd_21 + Matrix tmpd21 = a2.times(zdf21); + Matrix td21 = minusId.times(tmpd21); + + // to21 = -zof21 and to12 = -zof12 + Matrix to21 = minusId.times(zof21); + Matrix to12 = minusId.times(zof12); + + // ti12 = -a²*Zi_12 + Matrix tmpi12 = a2.times(zif12); + Matrix ti12 = minusId.times(tmpi12); + + // ti21 = - a*Zi_21 + Matrix tmpi21 = a.times(zif21); + Matrix ti21 = minusId.times(tmpi21); + + DenseMatrix zt = addMatrices22(zdf11.toDense(), td12.toDense(), mf); + zt = addMatrices22(zt, zdf22.toDense(), mf); + zt = addMatrices22(zt, td21.toDense(), mf); + zt = addMatrices22(zt, zof11.toDense(), mf); + zt = addMatrices22(zt, to21.toDense(), mf); + zt = addMatrices22(zt, zof22.toDense(), mf); + zt = addMatrices22(zt, to12.toDense(), mf); + zt = addMatrices22(zt, zif22.toDense(), mf); + zt = addMatrices22(zt, ti12.toDense(), mf); + zt = addMatrices22(zt, zif11.toDense(), mf); + zt = addMatrices22(zt, ti21.toDense(), mf); + + Matrix tmpzt = idDiv3.times(zt); + + Matrix zf = getZ(rg, xg, mf); + + zt = addMatrices22(tmpzt.toDense(), zf.toDense(), mf); + + rt = zt.get(0, 0); + xt = zt.get(1, 0); + } + + @Override + public void computeCurrents() { + // step 3: + // get the currents vectors + // [ I1o ] [ 1 ] + // [ I1d ] = 1/3 * Ic1 * [ a²] + // [ I1i ] [ a ] + // + // [ I2o ] [ 1 ] + // [ I2d ] = -1/3 *Ic1 * [ 1 ] + // [ I2i ] [ 1 ] + + Matrix mI3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, 1. / 3, mf); + Matrix ma2Div3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A2, 1. / 3, mf); + Matrix maDiv3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1. / 3, mf); + Matrix mMinusI = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, -1., mf); + + mIo = mI3.times(mIc); + mId = ma2Div3.times(mIc); + mIi = maDiv3.times(mIc); + + mI2o = mMinusI.times(mIo); + mI2d = mI2o; + mI2i = mI2o; + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1B2Calculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1B2Calculator.java new file mode 100644 index 00000000..66a1349d --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1B2Calculator.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class BiphasedC1B2Calculator extends BiphasedCommonSupportShortCircuitCalculator { + + public BiphasedC1B2Calculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf, + double v2dxInit, double v2dyInit, + double ro12, double xo12, double ro22, double xo22, double ro21, double xo21, + double rd12, double xd12, double rd22, double xd22, double rd21, double xd21) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf, v2dxInit, v2dyInit, ro12, xo12, ro22, xo22, ro21, xo21, rd12, xd12, rd22, xd22, rd21, xd21); + + //Description of the fault (short circuit between c1 and a2) : + // a1 ---------------x------------------ by definition : Ia1 = Ib1 = Ia2 = Ic2 = 0 + // b1 ---------------x------------------ Ic1 = -Ib2 + // c1 ---------------+------------------ Vc1 = Zf * Ic1 + Vb2 + // | + // Zf + // | + // b2 ---------------+------------------ + // c2 ---------------x------------------ + // a2 ---------------x------------------ + + //Problem to solve: + // Given the equalities above, we need to solve for V and I the following problem: + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = tM * [ V(init) ] - tM * inv(Yd) * M * [ Idf ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + // Where: + // - Yo and Yd are the full network admittance matrices (zero and direct fortescue components) + // - M is the extraction matrix to get the subvectors V1, V2, I1 and I2 of the full network vectors [V] and [I] + // + // Step 1: express the currents in fortescue basis : + // + // [ I1o ] [ 1 1 1 ] [ 0 ] [ 1 ] + // [ I1d ] = 1/3 * [ 1 a a²] * [ 0 ] = 1/3 * Ic1 * [ a²] + // [ I1i ] [ 1 a² a ] [ Ic1] [ a ] + // + // [ I2o ] [ 1 ] + // [ I2d ] = -1/3 *Ic1 * [ a ] + // [ I2i ] [ a²] + // + // Step 2: get Ic1 : + // Given: Vc1 = V1o + a * V1d + a² * V1i and Vb2 = V2o + a² * V2d + a * V2i + // and replacing them in: Vc1 = Zf * Ic1 + Vb2 + // we get + // a * V1d(init) - a² * V2d(init) + // Ic1 = -------------------------------------------------------------------------------------------------------------------- + // Zf + 1/3*(Zd_11 - a²*Zd_12 + Zd_22 - a*Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - a*Zi_12 + Zi_11 - a²*Zi_21) + // + // Where Zof_ij and Zdf_ij are complex impedance matrix elements at nodes 1 and 2: + // Zof = tM * inv(Yo) * M and Zdf = tM * inv(Yd) * M + // + // Then, for example, we have with complex variables : + // [ V1of ] [ I1of ] [ Zof_11 Zof_12 ] [ I1of ] + // [ V2of ] = -Zof * [ I2of ] = - [ Zof_21 Zof_22 ] * [ I2of ] + // + // Step 3: compute the short circuit voltages: + // From computed Ic1 we get complex values : I1o, I1d, I1i, I2o, I2d, I2i using step 1 formulas expressed with Ic1 + // Then compute the voltages from current values + + computeZt(); // computes rt and xt + computeIc(); // computes Ic of common support from rt and xt + computeCurrents(); // computes Io, Id, Ii for both supports from computed Ic + computeVoltages(); // computes Vo, Vd, Vi from computed currents and computed terms of the impedance matrix + + } + + @Override + public void computeIc() { + + // Compute Ic1 : + // Complex expression : + // a * V1d(init) - a² * V2d(init) + // Ic1 = ------------------------------------------------------------------------------------------------------------------- + // Zf + 1/3*(Zd_11 - a²*Zd_12 + Zd_22 - a*Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - a*Zi_12 + Zi_11 - a²*Zi_21) + // + // + // The equivalent cartesian matrix expression of Ic : + // 1 + // [ic1x] = ------------------------ * [ rt xt ] * ( [ -1/2 -sqrt(3)/2 ] * [vd1x] - [ 1/2 -sqrt(3)/2 ] * [vd2x] ) + // [ic1y] (rt² + xt²) [-xt rt ] ( [ sqrt(3)/2 -1/2 ] [vd1y] [ sqrt(3)/2 1/2 ] [vd2y] ) + // + + //compute the numerator matrix = a * V1d(init) - a² * V2d(init) + Matrix ma = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1.0, mf); + + Matrix mVd2Init = mf.create(2, 1, 2); + mVd2Init.add(0, 0, -v2dxInit); + mVd2Init.add(1, 0, -v2dyInit); + Matrix maVd2 = ma.times(mVd2Init); + + maVd2.add(0, 0, initVx); + maVd2.add(1, 0, initVy); + + DenseMatrix numerator = ma.times(maVd2).toDense(); + + // get Ic by multiplying the numerator to inv(Zt) + Matrix invZt = getInvZt(rt, xt, mf); + mIc = invZt.times(numerator); + } + + @Override + public void computeZt() { + + // Zf + 1/3*(Zd_11 - a²*Zd_12 + Zd_22 - a*Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - a*Zi_12 + Zi_11 - a²*Zi_21) + Matrix a2 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A2, 1.0, mf); + Matrix a = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1.0, mf); + Matrix minusId = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, -1.0, mf); + Matrix idDiv3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, 1. / 3., mf); + + // td12 = - a²*Zd_12 + Matrix tmpd12 = a2.times(zdf12); + Matrix td12 = minusId.times(tmpd12); + + // td21 = - a*Zd_21 + Matrix tmpd21 = a.times(zdf21); + Matrix td21 = minusId.times(tmpd21); + + // to21 = -zof21 and to12 = -zof12 + Matrix to21 = minusId.times(zof21); + Matrix to12 = minusId.times(zof12); + + // ti12 = - a*Zi_12 + Matrix tmpi12 = a.times(zif12); + Matrix ti12 = minusId.times(tmpi12); + + // ti21 = - a²*Zi_21 + Matrix tmpi21 = a2.times(zif21); + Matrix ti21 = minusId.times(tmpi21); + + DenseMatrix zt = addMatrices22(zdf11.toDense(), td12.toDense(), mf); + zt = addMatrices22(zt, zdf22.toDense(), mf); + zt = addMatrices22(zt, td21.toDense(), mf); + zt = addMatrices22(zt, zof11.toDense(), mf); + zt = addMatrices22(zt, to21.toDense(), mf); + zt = addMatrices22(zt, zof22.toDense(), mf); + zt = addMatrices22(zt, to12.toDense(), mf); + zt = addMatrices22(zt, zif22.toDense(), mf); + zt = addMatrices22(zt, ti12.toDense(), mf); + zt = addMatrices22(zt, zif11.toDense(), mf); + zt = addMatrices22(zt, ti21.toDense(), mf); + + Matrix tmpzt = idDiv3.times(zt); + + Matrix zf = getZ(rg, xg, mf); + + zt = addMatrices22(tmpzt.toDense(), zf.toDense(), mf); + + rt = zt.get(0, 0); + xt = zt.get(1, 0); + + } + + @Override + public void computeCurrents() { + // step 3: + // get the currents vectors + // [ I1o ] [ 1 ] + // [ I1d ] = 1/3 * Ic1 * [ a²] + // [ I1i ] [ a ] + // + // [ I2o ] [ 1 ] + // [ I2d ] = -1/3 *Ic1 * [ a ] + // [ I2i ] [ a²] + + Matrix mI3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, 1. / 3, mf); + Matrix ma2Div3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A2, 1. / 3, mf); + Matrix maDiv3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1. / 3, mf); + Matrix mMinusI = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, -1., mf); + + mIo = mI3.times(mIc); + mId = ma2Div3.times(mIc); + mIi = maDiv3.times(mIc); + + mI2o = mMinusI.times(mIo); + mI2d = maDiv3.times(mIo); + mI2i = ma2Div3.times(mIo); + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1C2Calculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1C2Calculator.java new file mode 100644 index 00000000..19bf728c --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedC1C2Calculator.java @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class BiphasedC1C2Calculator extends BiphasedCommonSupportShortCircuitCalculator { + + public BiphasedC1C2Calculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf, + double v2dxInit, double v2dyInit, + double ro12, double xo12, double ro22, double xo22, double ro21, double xo21, + double rd12, double xd12, double rd22, double xd22, double rd21, double xd21) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf, v2dxInit, v2dyInit, ro12, xo12, ro22, xo22, ro21, xo21, rd12, xd12, rd22, xd22, rd21, xd21); + +//Description of the fault (short circuit between c1 and a2) : + // a1 ---------------x------------------ by definition : Ia1 = Ib1 = Ia2 = Ib2 = 0 + // b1 ---------------x------------------ Ic1 = -Ic2 + // c1 ---------------+------------------ Vc1 = Zf * Ic1 + Vc2 + // | + // Zf + // | + // c2 ---------------+------------------ + // a2 ---------------x------------------ + // b2 ---------------x------------------ + + //Problem to solve: + // Given the equalities above, we need to solve for V and I the following problem: + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = tM * [ V(init) ] - tM * inv(Yd) * M * [ Idf ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + // Where: + // - Yo and Yd are the full network admittance matrices (zero and direct fortescue components) + // - M is the extraction matrix to get the subvectors V1, V2, I1 and I2 of the full network vectors [V] and [I] + // + // Step 1: express the currents in fortescue basis : + // + // [ I1o ] [ 1 1 1 ] [ 0 ] [ 1 ] + // [ I1d ] = 1/3 * [ 1 a a²] * [ 0 ] = 1/3 * Ic1 * [ a²] + // [ I1i ] [ 1 a² a ] [ Ic1] [ a ] + // + // [ I2o ] [ 1 ] + // [ I2d ] = -1/3 *Ic1 * [ a²] + // [ I2i ] [ a ] + // + // Step 2: get Ic1 : + // Given: Vc1 = V1o + a * V1d + a² * V1i and Vc2 = V2o + a * V2d + a² * V2i + // and replacing them in: Vc1 = Zf * Ic1 + Vb2 + // we get + // a * (V1d(init) - V2d(init)) + // Ic1 = --------------------------------------------------------------------------------------------------------- + // Zf + 1/3*(Zd_11 - Zd_12 + Zd_22 - Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - Zi_12 + Zi_11 - Zi_21) + // + // Where Zof_ij and Zdf_ij are complex impedance matrix elements at nodes 1 and 2: + // Zof = tM * inv(Yo) * M and Zdf = tM * inv(Yd) * M + // + // Then, for example, we have with complex variables : + // [ V1of ] [ I1of ] [ Zof_11 Zof_12 ] [ I1of ] + // [ V2of ] = -Zof * [ I2of ] = - [ Zof_21 Zof_22 ] * [ I2of ] + // + // Step 3: compute the short circuit voltages: + // From computed Ic1 we get complex values : I1o, I1d, I1i, I2o, I2d, I2i using step 1 formulas expressed with Ic1 + // Then compute the voltages from current values + + computeZt(); // computes rt and xt + computeIc(); // computes Ic of common support from rt and xt + computeCurrents(); // computes Io, Id, Ii for both supports from computed Ic + computeVoltages(); // computes Vo, Vd, Vi from computed currents and computed terms of the impedance matrix + + } + + @Override + public void computeIc() { + + // Compute Ic1 : + // Complex expression : + // a * (V1d(init) - V2d(init)) + // Ic1 = --------------------------------------------------------------------------------------------------------- + // Zf + 1/3*(Zd_11 - Zd_12 + Zd_22 - Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - Zi_12 + Zi_11 - Zi_21) + // + // + // The equivalent cartesian matrix expression of Ic : + // 1 + // [ic1x] = ------------------------ * [ rt xt ] * [ -1/2 -sqrt(3)/2 ] * ( [vd1x] - [vd2x] ) + // [ic1y] (rt² + xt²) [-xt rt ] [ sqrt(3)/2 -1/2 ] ( [vd1y] [vd2y] ) + // + + //compute the numerator matrix = a * (V1d(init) - V2d(init)) + Matrix ma = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1.0, mf); + + Matrix mVdInit = mf.create(2, 1, 2); + mVdInit.add(0, 0, initVx - v2dxInit); + mVdInit.add(1, 0, initVy - v2dyInit); + + Matrix numerator = ma.times(mVdInit); + + // get Ic by multiplying the numerator to inv(Zt) + Matrix invZt = getInvZt(rt, xt, mf); + mIc = invZt.times(numerator); + } + + @Override + public void computeZt() { + // Zf + 1/3*(Zd_11 - Zd_12 + Zd_22 - Zd_21 + Zo_11 - Zo_21 + Zo_22 - Zo_12 + Zi_22 - Zi_12 + Zi_11 - Zi_21) + Matrix mId = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, -1.0, mf); + Matrix idDiv3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, 1. / 3., mf); + + Matrix td12 = mId.times(zdf12); + Matrix td21 = mId.times(zdf21); + + Matrix to21 = mId.times(zof21); + Matrix to12 = mId.times(zof12); + + Matrix ti12 = mId.times(zif12); + Matrix ti21 = mId.times(zif21); + + DenseMatrix zt = addMatrices22(zdf11.toDense(), td12.toDense(), mf); + zt = addMatrices22(zt, zdf22.toDense(), mf); + zt = addMatrices22(zt, td21.toDense(), mf); + zt = addMatrices22(zt, zof11.toDense(), mf); + zt = addMatrices22(zt, to21.toDense(), mf); + zt = addMatrices22(zt, zof22.toDense(), mf); + zt = addMatrices22(zt, to12.toDense(), mf); + zt = addMatrices22(zt, zif22.toDense(), mf); + zt = addMatrices22(zt, ti12.toDense(), mf); + zt = addMatrices22(zt, zif11.toDense(), mf); + zt = addMatrices22(zt, ti21.toDense(), mf); + + Matrix tmpzt = idDiv3.times(zt); + + Matrix zf = getZ(rg, xg, mf); + + zt = addMatrices22(tmpzt.toDense(), zf.toDense(), mf); + + rt = zt.get(0, 0); + xt = zt.get(1, 0); + } + + @Override + public void computeCurrents() { + // step 3: + // get the currents vectors + // [ I1o ] [ 1 ] + // [ I1d ] = 1/3 * Ic1 * [ a²] + // [ I1i ] [ a ] + // + // [ I2o ] [ 1 ] + // [ I2d ] = -1/3 *Ic1 * [ a²] + // [ I2i ] [ a ] + + Matrix mI3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, 1. / 3, mf); + Matrix ma2Div3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A2, 1. / 3, mf); + Matrix maDiv3 = getMatrixByType(AbstractShortCircuitCalculator.BlocType.A, 1. / 3, mf); + Matrix mMinusI = getMatrixByType(AbstractShortCircuitCalculator.BlocType.Id, -1., mf); + + mIo = mI3.times(mIc); + mId = ma2Div3.times(mIc); + mIi = maDiv3.times(mIc); + + mI2o = mMinusI.times(mIo); + mI2d = mMinusI.times(mId); + mI2i = mMinusI.times(mIi); + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedCommonSupportShortCircuitCalculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedCommonSupportShortCircuitCalculator.java new file mode 100644 index 00000000..27cbf368 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedCommonSupportShortCircuitCalculator.java @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class BiphasedCommonSupportShortCircuitCalculator extends AbstractShortCircuitCalculator { + + protected double ro12; + protected double xo12; + protected double ro22; + protected double xo22; + protected double ro21; + protected double xo21; + + protected double rd12; + protected double xd12; + protected double rd22; + protected double xd22; + protected double rd21; + protected double xd21; + + protected double v2dxInit; + protected double v2dyInit; + + protected Matrix zdf11; + protected Matrix zdf12; + protected Matrix zdf21; + protected Matrix zdf22; + protected Matrix zof11; + protected Matrix zof12; + protected Matrix zof21; + protected Matrix zof22; + protected Matrix zif11; + protected Matrix zif12; + protected Matrix zif21; + protected Matrix zif22; + + protected Matrix zdf; + protected Matrix zof; + + protected Matrix mI2o; // current from bus 2 + protected Matrix mI2d; + protected Matrix mI2i; + + protected Matrix mVo; + protected Matrix mVd; + protected Matrix mVi; + + protected double rt; // values of total impedance used to get Ic + protected double xt; + + protected Matrix mIc; // short circuit phase C1 current circulating from common support 1 to 2 + + public BiphasedCommonSupportShortCircuitCalculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf, + double v2dxInit, double v2dyInit, + double ro12, double xo12, double ro22, double xo22, double ro21, double xo21, + double rd12, double xd12, double rd22, double xd22, double rd21, double xd21) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf); + this.ro12 = ro12; + this.ro21 = ro21; + this.ro22 = ro22; + this.xo12 = xo12; + this.xo21 = xo21; + this.xo22 = xo22; + this.rd12 = rd12; + this.rd21 = rd21; + this.rd22 = rd22; + this.xd12 = xd12; + this.xd21 = xd21; + this.xd22 = xd22; + this.v2dxInit = v2dxInit; + this.v2dyInit = v2dyInit; + + buildZxf(); // build all remaining matrix elements from inputs + + } + + public void computeZt() { } + + public void computeIc() { } + + public void computeCurrents() { } + + public void computeVoltages() { + // Function using no input args + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = -tM * inv(Yd) * M * [ Idf ] + tM * [ V(init) ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + + //get the voltage vectors + // Vo : + // [v1ox] [ rof_11 -xof_11 rof_12 -xof_12 ] [ i1ox ] + // [v1oy] = -1 * [ xof_11 rof_11 xof_12 rof_12 ] * [ i1oy ] + // [v2ox] [ rof_21 -xof_21 rof_22 -xof_22 ] [ i2ox ] + // [v2oy] [ xof_21 rof_21 xof_22 rof_22 ] [ i2oy ] + Matrix minusIo = mf.create(4, 1, 4); + minusIo.add(0, 0, -getmIo().toDense().get(0, 0)); + minusIo.add(1, 0, -getmIo().toDense().get(1, 0)); + minusIo.add(2, 0, -getmI2o().toDense().get(0, 0)); + minusIo.add(3, 0, -getmI2o().toDense().get(1, 0)); + + mVo = zof.times(minusIo); + + // Vd : + // [v1dx] [ rdf_11 -xdf_11 rdf_12 -xdf_12 ] [ i1ox ] [v1dx(init)] + // [v1dy] = -1 * [ xdf_11 rdf_11 xdf_12 rdf_12 ] * [ i1oy ] + [v1dy(init)] + // [v2dx] [ rdf_21 -xdf_21 rdf_22 -xdf_22 ] [ i2ox ] [v2dx(init)] + // [v2dy] [ xdf_21 rdf_21 xdf_22 rdf_22 ] [ i2oy ] [v2dy(init)] + Matrix minusId = mf.create(4, 1, 4); + minusId.add(0, 0, -getmId().toDense().get(0, 0)); + minusId.add(1, 0, -getmId().toDense().get(1, 0)); + minusId.add(2, 0, -getmI2d().toDense().get(0, 0)); + minusId.add(3, 0, -getmI2d().toDense().get(1, 0)); + + mVd = zdf.times(minusId); + // TODO: mVd contains the delta values, not Vinit, this is why lines below are commented and will be removed + //mVd.add(0, 0, initVx); + //mVd.add(1, 0, initVy); + //mVd.add(2, 0, v2dxInit); + //mVd.add(3, 0, v2dyInit); + + // Vi : + // [v1ix] [ rdf_11 -xdf_11 rdf_12 -xdf_12 ] [ i1dx ] + // [v1iy] = -1 * [ xdf_11 rdf_11 xdf_12 rdf_12 ] * [ i1dy ] + // [v2ix] [ rdf_21 -xdf_21 rdf_22 -xdf_22 ] [ i2dx ] + // [v2iy] [ xdf_21 rdf_21 xdf_22 rdf_22 ] [ i2dy ] + + Matrix minusIi = mf.create(4, 1, 4); + minusIi.add(0, 0, -getmIi().toDense().get(0, 0)); + minusIi.add(1, 0, -getmIi().toDense().get(1, 0)); + minusIi.add(2, 0, -getmI2i().toDense().get(0, 0)); + minusIi.add(3, 0, -getmI2i().toDense().get(1, 0)); + + mVi = zdf.times(minusIi); + + } + + public void buildZxf() { + + zdf11 = mf.create(2, 2, 2); + zdf11.add(0, 0, rdf); + zdf11.add(0, 1, -xdf); + zdf11.add(1, 0, xdf); + zdf11.add(1, 1, rdf); + + zdf12 = mf.create(2, 2, 2); + zdf12.add(0, 0, rd12); + zdf12.add(0, 1, -xd12); + zdf12.add(1, 0, xd12); + zdf12.add(1, 1, rd12); + + zdf21 = mf.create(2, 2, 2); + zdf21.add(0, 0, rd21); + zdf21.add(0, 1, -xd21); + zdf21.add(1, 0, xd21); + zdf21.add(1, 1, rd21); + + zdf22 = mf.create(2, 2, 2); + zdf22.add(0, 0, rd22); + zdf22.add(0, 1, -xd22); + zdf22.add(1, 0, xd22); + zdf22.add(1, 1, rd22); + + zof11 = mf.create(2, 2, 2); + zof11.add(0, 0, rof); + zof11.add(0, 1, -xof); + zof11.add(1, 0, xof); + zof11.add(1, 1, rof); + + zof12 = mf.create(2, 2, 2); + zof12.add(0, 0, ro12); + zof12.add(0, 1, -xo12); + zof12.add(1, 0, xo12); + zof12.add(1, 1, ro12); + + zof21 = mf.create(2, 2, 2); + zof21.add(0, 0, ro21); + zof21.add(0, 1, -xo21); + zof21.add(1, 0, xo21); + zof21.add(1, 1, ro21); + + zof22 = mf.create(2, 2, 2); + zof22.add(0, 0, ro22); + zof22.add(0, 1, -xo22); + zof22.add(1, 0, xo22); + zof22.add(1, 1, ro22); + + zif11 = mf.create(2, 2, 2); + zif11.add(0, 0, rdf); + zif11.add(0, 1, -xdf); + zif11.add(1, 0, xdf); + zif11.add(1, 1, rdf); + + zif12 = mf.create(2, 2, 2); + zif12.add(0, 0, rd12); + zif12.add(0, 1, -xd12); + zif12.add(1, 0, xd12); + zif12.add(1, 1, rd12); + + zif21 = mf.create(2, 2, 2); + zif21.add(0, 0, rd21); + zif21.add(0, 1, -xd21); + zif21.add(1, 0, xd21); + zif21.add(1, 1, rd21); + + zif22 = mf.create(2, 2, 2); + zif22.add(0, 0, rd22); + zif22.add(0, 1, -xd22); + zif22.add(1, 0, xd22); + zif22.add(1, 1, rd22); + + //Matrix zof + // [ rof_11 -xof_11 rof_12 -xof_12 ] + // [ xof_11 rof_11 xof_12 rof_12 ] + // [ rof_21 -xof_21 rof_22 -xof_22 ] + // [ xof_21 rof_21 xof_22 rof_22 ] + zof = mf.create(4, 4, 16); + zof.add(0, 0, rof); + zof.add(0, 1, -xof); + zof.add(1, 0, xof); + zof.add(1, 1, rof); + + zof.add(0, 2, ro12); + zof.add(0, 3, -xo12); + zof.add(1, 2, xo12); + zof.add(1, 3, ro12); + + zof.add(2, 0, ro21); + zof.add(2, 1, -xo21); + zof.add(3, 0, xo21); + zof.add(3, 1, ro21); + + zof.add(2, 2, ro22); + zof.add(2, 3, -xo22); + zof.add(3, 2, xo22); + zof.add(3, 3, ro22); + + //Matrix zdf + zdf = mf.create(4, 4, 16); + zdf.add(0, 0, rdf); + zdf.add(0, 1, -xdf); + zdf.add(1, 0, xdf); + zdf.add(1, 1, rdf); + + zdf.add(0, 2, rd12); + zdf.add(0, 3, -xd12); + zdf.add(1, 2, xd12); + zdf.add(1, 3, rd12); + + zdf.add(2, 0, rd21); + zdf.add(2, 1, -xd21); + zdf.add(3, 0, xd21); + zdf.add(3, 1, rd21); + + zdf.add(2, 2, rd22); + zdf.add(2, 3, -xd22); + zdf.add(3, 2, xd22); + zdf.add(3, 3, rd22); + + } + + public Matrix getmI2d() { + return mI2d; + } + + public Matrix getmI2i() { + return mI2i; + } + + public Matrix getmI2o() { + return mI2o; + } + + public Matrix getmVd() { + return mVd; + } + + public Matrix getmVi() { + return mVi; + } + + public Matrix getmVo() { + return mVo; + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedGroundShortCircuitCalculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedGroundShortCircuitCalculator.java new file mode 100644 index 00000000..e7efb48c --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedGroundShortCircuitCalculator.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class BiphasedGroundShortCircuitCalculator extends AbstractShortCircuitCalculator { + + public BiphasedGroundShortCircuitCalculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf); + + } + + public void computeCurrents() { + //Description of the fault: + // a ---------------x------------------ by definition : Ia = 0 + // b ---------------+------------------ Vc = Vb = 0 + // | + // | + // c ---------------+------------------ + // | + // | + // ///// + // + //Problem to solve: + // Given the equalities above, we need to solve for V and I the following problem: + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = -tM * inv(Yd) * M * [ Idf ] + tM * [ V(init) ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + // Where: + // - Yo and Yd are the full network admittance matrices (zero and direct fortescue components) + // - M is the extraction matrix to get the subvectors V1, I1 of the full network vectors [V] and [I] + // + // Step 1: express the currents in fortescue basis : + // + // [ Io ] [ 1 1 1 ] [ 0 ] + // [ Id ] = 1/3 * [ 1 a a²] * [ Ib ] => Io + Id + Ii = 0 + // [ Ii ] [ 1 a² a ] [ Ic ] + // + // Vo = Vd = Vi = 1/3 * Va + // + // Step 2: + // Using the previous expressions we get : Id, Ii, Io and Va + + // Zof and Zdf are complex impedance matrix elements : + // Zof = tM * inv(Yo) * M and Zdf = tM * inv(Yd) * M + // + // Step 3: compute the short circuit voltages: + // From computed Ic1 we get complex values : I1o, I1d, I1i, I2o, I2d, I2i using step 1 formulas expressed with Ic1 + // Then compute the voltages from current values + + Matrix zof = getZ(rof, xof, mf); + Matrix zdf = getZ(rdf, xdf, mf); + + // (zof + zdf) * [Vinit] + // Id = --------------------------- + // Zdf * (Zdf + 2 * Zof) + // + // - zof * [Vinit] + // Ii = --------------------------- + // Zdf * (Zdf + 2 * Zof) + // + // - zdf * [Vinit] + // Io = --------------------------- + // Zdf * (Zdf + 2 * Zof) + // + // - 3 * zof * [Vinit] + // Va = --------------------- + // Zdf + 2 * Zof + + Matrix vdInit = mf.create(2, 1, 2); + vdInit.add(0, 0, initVx); + vdInit.add(1, 0, initVy); + + Matrix twoId = getMatrixByType(BlocType.Id, 2., mf); + Matrix minusId = getMatrixByType(BlocType.Id, -1., mf); + Matrix threeId = getMatrixByType(BlocType.Id, 3., mf); + + Matrix twoZof = twoId.times(zof); + Matrix zdf2Zof = addMatrices22(zdf.toDense(), twoZof.toDense(), mf); + Matrix zdfZof = addMatrices22(zdf.toDense(), zof.toDense(), mf); + Matrix minusZof = zof.times(minusId); + Matrix minusZdf = zdf.times(minusId); + + Matrix numId = zdfZof.times(vdInit); + Matrix numIo = minusZdf.times(vdInit); + Matrix numIi = minusZof.times(vdInit); + + Matrix demonI = zdf.times(zdf2Zof); + Matrix invDemonI = getInvZt(demonI, mf); + + mId = invDemonI.times(numId); + mIo = invDemonI.times(numIo); + mIi = invDemonI.times(numIi); + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedShortCircuitCalculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedShortCircuitCalculator.java new file mode 100644 index 00000000..7e24f43f --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/BiphasedShortCircuitCalculator.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class BiphasedShortCircuitCalculator extends AbstractShortCircuitCalculator { + + public BiphasedShortCircuitCalculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf); + + } + + public void computeCurrents() { + //Description of the fault: + // a ---------------x------------------ by definition : Ia = 0 + // b ---------------+------------------ Ib = -Ic + // | + // Zf + // | + // c ---------------+------------------ Vb = Zf * Ib + Vc + // + //Problem to solve: + // Given the equalities above, we need to solve for V and I the following problem: + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = -tM * inv(Yd) * M * [ Idf ] + tM * [ V(init) ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + // Where: + // - Yo and Yd are the full network admittance matrices (zero and direct fortescue components) + // - M is the extraction matrix to get the subvectors V1, I1 of the full network vectors [V] and [I] + // + // Step 1: express the currents in fortescue basis : + // + // [ Io ] [ 1 1 1 ] [ 0 ] [ 0 ] + // [ Id ] = 1/3 * [ 1 a a²] * [ Ib ] = 1/3 * Ib * [a - a²] + // [ Ii ] [ 1 a² a ] [-Ib ] [a²- a ] + // + // Step 2: get Ib : + // Given: Vb = Vo + a² * Vd + a * Vi and Vc = Vo + a * Vd + a² * Vi + // replacing them in Vb = Zf * Ib + Vc we get : + // + // tM * [Vinit] j * sqrt(3) * tM * [Vinit] + // Ib = ----------------------------------- = ----------------------------- + // (a-a²)/3 * (Zif + Zdf) + Zf/(a²-a) Zdf + Zif +Zf + // + // Where Zof and Zdf are complex impedance matrix elements : + // Zof = tM * inv(Yo) * M and Zdf = tM * inv(Yd) * M + // + // Step 3: compute the short circuit voltages: + // From computed Ic1 we get complex values : I1o, I1d, I1i, I2o, I2d, I2i using step 1 formulas expressed with Ic1 + // Then compute the voltages from current values + double rt = 2 * rdf + rg; + double xt = 2 * xdf + xg; + + // [Zt] = [ rt -xt ] + // [ xt rt ] + // + // Cartesian expression of Ic using matrices : + // -sqrt(3) + // [ibx] = - inv([Zt]) * [j] *sqrt(3) * [vdx] = ------------ * [ rt xt ] * [ 0 -1 ] * [vdx] + // [iby] [vdy] (rt² + xt²) [-xt rt ] [ 1 0 ] [vdy] + + Matrix vdInit = mf.create(2, 1, 2); + vdInit.add(0, 0, initVx); + vdInit.add(1, 0, initVy); + + Matrix jmSqrt3 = getMatrixByType(BlocType.J, -Math.sqrt(3), mf); + Matrix invZt = getInvZt(rt, xt, mf); + + Matrix tmpjVd = jmSqrt3.times(vdInit); + Matrix mIb = invZt.times(tmpjVd); + + // Compute the currents : + // [ Io ] [ 1 1 1 ] [ 0 ] [ 0 ] + // [ Id ] = 1/3 * [ 1 a a²] * [ Ib ] = 1/3 * Ib * [a - a²] + // [ Ii ] [ 1 a² a ] [-Ib ] [a²- a ] + + // [Io] = 0 + Matrix adiv3 = getMatrixByType(BlocType.A, 1. / 3., mf); + Matrix ma2div3 = getMatrixByType(BlocType.A2, -1. / 3, mf); + Matrix minusId = getMatrixByType(BlocType.Id, -1., mf); + Matrix aa2div3 = addMatrices22(adiv3.toDense(), ma2div3.toDense(), mf); + + mId = aa2div3.times(mIb); + mIi = minusId.times(mId); + mIo = mf.create(2, 1, 2); + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/MonophasedShortCircuitCalculator.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/MonophasedShortCircuitCalculator.java new file mode 100644 index 00000000..72d99963 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/MonophasedShortCircuitCalculator.java @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; + +/** + * @author Jean-Baptiste Heyberger + */ +public class MonophasedShortCircuitCalculator extends AbstractShortCircuitCalculator { + + public MonophasedShortCircuitCalculator(double rdf, double xdf, double rof, double xof, double rg, double xg, + double initVx, double initVy, MatrixFactory mf) { + super(rdf, xdf, rof, xof, rg, xg, initVx, initVy, mf); + + } + + public void computeCurrents() { + + //Description of the fault: + // a ---------------x------------------ by definition : Ia = Ib = 0 + // b ---------------x------------------ + // c ---------------+------------------ Vc = Zf * Ic + // | + // Zf + // | + // ///// + + //Problem to solve: + // Given the equalities above, we need to solve for V and I the following problem: + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = tM * [ V(init) ] - tM * inv(Yd) * M * [ Idf ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + // Where: + // - Yo and Yd are the full network admittance matrices (zero and direct fortescue components) + // - M is the extraction matrix to get the subvectors V1, I1 of the full network vectors [V] and [I] + // + // Step 1: express the currents in fortescue basis : + // + // [ Io ] [ 1 1 1 ] [ 0 ] [ 1 ] + // [ Id ] = 1/3 * [ 1 a a²] * [ 0 ] = 1/3 * Ic * [ a²] + // [ Ii ] [ 1 a² a ] [ Ic ] [ a ] + // + // Step 2: get Ic1 : + // Given: Vc = a² * Vo + Vd + a * Vi and replacing it in Vc = Zf * Ic we get : + // + // a * tM * [Vinit] + // Ic = ----------------------------- + // 1/3 * (Zof + 2 * Zdf) + Zf + // + // Where Zof and Zdf are complex impedance matrix elements : + // Zof = tM * inv(Yo) * M and Zdf = tM * inv(Yd) * M + // + // Step 3: compute the short circuit voltages: + // From computed Ic1 we get complex values : I1o, I1d, I1i, I2o, I2d, I2i using step 1 formulas expressed with Ic1 + // Then compute the voltages from current values + + // Complex expression of Ic : + // a * Vd(init) a * Vd(init) + // Ic = ----------------------------- = ----------------- + // 1/3 * (Zof + 2 * Zdf) + Zf Zt + + double rt = (2 * rdf + rof) / 3 + rg; + double xt = (2 * xdf + xof) / 3 + xg; + + // [Zt] = [ rt -xt ] + // [ xt rt ] + // + // Cartesian expression of Ic using matrices : + // 1 + // [icx] = inv([Zt]) * [a] * [vdx] = ------------ * [ rt xt ] * [ -1/2 -sqrt(3)/2 ] * [vdx] + // [icy] [vdy] (rt² + xt²) [-xt rt ] [ sqrt(3)/2 -1/2 ] [vdy] + + Matrix vdInit = mf.create(2, 1, 2); + vdInit.add(0, 0, initVx); + vdInit.add(1, 0, initVy); + + Matrix ma = getMatrixByType(BlocType.A, 1.0, mf); + Matrix ma2 = getMatrixByType(BlocType.A2, 1.0, mf); + + Matrix invZt = getInvZt(rt, xt, mf); + + Matrix tmpaVd = ma.times(vdInit); + Matrix mIc = invZt.times(tmpaVd); + + System.out.println(" Vinit = " + initVx + " + j(" + initVy + ")"); + System.out.println(" Ic = " + mIc.toDense().get(0, 0) + " + j(" + mIc.toDense().get(1, 0) + ")"); + System.out.println(" Zo = " + rof + " + j(" + xof + ")"); + System.out.println(" Zd = " + rdf + " + j(" + xdf + ")"); + + // TODO : check this is equivalent to : + // + // -rt*(vdxi + vdyi*sqrt(3)) + xt*(vdxi*sqrt(3)-vdyi) + //Icx = ---------------------------------------------------- + // 2 * (rt² + xt²) + + //double icx = (-rt * (v1dxInit + v1dyInit * Math.sqrt(3)) + xt*(v1dxInit * Math.sqrt(3) - v1dyInit)) / (2 * detZt); + + // + // rt*(vdxi*sqrt(3) - vdyi) + xt*(vdxi+sqrt(3)*vdyi) + //Icy = ---------------------------------------------------- + // 2 * (rt² + xt²) + + //double icy = (-rt * (v1dxInit * Math.sqrt(3) - v1dyInit) + xt * (v1dxInit + v1dyInit * Math.sqrt(3))) / (2 * detZt); + + Matrix mIdiv3 = getMatrixByType(BlocType.Id, 1. / 3., mf); + + mIo = mIdiv3.times(mIc); + mId = ma2.times(mIo); + mIi = ma.times(mIo); + + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/OpenShortCircuitProvider.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/OpenShortCircuitProvider.java new file mode 100644 index 00000000..f673a698 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/OpenShortCircuitProvider.java @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.google.auto.service.AutoService; +import com.google.common.base.Stopwatch; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.computation.ComputationManager; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.math.matrix.SparseMatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import com.powsybl.security.LimitViolation; +import com.powsybl.shortcircuit.*; +import com.powsybl.shortcircuit.interceptors.ShortCircuitAnalysisInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * @author Jean-Baptiste Heyberger + */ +@AutoService(ShortCircuitAnalysisProvider.class) +public class OpenShortCircuitProvider implements ShortCircuitAnalysisProvider { + + private static final Logger LOGGER = LoggerFactory.getLogger(OpenShortCircuitProvider.class); + + private final List interceptors = new ArrayList<>(); + + private final MatrixFactory matrixFactory; + + public OpenShortCircuitProvider() { + this(new SparseMatrixFactory()); + } + + public OpenShortCircuitProvider(MatrixFactory matrixFactory) { + this.matrixFactory = Objects.requireNonNull(matrixFactory); + } + + @Override + public void addInterceptor(ShortCircuitAnalysisInterceptor interceptor) { + interceptors.add(interceptor); + } + + @Override + public boolean removeInterceptor(ShortCircuitAnalysisInterceptor interceptor) { + return interceptors.remove(interceptor); + } + + @Override + public String getName() { + return "OpenShortCircuit"; + } + + @Override + public String getVersion() { + return "0.1"; + } + + @Override + public CompletableFuture run(Network network, List faults, ShortCircuitParameters parameters, ComputationManager computationManager, List faultParameters) { + //public CompletableFuture run(Network network, List faults, ShortCircuitParameters parameters, ComputationManager computationManager, List faultParameters, Reporter reporter) { + + Objects.requireNonNull(network); + Objects.requireNonNull(parameters); + Stopwatch stopwatch = Stopwatch.createStarted(); + + LoadFlowParameters lfParameters = new LoadFlowParameters(); + LoadFlow.Runner loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + + LoadFlowResult lfResult = loadFlowRunner.run(network, lfParameters); + + List scList = new ArrayList<>(); + + // TODO : improve the handling of faults, for now we use this map to get the correspondence between short circuit provider and internal modelling of fault + Map scFaultToFault = new HashMap<>(); + + for (Fault fault : faults) { + + if (fault.getType() == Fault.Type.BRANCH) { + LOGGER.warn("Short circuit of type BRANCH not yet supported, fault : " + fault.getId() + " is ignored"); + continue; + } + + if (fault.getFaultType() == Fault.FaultType.SINGLE_PHASE) { + LOGGER.warn(" Short circuit of type SINGLE_PHASE not yet supported, fault : " + fault.getId() + " is ignored"); + continue; + } + + // TODO : transform parallel input into a series input + if (fault.getConnectionType() == Fault.ConnectionType.PARALLEL) { + LOGGER.warn(" Short circuit connection of type PARALLEL not yet supported, fault : " + fault.getId() + " is ignored"); + continue; + } + + // TODO : see how to get lfBus from iidm Bus + String elementId = fault.getElementId(); + + double rFault = fault.getRToGround(); + double xFault = fault.getXToGround(); + Bus bus = network.getBusBreakerView().getBus(elementId); + String busId = bus.getId(); + ShortCircuitFault sc = new ShortCircuitFault(busId, true, busId, rFault, xFault, ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + scList.add(sc); + + // TODO improve: + scFaultToFault.put(sc, fault); + + } + + //Parameters that could be added in the short circuit provider API later: + // Voltage Profile + //ShortCircuitBalancedParameters.VoltageProfileType vp = ShortCircuitBalancedParameters.VoltageProfileType.CALCULATED; + ShortCircuitEngineParameters.VoltageProfileType vp = ShortCircuitEngineParameters.VoltageProfileType.NOMINAL; + + // Selective or Systematic short circuit analysis + //ShortCircuitBalancedParameters.AnalysisType at = ShortCircuitBalancedParameters.AnalysisType.SYSTEMATIC; + ShortCircuitEngineParameters.AnalysisType at = ShortCircuitEngineParameters.AnalysisType.SELECTIVE; + + // selection of the period of analysis + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.TRANSIENT; + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(); //no extra data handled in the provider, we need to enrich the input API if necessary + + LoadFlowParameters loadFlowParameters = new LoadFlowParameters(); + ShortCircuitNormCourcirc shortCircuitNormCourcirc = new ShortCircuitNormCourcirc(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, at, scList, true, vp, false, periodType, additionalDataInfo, shortCircuitNormCourcirc); + ShortCircuitBalancedEngine scbEngine = new ShortCircuitBalancedEngine(network, scbParameters); + + scbEngine.run(); + + List frs = new ArrayList<>(); + List lvs = new ArrayList<>(); + + // the results per faults might be inconsistent if many busses per voltage level + // TODO : see how this could be improved by allowing results per electrical bus on the short circuit provider + for (Map.Entry scResult : scbEngine.resultsPerFault.entrySet()) { + ShortCircuitFault scFault = scResult.getKey(); + double ir = scResult.getValue().getIdx(); + double ii = scResult.getValue().getIdy(); + double icc = Math.sqrt(ir * ir + ii * ii); + + Fault fault = scFaultToFault.get(scFault); + + // TODO : put here additional results + List feederResults = new ArrayList<>(); + List limitViolations = new ArrayList<>(); + FortescueValue current = new FortescueValue(icc, 0.); + + FaultResult fr = new FaultResult(fault, 0., feederResults, limitViolations, current); + frs.add(fr); + } + + LOGGER.info("Short circuit calculation done in {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); + + return CompletableFuture.completedFuture(new ShortCircuitAnalysisResult(frs)); + } + +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBalancedEngine.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBalancedEngine.java new file mode 100644 index 00000000..344b9193 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBalancedEngine.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.network.LfBus; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitBalancedEngine extends AbstractShortCircuitEngine { + + public ShortCircuitBalancedEngine(Network network, ShortCircuitEngineParameters parameters) { + super(network, parameters); + } + + @Override + public void run() { //can handle both selective and systematic analysis with one single matrix inversion + + // building a contingency list with all voltage levels + // TODO : generate a list with busses + if (parameters.getAnalysisType() == ShortCircuitEngineParameters.AnalysisType.SYSTEMATIC) { + buildSystematicList(ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + } + + solverFaultList = buildFaultListsFromInputs().getKey(); + + ImpedanceLinearResolutionParameters linearResolutionParameters = new ImpedanceLinearResolutionParameters(acLoadFlowParameters, + parameters.getMatrixFactory(), solverFaultList, parameters.isVoltageUpdate(), getAdmittanceVoltageProfileTypeFromParam(), getAdmittancePeriodTypeFromParam(), AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN, + parameters.isIgnoreShunts(), parameters.getAdditionalDataInfo()); + + ImpedanceLinearResolution directResolution = new ImpedanceLinearResolution(network, linearResolutionParameters); + + directResolution.run(); + + //Build the ShortCircuit results using the Thevenin computation results + resultsPerFault = new LinkedHashMap<>(); + processAdmittanceLinearResolutionResults(directResolution); + + } + + protected void processAdmittanceLinearResolutionResults(ImpedanceLinearResolution directResolution) { + + for (ImpedanceLinearResolution.ImpedanceLinearResolutionResult linearResolutionResult : directResolution.results) { + LfBus bus = linearResolutionResult.getBus(); + + // For each contingency that matches the given bus of the linear resolution we compute: + // If = Eth / (Zth + Zf) gives: + // + // ethi*(xf+xth) + ethr*(rf+rth) + j*[ethi*(rf+rth) - ethr*(xf+xth)] + // If = -------------------------------------------------------------------- + // (rf+rth)² + (xf+xth)² + // + + // values that does not change for a given bus in input + double vxInit = linearResolutionResult.getEthr(); + double vyInit = linearResolutionResult.getEthi(); + + double rth = linearResolutionResult.getRthz11(); + double xth = linearResolutionResult.getXthz12(); + + for (CalculationLocation calculationLocation : solverFaultList) { + ShortCircuitFault scfe = (ShortCircuitFault) calculationLocation; // TODO : better check but calculationLocation must be a ShortCircuitFault + ShortCircuitFault scf = null; + if (bus.getId().equals(scfe.getLfBusInfo())) { + scf = scfe; + } + + if (scf == null) { + continue; + } + + double rf = scf.getZfr(); + double xf = scf.getZfi(); + + double denom = (rf + rth) * (rf + rth) + (xf + xth) * (xf + xth); + double ifr = (vyInit * (xf + xth) + vxInit * (rf + rth)) / denom; + double ifi = (vyInit * (rf + rth) - vxInit * (xf + xth)) / denom; + // The post-fault voltage values at faulted bus are computed as follow : + // [Vr] = [Vr_init] - ifr * [e_dVr] + ifi * [e_dVi] + // [Vi] = [Vi_init] - ifr * [e_dVi] - ifi * [e_dVr] + double dvr = -ifr * linearResolutionResult.getEnBus().get(0, 0) + ifi * linearResolutionResult.getEnBus().get(1, 0); + double dvi = -ifr * linearResolutionResult.getEnBus().get(1, 0) - ifi * linearResolutionResult.getEnBus().get(0, 0); + + ShortCircuitResult res = new ShortCircuitResult(scf, bus, ifr, ifi, rth, xth, vxInit, vyInit, dvr, dvi, parameters.getMatrixFactory(), linearResolutionResult.getEqSysFeeders(), parameters.getNorm()); + if (parameters.voltageUpdate) { + //we get the lfNetwork to process the results + res.setLfNetwork(directResolution.lfNetworkResult); + + res.setTrueVoltageProfileUpdate(); + // The post-fault voltage values are computed as follow : + // [Vr] = [Vr_init] - ifr * [e_dVr] + ifi * [e_dVi] + // [Vi] = [Vi_init] - ifr * [e_dVi] - ifi * [e_dVr] + // we compute the delta values to be added to Vinit if we want the post-fault voltage : + int nbBusses = directResolution.lfNetworkResult.getBuses().size(); + res.createEmptyFortescueVoltageVector(nbBusses); + + for (Map.Entry vd : linearResolutionResult.getDv().entrySet()) { + int busNum = vd.getKey(); + double edVr = vd.getValue().get(0, 0); + double edVi = vd.getValue().get(1, 0); + //System.out.println(" dVth(" + vdr.getKey() + ") = " + edVr + " + j(" + edVi + ")"); + double deltaVr = -ifr * edVr + ifi * edVi; + double deltaVi = -ifr * edVi - ifi * edVr; + + res.fillVoltageInFortescueVector(busNum, deltaVr, deltaVi); + } + } + + res.updateFeedersResult(); // feeders are updated only if voltageUpdate is made + resultsPerFault.put(scf, res); + } + } + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitEngineParameters.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitEngineParameters.java new file mode 100644 index 00000000..7b08d214 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitEngineParameters.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.math.matrix.MatrixFactory; + +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitEngineParameters { + public enum VoltageProfileType { + CALCULATED, // use the computed values at nodes to compute Zth and Eth + NOMINAL; // use the nominal voltage values at nodes to get Zth and Eth + } + + public enum PeriodType { + SUB_TRANSIENT, //uses subTransient parameters x"d + TRANSIENT, //uses transient parameters x'd + STEADY_STATE; + } + + public enum AnalysisType { + SELECTIVE, // short circuit analysis for List faults in input + SYSTEMATIC; // short circuit analysis for all busses of input grid + } + + private final LoadFlowParameters loadFlowParameters; + + private List shortCircuitFaults; + + private final MatrixFactory matrixFactory; + + private final VoltageProfileType vProfile; + + private final boolean ignoreShunts; + + private final AnalysisType analysisType; + + public boolean voltageUpdate; + + private AdditionalDataInfo additionalDataInfo; + + private PeriodType periodType; + + private ShortCircuitNorm norm; + + public ShortCircuitEngineParameters(LoadFlowParameters loadFlowParameters, MatrixFactory matrixFactory, AnalysisType analysisType, List faults, boolean isVoltageExport, VoltageProfileType vProfile, boolean ignoreShunts, PeriodType periodType, AdditionalDataInfo additionalDataInfo, ShortCircuitNorm norm) { + this.loadFlowParameters = Objects.requireNonNull(loadFlowParameters); + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.shortCircuitFaults = Objects.requireNonNull(faults); + this.voltageUpdate = isVoltageExport; + this.ignoreShunts = ignoreShunts; + this.vProfile = vProfile; + this.analysisType = analysisType; + this.periodType = periodType; + this.additionalDataInfo = Objects.requireNonNull(additionalDataInfo); + this.norm = norm; + } + + public LoadFlowParameters getLoadFlowParameters() { + return loadFlowParameters; + } + + public List getShortCircuitFaults() { + return shortCircuitFaults; + } + + public MatrixFactory getMatrixFactory() { + return matrixFactory; + } + + public VoltageProfileType getVoltageProfileType() { + return vProfile; + } + + public boolean isIgnoreShunts() { + return ignoreShunts; + } + + public AnalysisType getAnalysisType() { + return analysisType; + } + + public void setShortCircuitFaults(List faults) { + shortCircuitFaults = faults; + } + + public AdditionalDataInfo getAdditionalDataInfo() { + return additionalDataInfo; + } + + public PeriodType getPeriodType() { + return periodType; + } + + public ShortCircuitNorm getNorm() { + return norm; + } + + public boolean isVoltageUpdate() { + return voltageUpdate; + } + + public void setVoltageUpdate(boolean bool) { + voltageUpdate = bool; + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitFault.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitFault.java new file mode 100644 index 00000000..b7217f65 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitFault.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.incubator.simulator.util.CalculationLocation; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitFault extends CalculationLocation { + + public ShortCircuitFault(String busLocation, boolean voltageUpdate, String faultId, double zfr, double zfi, ShortCircuitType type) { + super(busLocation, voltageUpdate); + this.zfr = zfr; + this.zfi = zfi; + this.type = type; + this.inputByBus = false; + this.faultId = faultId; + } + + public ShortCircuitFault(String busLocation, String busLocationBiPhased, boolean voltageUpdate, String faultId, double zfr, double zfi, ShortCircuitType type, ShortCircuitBiphasedType biphasedType) { + super(busLocation, busLocationBiPhased, voltageUpdate); + this.zfr = zfr; + this.zfi = zfi; + this.type = type; + this.inputByBus = false; + this.faultId = faultId; + this.biphasedType = biphasedType; + } + + + public enum ShortCircuitType { + TRIPHASED_GROUND, + BIPHASED, + BIPHASED_GROUND, + BIPHASED_COMMON_SUPPORT, + MONOPHASED; + } + + public enum ShortCircuitBiphasedType { + C1_C2, + C1_B2, + C1_A2; + } + + // TODO : remove once input by bus is OK + + private String faultId; + + private boolean inputByBus; // true if input given by bus + + private double zfr; //real part of the short circuit impedance Zf + private double zfi; //imaginary part of the short circuit impedance Zf + + private ShortCircuitType type; + + private ShortCircuitBiphasedType biphasedType; + + public boolean isInputByBus() { + return inputByBus; + } + + public ShortCircuitType getType() { + return type; + } + + public double getZfr() { + return zfr; + } + + public double getZfi() { + return zfi; + } + + public ShortCircuitBiphasedType getBiphasedType() { + return biphasedType; + } + + public String getFaultId() { + return faultId; + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNorm.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNorm.java new file mode 100644 index 00000000..f202e76d --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNorm.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitNorm { + + public enum NormType { + NONE, + IEC, + RTE_COURCIRC; + } + + ShortCircuitNorm() { + + } + + public double getCmaxVoltageFactor(double nominalVoltage) { + return 1.0; + } + + public double getCminVoltageFactor(double nominalVoltage) { + return 1.0; + } + + public double getKtT2W(TwoWindingsTransformer t2w) { + return 1.0; + } + + public double getKtT3W(ThreeWindingsTransformer t3w, int numLeg) { + return 1.0; + } + + public NormType getNormType() { + return NormType.NONE; + } + +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNormCourcirc.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNormCourcirc.java new file mode 100644 index 00000000..a280cdf4 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNormCourcirc.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitNormCourcirc extends ShortCircuitNorm { + + public ShortCircuitNormCourcirc() { + super(); + } + + @Override + public double getCmaxVoltageFactor(double nominalVoltage) { + double cmax = 1.0; + return cmax; + } + + @Override + public double getCminVoltageFactor(double nominalVoltage) { + double cmin = 1.0; + return cmin; + } + + @Override + public NormType getNormType() { + return NormType.RTE_COURCIRC; + } +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNormIec.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNormIec.java new file mode 100644 index 00000000..db164a21 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitNormIec.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitNormIec extends ShortCircuitNorm { + + public static final double LOW_VOLTAGE_KV_THRESHOLD = 1.; + public static final double MEDIUM_VOLTAGE_KV_THRESHOLD = 35.; + + public static final double LOW_VOLTAGE_6PERCENT_CMAX = 1.05; + public static final double LOW_VOLTAGE_10PERCENT_CMAX = 1.10; + public static final double MEDIUM_VOLTAGE_CMAX = 1.10; + public static final double HIGH_VOLTAGE_MAX_CMAX = 1.10; + + public static final double LOW_VOLTAGE_CMIN = 0.95; + public static final double MEDIUM_VOLTAGE_CMIN = 1.0; + public static final double HIGH_VOLTAGE_MAX_CMIN = 1.0; + + public ShortCircuitNormIec() { + super(); + } + + @Override + public double getCmaxVoltageFactor(double nominalVoltage) { + double cmax = 1.0; + if (nominalVoltage <= LOW_VOLTAGE_KV_THRESHOLD) { + cmax = LOW_VOLTAGE_6PERCENT_CMAX; //TODO : make the distinction with 10% tolerance parameter + } else if (nominalVoltage <= MEDIUM_VOLTAGE_KV_THRESHOLD) { + cmax = MEDIUM_VOLTAGE_CMAX; + } else { + cmax = HIGH_VOLTAGE_MAX_CMAX; + } + return cmax; + } + + @Override + public double getCminVoltageFactor(double nominalVoltage) { + double cmin = 1.0; + if (nominalVoltage <= LOW_VOLTAGE_KV_THRESHOLD) { + cmin = LOW_VOLTAGE_CMIN; + } else if (nominalVoltage <= MEDIUM_VOLTAGE_KV_THRESHOLD) { + cmin = MEDIUM_VOLTAGE_CMIN; + } else { + cmin = HIGH_VOLTAGE_MAX_CMIN; + } + return cmin; + } + + @Override + public double getKtT2W(TwoWindingsTransformer t2w) { + double cmax = getCmaxVoltageFactor(t2w.getTerminal1().getVoltageLevel().getNominalV()); + double ratedSt2w = t2w.getRatedS(); + double ratedU2 = t2w.getRatedU2(); //TODO : check that the assumption to use ratedU2 is always correct + double xt2w = t2w.getX(); + + double kt = 0.95 * cmax / (1 + 0.6 * xt2w * ratedSt2w / (ratedU2 * ratedU2)); + + return kt; + } + + @Override + public double getKtT3W(ThreeWindingsTransformer t3w, int numLeg) { + + ThreeWindingsTransformer.Leg t3wLeg; + if (numLeg == 1) { + t3wLeg = t3w.getLeg1(); + } else if (numLeg == 2) { + t3wLeg = t3w.getLeg2(); + } else if (numLeg == 3) { + t3wLeg = t3w.getLeg3(); + } else { + throw new IllegalArgumentException("Three Winding Transformer " + t3w.getId() + " input leg must be 1, 2 or 3 but was : " + numLeg); + } + + double cmax = getCmaxVoltageFactor(t3wLeg.getTerminal().getVoltageLevel().getNominalV()); + double ratedSt3w = t3wLeg.getRatedS(); + double ratedU0 = t3w.getRatedU0(); // we suppose that all impedances are based on an impedance base (Sb_leg, U0) + double xt3w = t3wLeg.getX(); + + double kt = 0.95 * cmax / (1 + 0.6 * xt3w * ratedSt3w / (ratedU0 * ratedU0)); + + return kt; + } + + @Override + public NormType getNormType() { + return NormType.IEC; + } + +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitResult.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitResult.java new file mode 100644 index 00000000..20d95c72 --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitResult.java @@ -0,0 +1,476 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.PiModel; + +import java.util.*; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitResult { + + public class CommonSupportResult { + + private LfBus lfBus2; // FIXME : might be wrongly overwritten in the "resultsPerFault" presentation + + private double eth2x; + + private double eth2y; + + private DenseMatrix i2Fortescue; //fortescue vector of currents + + private DenseMatrix v2Fortescue; //fortescue vector of voltages + + CommonSupportResult(LfBus lfBus2, double eth2x, double eth2y, + double i2dx, double i2dy, double i2ox, double i2oy, double i2ix, double i2iy, + double dv2dx, double dv2dy, double dv2ox, double dv2oy, double dv2ix, double dv2iy) { + this.lfBus2 = lfBus2; + this.eth2x = eth2x; + this.eth2y = eth2y; + + Matrix mI2 = matrixFactory.create(6, 1, 6); + mI2.add(0, 0, i2ox); + mI2.add(1, 0, i2oy); + mI2.add(2, 0, i2dx); + mI2.add(3, 0, i2dy); + mI2.add(4, 0, i2ix); + mI2.add(5, 0, i2iy); + this.i2Fortescue = mI2.toDense(); + + //construction of the fortescue vector vFortescue = t[Vh, Vd, Vi] + double vdx = eth2x + dv2dx; + double vdy = eth2y + dv2dy; + double vhx = dv2ox; + double vhy = dv2oy; + double vix = dv2ix; + double viy = dv2iy; + + Matrix mV2 = matrixFactory.create(6, 1, 6); + mV2.add(0, 0, vhx); + mV2.add(1, 0, vhy); + mV2.add(2, 0, vdx); + mV2.add(3, 0, vdy); + mV2.add(4, 0, vix); + mV2.add(5, 0, viy); + this.v2Fortescue = mV2.toDense(); + } + } + + private final MatrixFactory matrixFactory; + + private LfBus lfBus; // FIXME : might be wrongly overwritten in the "resultsPerFault" presentation + + private LfNetwork lfNetwork; + + private ShortCircuitNorm norm; + + private double rd; // equivalent direct impedance + + private double xd; + + private double ri; // equivalent inverse impedance + + private double xi; + + private double rh; // equivalent homopolar impedance + + private double xh; + + private double ethx; + + private double ethy; + + private DenseMatrix iFortescue; //fortescue vector of currents + + private DenseMatrix vFortescue; //fortescue vector of voltages + + private boolean isVoltageProfileUpdated; + private List busNum2Dv; + + private EquationSystemFeeders eqSysFeedersDirect; + + private EquationSystemFeeders eqSysFeedersHomopolar; + + private Map feedersAtBusResultsDirect; + + private Map feedersAtBusResultsHomopolar; + + private ShortCircuitFault shortCircuitFault; + + private CommonSupportResult commonSupportResult; // used only for biphased with common support faults + + public ShortCircuitResult(ShortCircuitFault shortCircuitFault, LfBus lfBus, + double ifr, double ifi, + double rth, double xth, double ethr, double ethi, double dvr, double dvi, + MatrixFactory matrixFactory, EquationSystemFeeders eqSysFeeders, ShortCircuitNorm norm) { + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.lfBus = lfBus; + this.eqSysFeedersDirect = eqSysFeeders; + this.shortCircuitFault = shortCircuitFault; + this.norm = norm; + + this.rd = rth; + this.xd = xth; + this.ri = 0.; + this.xi = 0.; + this.rh = 0.; + this.xh = 0.; + + this.ethx = ethr; + this.ethy = ethi; + + //construction of the fortescue vector iFortescue = t[Ih, Id, Ii] + double idx = ifr; + double idy = ifi; + double iix = 0.; + double iiy = 0.; + double ihx = 0.; + double ihy = 0.; + + Matrix mI = matrixFactory.create(6, 1, 6); + mI.add(0, 0, ihx); + mI.add(1, 0, ihy); + mI.add(2, 0, idx); + mI.add(3, 0, idy); + mI.add(4, 0, iix); + mI.add(5, 0, iiy); + this.iFortescue = mI.toDense(); + + //construction of the fortescue vector vFortescue = t[Vh, Vd, Vi] + double vdx = ethr + dvr; + double vdy = ethi + dvi; + double vhx = 0.; + double vhy = 0.; + double vix = 0.; + double viy = 0.; + + Matrix mV = matrixFactory.create(6, 1, 6); + mV.add(0, 0, vhx); + mV.add(1, 0, vhy); + mV.add(2, 0, vdx); + mV.add(3, 0, vdy); + mV.add(4, 0, vix); + mV.add(5, 0, viy); + this.vFortescue = mV.toDense(); + + isVoltageProfileUpdated = false; + + } + + public ShortCircuitResult(ShortCircuitFault shortCircuitFault, LfBus lfBus, + double idx, double idy, double iox, double ioy, double iix, double iiy, + double rd, double xd, double ro, double xo, double ri, double xi, + double vdxinit, double vdyinit, double dvdx, double dvdy, double dvox, double dvoy, double dvix, double dviy, + MatrixFactory matrixFactory, EquationSystemFeeders eqSysFeedersDirect, EquationSystemFeeders eqSysFeedersHomopolar, ShortCircuitNorm norm) { + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.lfBus = lfBus; + this.eqSysFeedersDirect = eqSysFeedersDirect; + this.eqSysFeedersHomopolar = eqSysFeedersHomopolar; + this.shortCircuitFault = shortCircuitFault; + this.norm = norm; + + this.rd = rd; + this.xd = xd; + this.ri = ri; + this.xi = xi; + this.rh = ro; + this.xh = xo; + + this.ethx = vdxinit; + this.ethy = vdyinit; + + //construction of the fortescue vector iFortescue = t[Ih, Id, Ii] + Matrix mI = matrixFactory.create(6, 1, 6); + mI.add(0, 0, iox); + mI.add(1, 0, ioy); + mI.add(2, 0, idx); + mI.add(3, 0, idy); + mI.add(4, 0, iix); + mI.add(5, 0, iiy); + this.iFortescue = mI.toDense(); + + //construction of the fortescue vector vFortescue = t[Vh, Vd, Vi] + double vdx = ethx + dvdx; + double vdy = ethy + dvdy; + double vhx = dvox; + double vhy = dvoy; + double vix = dvix; + double viy = dviy; + + Matrix mV = matrixFactory.create(6, 1, 6); + mV.add(0, 0, vhx); + mV.add(1, 0, vhy); + mV.add(2, 0, vdx); + mV.add(3, 0, vdy); + mV.add(4, 0, vix); + mV.add(5, 0, viy); + this.vFortescue = mV.toDense(); + + isVoltageProfileUpdated = false; + + } + + public ShortCircuitResult(ShortCircuitFault shortCircuitFault, LfBus lfBus, + double idx, double idy, double iox, double ioy, double iix, double iiy, + double rd, double xd, double ro, double xo, double ri, double xi, + double vdxinit, double vdyinit, double dvdx, double dvdy, double dvox, double dvoy, double dvix, double dviy, + MatrixFactory matrixFactory, EquationSystemFeeders eqSysFeedersDirect, EquationSystemFeeders eqSysFeedersHomopolar, ShortCircuitNorm norm, + double i2dx, double i2dy, double i2ox, double i2oy, double i2ix, double i2iy, + double v2dxinit, double v2dyinit, double dv2dx, double dv2dy, double dv2ox, double dv2oy, double dv2ix, double dv2iy, + LfBus lfBus2) { + this(shortCircuitFault, lfBus, + idx, idy, iox, ioy, iix, iiy, + rd, xd, ro, xo, ri, xi, + vdxinit, vdyinit, dvdx, dvdy, dvox, dvoy, dvix, dviy, + matrixFactory, eqSysFeedersDirect, eqSysFeedersHomopolar, norm); + + this.commonSupportResult = new CommonSupportResult(lfBus2, v2dxinit, v2dyinit, + i2dx, i2dy, i2ox, i2oy, i2ix, i2iy, + dv2dx, dv2dy, dv2ox, dv2oy, dv2ix, dv2iy); + + } + + public void updateFeedersResult() { + //System.out.println(" VL name = " + shortCircuitVoltageLevelLocation); + //System.out.println(" bus name = " + shortCircuitLfbusLocation); + //System.out.println(" Icc = " + getIcc()); + //System.out.println(" Ih = " + iFortescue.get(0, 0) + " + j(" + iFortescue.get(1, 0) + ")"); + //System.out.println(" Id = " + iFortescue.get(2, 0) + " + j(" + iFortescue.get(3, 0) + ")"); + //System.out.println(" Ii = " + iFortescue.get(4, 0) + " + j(" + iFortescue.get(5, 0) + ")"); + //System.out.println(" Vh = " + vFortescue.get(0, 0) + " + j(" + vFortescue.get(1, 0) + ")"); + //System.out.println(" Vd = " + vFortescue.get(2, 0) + " + j(" + vFortescue.get(3, 0) + ")"); + //System.out.println(" Vi = " + vFortescue.get(4, 0) + " + j(" + vFortescue.get(5, 0) + ")"); + //System.out.println(" Eth = " + ethx + " + j(" + ethy + ")"); + + if (isVoltageProfileUpdated) { + + /*for (Map.Entry vd : bus2dv.entrySet()) { + System.out.println(" dVd(" + vd.getKey() + ") = " + vd.getValue().get(2, 0) + " + j(" + vd.getValue().get(3, 0) + ")"); + System.out.println(" dVo(" + vd.getKey() + ") = " + vd.getValue().get(0, 0) + " + j(" + vd.getValue().get(1, 0) + ")"); + System.out.println(" dVi(" + vd.getKey() + ") = " + vd.getValue().get(4, 0) + " + j(" + vd.getValue().get(5, 0) + ")"); + }*/ + + feedersAtBusResultsDirect = new HashMap<>(); // TODO : homopolar + + for (LfBus bus : lfNetwork.getBuses()) { + //int busNum = bus.getNum(); + //double dvx = busNum2Dv.get(busNum).get(2, 0); + //double dvy = busNum2Dv.get(busNum).get(3, 0); + //double vx = dvx + ethx; + //double vy = dvy + ethy; + + //System.out.println(" dVd(" + bus.getId() + ") = " + dvx + " + j(" + dvy + ") Module = " + bus.getNominalV() * Math.sqrt(vx * vx + vy * vy)); + //System.out.println(" dVo(" + bus.getId() + ") = " + bus2dv.get(busNum).get(0, 0) + " + j(" + bus2dv.get(busNum).get(1, 0) + ")"); + //System.out.println(" dVi(" + bus.getId() + ") = " + bus2dv.get(busNum).get(4, 0) + " + j(" + bus2dv.get(busNum).get(5, 0) + ")"); + + // Init of feeder results + EquationSystemBusFeeders busFeeders = eqSysFeedersDirect.busToFeeders.get(bus); + EquationSystemBusFeedersResult resultBusFeeders = new EquationSystemBusFeedersResult(busFeeders.getFeeders(), bus); + feedersAtBusResultsDirect.put(bus, resultBusFeeders); // TODO : homopolar + + } + + for (LfBranch branch : lfNetwork.getBranches()) { + LfBus bus1 = branch.getBus1(); + LfBus bus2 = branch.getBus2(); + if (bus1 != null && bus2 != null) { + DenseMatrix yd12 = getAdmittanceMatrixBranch(branch, bus1, bus2, matrixFactory, AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN); + int busNum1 = bus1.getNum(); + double dvx1 = busNum2Dv.get(busNum1).get(2, 0); + double dvy1 = busNum2Dv.get(busNum1).get(3, 0); + int busNum2 = bus2.getNum(); + double dvx2 = busNum2Dv.get(busNum2).get(2, 0); + double dvy2 = busNum2Dv.get(busNum2).get(3, 0); + DenseMatrix v12 = matrixFactory.create(4, 1, 4).toDense(); + v12.add(0, 0, dvx1 + 0.); //TODO : replace 1. by initial value + v12.add(1, 0, dvy1 + 0.); //TODO : replace 0. by initial value + v12.add(2, 0, dvx2 + 0.); //TODO : replace 1. by initial value + v12.add(3, 0, dvy2 + 0.); //TODO : replace 0. by initial value + DenseMatrix i12 = yd12.times(v12).toDense(); + //System.out.println(" dI1d(" + branch.getId() + ") = " + i12.get(0, 0) + " + j(" + i12.get(1, 0) + ") Module I1d = " + 1000. * 100. / bus1.getNominalV() * Math.sqrt((i12.get(0, 0) * i12.get(0, 0) + i12.get(1, 0) * i12.get(1, 0)) / 3)); + //System.out.println(" dI2d(" + branch.getId() + ") = " + i12.get(2, 0) + " + j(" + i12.get(3, 0) + ") Module I2d = " + 1000. * 100. / bus2.getNominalV() * Math.sqrt((i12.get(2, 0) * i12.get(2, 0) + i12.get(3, 0) * i12.get(3, 0)) / 3)); + + // Feeders : + // compute the sum of currents from branches at each bus + EquationSystemBusFeedersResult resultBus1Feeders = feedersAtBusResultsDirect.get(bus1); // TODO : homopolar + EquationSystemBusFeedersResult resultBus2Feeders = feedersAtBusResultsDirect.get(bus2); // TODO : homopolar + + resultBus1Feeders.addIfeeders(i12.get(0, 0), i12.get(1, 0)); + resultBus2Feeders.addIfeeders(i12.get(2, 0), i12.get(3, 0)); + + } + } + + // computing feeders contribution + for (LfBus bus : lfNetwork.getBuses()) { + EquationSystemBusFeedersResult busFeeders = feedersAtBusResultsDirect.get(bus); // TODO : homopolar + busFeeders.updateContributions(); + } + } + } + + public double getIdx() { + return iFortescue.get(2, 0); + } + + public double getIdy() { + return iFortescue.get(3, 0); + } + + public double getIox() { + return iFortescue.get(0, 0); + } + + public double getIoy() { + return iFortescue.get(1, 0); + } + + public double getIcc() { + // Icc = 1/sqrt(3) * Eth(pu) / Zth(pu) * SB(MVA) * 10e6 / (VB(kV) * 10e3) + //return Math.sqrt((getIdx() * getIdx() + getIdy() * getIdy()) / 3) * 1000. * 100. / lfBus.getNominalV(); + return Math.sqrt((getIdx() * getIdx() + getIdy() * getIdy()) / 3) * 1000. * 100.; + } + + public double getIk() { + // Ik = c * Un / (sqrt(3) * Zk) = c / sqrt(3) * Eth(pu) / Zth(pu) * Sb / Vb + return Math.sqrt((getIdx() * getIdx() + getIdy() * getIdy()) / 3) * 100. / lfBus.getNominalV() * norm.getCmaxVoltageFactor(lfBus.getNominalV()); + } + + public LfBus getLfBus() { + return lfBus; + } + + public double getPcc() { + //Pcc = |Eth|*Icc*sqrt(3) + return Math.sqrt(3) * getIcc() * lfBus.getV() * lfBus.getNominalV(); //TODO: check formula + } + + public void setTrueVoltageProfileUpdate() { + isVoltageProfileUpdated = true; + } + + public void createEmptyFortescueVoltageVector(int nbBusses) { + List busNum2Dv = new ArrayList<>(); + for (int i = 0; i < nbBusses; i++) { + DenseMatrix mdV = matrixFactory.create(6, 1, 6).toDense(); + busNum2Dv.add(mdV); + } + this.busNum2Dv = busNum2Dv; + } + + public void fillVoltageInFortescueVector(int busNum, double dVdx, double dVdy) { + this.busNum2Dv.get(busNum).add(2, 0, dVdx); + this.busNum2Dv.get(busNum).add(3, 0, dVdy); + } + + public void fillVoltageInFortescueVector(int busNum, double dVdx, double dVdy, double dVox, double dVoy, double dVix, double dViy) { + this.busNum2Dv.get(busNum).add(0, 0, dVox); + this.busNum2Dv.get(busNum).add(1, 0, dVoy); + this.busNum2Dv.get(busNum).add(2, 0, dVdx); + this.busNum2Dv.get(busNum).add(3, 0, dVdy); + this.busNum2Dv.get(busNum).add(4, 0, dVix); + this.busNum2Dv.get(busNum).add(5, 0, dViy); + } + + public void setLfNetwork(LfNetwork lfNetwork) { + this.lfNetwork = lfNetwork; + } + + static DenseMatrix getAdmittanceMatrixBranch(LfBranch branch, LfBus bus1, LfBus bus2, MatrixFactory matrixFactory, + AdmittanceEquationSystem.AdmittanceType admittanceType) { + + // TODO : code duplicated with the admittance equation system, should be un-duplicated + PiModel piModel = branch.getPiModel(); + if (piModel.getX() == 0) { + throw new IllegalArgumentException("Branch '" + branch.getId() + "' has reactance equal to zero"); + } + double rho = piModel.getR1(); + if (piModel.getZ() == 0) { + throw new IllegalArgumentException("Branch '" + branch.getId() + "' has Z equal to zero"); + } + double zInvSquare = 1 / (piModel.getZ() * piModel.getZ()); + double r = piModel.getR(); + double x = piModel.getX(); + double alpha = piModel.getA1(); + double cosA = Math.cos(Math.toRadians(alpha)); + double sinA = Math.sin(Math.toRadians(alpha)); + double gPi1 = piModel.getG1(); + double bPi1 = piModel.getB1(); + double gPi2 = piModel.getG2(); + double bPi2 = piModel.getB2(); + + double g12 = rho * zInvSquare * (r * cosA + x * sinA); + double b12 = -rho * zInvSquare * (x * cosA + r * sinA); + double g1g12sum = rho * rho * (gPi1 + r * zInvSquare); + double b1b12sum = rho * rho * (bPi1 - x * zInvSquare); + if (admittanceType == AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + g12 = g12 * AdmittanceConstants.COEF_XO_XD; // Xo = 3 * Xd as a first approximation : TODO : improve when more data available + b12 = b12 * AdmittanceConstants.COEF_XO_XD; + g1g12sum = g1g12sum * AdmittanceConstants.COEF_XO_XD; + b1b12sum = b1b12sum * AdmittanceConstants.COEF_XO_XD; + } + + double g21 = rho * zInvSquare * (r * cosA + x * sinA); + double b21 = rho * zInvSquare * (r * sinA - x * cosA); + double g2g21sum = r * zInvSquare + gPi2; + double b2b21sum = -x * zInvSquare + bPi2; + if (admittanceType == AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + g21 = g21 * AdmittanceConstants.COEF_XO_XD; // Xo = 3 * Xd as a first approximation : TODO : improve when more data available + b21 = b21 * AdmittanceConstants.COEF_XO_XD; + g2g21sum = g2g21sum * AdmittanceConstants.COEF_XO_XD; + b2b21sum = b2b21sum * AdmittanceConstants.COEF_XO_XD; + } + + DenseMatrix mAdmittance = matrixFactory.create(4, 4, 16).toDense(); + mAdmittance.add(0, 0, g1g12sum); + mAdmittance.add(0, 1, -b1b12sum); + mAdmittance.add(0, 2, -g12); + mAdmittance.add(0, 3, b12); + mAdmittance.add(1, 0, b1b12sum); + mAdmittance.add(1, 1, g1g12sum); + mAdmittance.add(1, 2, -b12); + mAdmittance.add(1, 3, -g12); + mAdmittance.add(2, 0, -g21); + mAdmittance.add(2, 1, b21); + mAdmittance.add(2, 2, g2g21sum); + mAdmittance.add(2, 3, -b2b21sum); + mAdmittance.add(3, 0, -b21); + mAdmittance.add(3, 1, -g21); + mAdmittance.add(3, 2, b2b21sum); + mAdmittance.add(3, 3, g2g21sum); + + return mAdmittance.toDense(); + + } + + // used for tests + public double getIxFeeder(String busId, String feederId) { + double ix = 0.; + for (LfBus bus : lfNetwork.getBuses()) { + if (bus.getId().equals(busId)) { + EquationSystemBusFeedersResult resultFeeder = feedersAtBusResultsDirect.get(bus); // TODO : homopolar + List busFeedersResults = resultFeeder.getBusFeedersResults(); + for (EquationSystemResultFeeder feeder : busFeedersResults) { + if (feeder.getId().equals(feederId)) { + ix = feeder.getIxContribution(); + } + } + } + } + return ix; + } + +} diff --git a/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitUnbalancedEngine.java b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitUnbalancedEngine.java new file mode 100644 index 00000000..5149af4f --- /dev/null +++ b/simulator/short-circuit/src/main/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitUnbalancedEngine.java @@ -0,0 +1,419 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +//import com.powsybl.math.matrix.DenseMatrix; + +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.network.LfBus; +import org.apache.commons.math3.util.Pair; + +import java.util.*; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitUnbalancedEngine extends AbstractShortCircuitEngine { + + public ShortCircuitUnbalancedEngine(Network network, ShortCircuitEngineParameters parameters) { + super(network, parameters); + } + + @Override + public void run() { + + if (parameters.getAnalysisType() == ShortCircuitEngineParameters.AnalysisType.SYSTEMATIC) { + buildSystematicList(ShortCircuitFault.ShortCircuitType.MONOPHASED); // TODO : by default it is monophased, could be changed to choose type of systematic default + // Biphased common support faults will not be supported yet in systematic + } + + // We handle a pre-treatement of faults given in input: + // - filtering of some inconsistencies on the bus identification + // - addition of info in each fault to ease the identification in LfNetwork of iidm info + Pair, List> faultLists = buildFaultListsFromInputs(); + + solverFaultList = faultLists.getKey(); + solverBiphasedFaultList = faultLists.getValue(); + + ImpedanceLinearResolutionParameters admittanceLinearResolutionParametersHomopolar = new ImpedanceLinearResolutionParameters(acLoadFlowParameters, + parameters.getMatrixFactory(), solverFaultList, parameters.isVoltageUpdate(), + getAdmittanceVoltageProfileTypeFromParam(), getAdmittancePeriodTypeFromParam(), AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR, + parameters.isIgnoreShunts(), parameters.getAdditionalDataInfo(), solverBiphasedFaultList); + + ImpedanceLinearResolutionParameters admittanceLinearResolutionParametersDirect = new ImpedanceLinearResolutionParameters(acLoadFlowParameters, + parameters.getMatrixFactory(), solverFaultList, parameters.isVoltageUpdate(), + getAdmittanceVoltageProfileTypeFromParam(), getAdmittancePeriodTypeFromParam(), AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN, + parameters.isIgnoreShunts(), parameters.getAdditionalDataInfo(), solverBiphasedFaultList); + + ImpedanceLinearResolution directResolution = new ImpedanceLinearResolution(network, admittanceLinearResolutionParametersDirect); + ImpedanceLinearResolution homopolarResolution = new ImpedanceLinearResolution(network, admittanceLinearResolutionParametersHomopolar); + + directResolution.run(); + homopolarResolution.run(); + + //Build the ShortCircuit results using the linear resolution computation results + resultsPerFault = new LinkedHashMap<>(); + processAdmittanceLinearResolutionResults(directResolution, homopolarResolution, ShortCircuitFault.ShortCircuitType.MONOPHASED); + processAdmittanceLinearResolutionResults(directResolution, homopolarResolution, ShortCircuitFault.ShortCircuitType.BIPHASED); + processAdmittanceLinearResolutionResults(directResolution, homopolarResolution, ShortCircuitFault.ShortCircuitType.BIPHASED_GROUND); + processAdmittanceLinearResolutionResults(directResolution, homopolarResolution, ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT); + } + + public void processAdmittanceLinearResolutionResults(ImpedanceLinearResolution directResolution, ImpedanceLinearResolution homopolarResolution, ShortCircuitFault.ShortCircuitType shortCircuitType) { + + int numResult = 0; + for (ImpedanceLinearResolution.ImpedanceLinearResolutionResult directResult : directResolution.results) { + + ImpedanceLinearResolution.ImpedanceLinearResolutionResult homopolarResult = homopolarResolution.results.get(numResult); + numResult++; + + LfBus lfBus1 = directResult.getBus(); + + List matchingFaults = new ArrayList<>(); + + if (shortCircuitType == ShortCircuitFault.ShortCircuitType.MONOPHASED + || shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED + || shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED_GROUND) { + for (CalculationLocation calculationLocation : solverFaultList) { + ShortCircuitFault scfe = (ShortCircuitFault) calculationLocation; // TODO : better check but calculationLocation must be a ShortCircuitFault + if (lfBus1.getId().equals(scfe.getLfBusInfo()) && scfe.getType() == shortCircuitType) { + matchingFaults.add(scfe); + } + } + } + + if (shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT) { + for (CalculationLocation calculationLocation : solverBiphasedFaultList) { + ShortCircuitFault scfe = (ShortCircuitFault) calculationLocation; // TODO : better check but calculationLocation must be a ShortCircuitFault + if (lfBus1.getId().equals(scfe.getLfBusInfo()) && scfe.getType() == shortCircuitType) { + matchingFaults.add(scfe); + } + } + } + + double v1dxInit = directResult.getEthr(); + double v1dyInit = directResult.getEthi(); + + double rdf = directResult.getRthz11(); + double xdf = directResult.getXthz12(); + + double rof = homopolarResult.getRthz11(); + double xof = homopolarResult.getXthz12(); + + for (ShortCircuitFault scf : matchingFaults) { + + double rf = scf.getZfr(); + double xf = scf.getZfi(); + + MatrixFactory mf = parameters.getMatrixFactory(); + + Matrix mIo = mf.create(2, 1, 2); + Matrix mId = mf.create(2, 1, 2); + Matrix mIi = mf.create(2, 1, 2); + + ShortCircuitResult res; + + if (shortCircuitType == ShortCircuitFault.ShortCircuitType.MONOPHASED + || shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED + || shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED_GROUND) { + if (shortCircuitType == ShortCircuitFault.ShortCircuitType.MONOPHASED) { + MonophasedShortCircuitCalculator monophasedCalculator = new MonophasedShortCircuitCalculator(rdf, xdf, rof, xof, rf, xf, v1dxInit, v1dyInit, mf); + monophasedCalculator.computeCurrents(); + + mIo = monophasedCalculator.getmIo(); + mId = monophasedCalculator.getmId(); + mIi = monophasedCalculator.getmIi(); + + } else if (shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED) { + BiphasedShortCircuitCalculator biphasedCalculator = new BiphasedShortCircuitCalculator(rdf, xdf, rof, xof, rf, xf, v1dxInit, v1dyInit, mf); + biphasedCalculator.computeCurrents(); + + mIo = biphasedCalculator.getmIo(); + mId = biphasedCalculator.getmId(); + mIi = biphasedCalculator.getmIi(); + } else if (shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED_GROUND) { + BiphasedGroundShortCircuitCalculator biphasedGrCalculator = new BiphasedGroundShortCircuitCalculator(rdf, xdf, rof, xof, rf, xf, v1dxInit, v1dyInit, mf); + biphasedGrCalculator.computeCurrents(); + + mIo = biphasedGrCalculator.getmIo(); + mId = biphasedGrCalculator.getmId(); + mIi = biphasedGrCalculator.getmIi(); + } + + res = buildUnbalancedResult(mId, mIo, mIi, rdf, xdf, rof, xof, mf, + directResult, homopolarResult, + scf, lfBus1, v1dxInit, v1dyInit, directResolution); + + } else if (shortCircuitType == ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT) { + // TODO : We only handle the first biphased of the list for now, check how to handle this in the final version + ImpedanceLinearResolution.ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased biphasedDirectResult = directResult.getBiphasedResultsAtBus().get(0); + ImpedanceLinearResolution.ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased biphasedHomopolarResult = homopolarResult.getBiphasedResultsAtBus().get(0); + + double ro12 = biphasedHomopolarResult.getZ12txx(); // TODO : add some tests to check consistency with Z12tyy and Z12tyx + double xo12 = -biphasedHomopolarResult.getZ12txy(); + double ro22 = biphasedHomopolarResult.getZ22txx(); + double xo22 = -biphasedHomopolarResult.getZ22txy(); + double ro21 = biphasedHomopolarResult.getZ21txx(); + double xo21 = -biphasedHomopolarResult.getZ21txy(); + + double rd12 = biphasedDirectResult.getZ12txx(); + double xd12 = -biphasedDirectResult.getZ12txy(); + double rd22 = biphasedDirectResult.getZ22txx(); + double xd22 = -biphasedDirectResult.getZ22txy(); + double rd21 = biphasedDirectResult.getZ21txx(); + double xd21 = -biphasedDirectResult.getZ21txy(); + + BiphasedCommonSupportShortCircuitCalculator biphasedCommonCalculator; + if (scf.getBiphasedType() == ShortCircuitFault.ShortCircuitBiphasedType.C1_A2) { + biphasedCommonCalculator = new BiphasedC1A2Calculator(rdf, xdf, rof, xof, rf, xf, v1dxInit, v1dyInit, mf, + biphasedDirectResult.getV2x(), biphasedDirectResult.getV2y(), + ro12, xo12, ro22, xo22, ro21, xo21, + rd12, xd12, rd22, xd22, rd21, xd21); + } else if (scf.getBiphasedType() == ShortCircuitFault.ShortCircuitBiphasedType.C1_B2) { + biphasedCommonCalculator = new BiphasedC1B2Calculator(rdf, xdf, rof, xof, rf, xf, v1dxInit, v1dyInit, mf, + biphasedDirectResult.getV2x(), biphasedDirectResult.getV2y(), + ro12, xo12, ro22, xo22, ro21, xo21, + rd12, xd12, rd22, xd22, rd21, xd21); + } else if (scf.getBiphasedType() == ShortCircuitFault.ShortCircuitBiphasedType.C1_C2) { + biphasedCommonCalculator = new BiphasedC1C2Calculator(rdf, xdf, rof, xof, rf, xf, v1dxInit, v1dyInit, mf, + biphasedDirectResult.getV2x(), biphasedDirectResult.getV2y(), + ro12, xo12, ro22, xo22, ro21, xo21, + rd12, xd12, rd22, xd22, rd21, xd21); + } else { + throw new IllegalArgumentException(" short circuit fault of type : " + scf.getBiphasedType() + " not yet handled"); + } + + //biphasedCommonCalculator.computeCurrents(); + mIo = biphasedCommonCalculator.getmIo(); + mId = biphasedCommonCalculator.getmId(); + mIi = biphasedCommonCalculator.getmIi(); + + // TODO : check if we need to make a separate function to handle biphased common support + Matrix mI2o = biphasedCommonCalculator.getmI2o(); + Matrix mI2d = biphasedCommonCalculator.getmI2d(); + Matrix mI2i = biphasedCommonCalculator.getmI2i(); + + //biphasedCommonCalculator.computeVoltages(); + Matrix mdVo = biphasedCommonCalculator.getmVo(); // Contains variations of voltages, without Vinit + Matrix mdVd = biphasedCommonCalculator.getmVd(); // each voltage vector contains [V1x; V1y; V2x; V2y] + Matrix mdVi = biphasedCommonCalculator.getmVi(); + + LfBus lfBus2 = biphasedDirectResult.getBus2(); + + double v2dxInit = biphasedDirectResult.getV2x(); + double v2dyInit = biphasedDirectResult.getV2y(); + + res = buildUnbalancedCommunSuppportResult(mId, mIo, mIi, mI2d, mI2o, mI2i, mdVd, mdVo, mdVi, rdf, xdf, rof, xof, mf, + directResult, homopolarResult, scf, + lfBus1, v1dxInit, v1dyInit, directResolution, + lfBus2, v2dxInit, v2dyInit, biphasedDirectResult, biphasedHomopolarResult); + + } else { + throw new IllegalArgumentException(" Post-processing of short circuit type = " + shortCircuitType + "not yet implemented"); + } + + res.updateFeedersResult(); // feeders are updated only if voltageUpdate is made. TODO : see if update of homopolar feeders are to be updated + resultsPerFault.put(scf, res); + } + + } + } + + public ShortCircuitResult buildUnbalancedResult(Matrix mId, Matrix mIo, Matrix mIi, double rdf, double xdf, double rof, double xof, MatrixFactory mf, + ImpedanceLinearResolution.ImpedanceLinearResolutionResult directResult, + ImpedanceLinearResolution.ImpedanceLinearResolutionResult homopolarResult, + ShortCircuitFault scf, LfBus lfBus1, double v1dxInit, double v1dyInit, ImpedanceLinearResolution directResolution) { + //get the voltage vectors + // Vo : + // [vox] [ rof -xof ] [ iox ] + // [voy] = - [ xof rof ] * [ ioy ] + + Matrix zof = getZ(rof, xof, mf); + Matrix zdf = getZ(rdf, xdf, mf); + + Matrix minusVo = zof.times(mIo); + Matrix minusVd = zdf.times(mId); + Matrix minusVi = zdf.times(mIi); + + //record the results + EquationSystemFeeders equationSystemFeedersDirect = directResult.getEqSysFeeders(); + EquationSystemFeeders equationSystemFeedersHomopolar = homopolarResult.getEqSysFeeders(); + + ShortCircuitResult res = new ShortCircuitResult(scf, lfBus1, + mId.toDense().get(0, 0), mId.toDense().get(1, 0), + mIo.toDense().get(0, 0), mIo.toDense().get(1, 0), + mIi.toDense().get(0, 0), mIi.toDense().get(1, 0), + rdf, xdf, rof, xof, rdf, xdf, + v1dxInit, v1dyInit, + -minusVd.toDense().get(0, 0), -minusVd.toDense().get(1, 0), + -minusVo.toDense().get(0, 0), -minusVo.toDense().get(1, 0), + -minusVi.toDense().get(0, 0), -minusVi.toDense().get(1, 0), + mf, equationSystemFeedersDirect, equationSystemFeedersHomopolar, parameters.getNorm()); + + if (parameters.voltageUpdate) { + res.setLfNetwork(directResolution.lfNetworkResult); + res.setTrueVoltageProfileUpdate(); + // The post-fault voltage values for the network busses are computed as follow : + // [ Vof ] = -inv(Yo) * M * [ Iof ] + // [ Vdf ] = -inv(Yd) * M * [ Idf ] + [ V(init) ] + // [ Vif ] = -inv(Yd) * M * [ Iif ] + // dMo = inv(Yo) * M + // dMd = inv(Yd) * M + + int nbBusses = directResolution.lfNetworkResult.getBuses().size(); + res.createEmptyFortescueVoltageVector(nbBusses); + + for (Map.Entry vd : directResult.getDv().entrySet()) { + int busNum = vd.getKey(); + + //direct + double edVr = vd.getValue().get(0, 0); + double edVi = vd.getValue().get(1, 0); + + double idr = -mId.toDense().get(0, 0); + double idi = -mId.toDense().get(1, 0); + + double deltaVdr = -idr * edVr + idi * edVi; + double deltaVdi = -idr * edVi - idi * edVr; + + //inverse + double iir = -mIi.toDense().get(0, 0); + double iii = -mIi.toDense().get(1, 0); + + double deltaVir = -iir * edVr + iii * edVi; + double deltaVii = -iir * edVi - iii * edVr; + + //homopolar + double eoVr = homopolarResult.getDv().get(busNum).get(0, 0); + double eoVi = homopolarResult.getDv().get(busNum).get(1, 0); + + double ior = -mIo.toDense().get(0, 0); + double ioi = -mIo.toDense().get(1, 0); + + double deltaVor = -ior * eoVr + ioi * eoVi; + double deltaVoi = -ior * eoVi - ioi * eoVr; + + //System.out.println(" dVth(" + vdr.getKey() + ") = " + edVr + " + j(" + edVi + ")"); + + res.fillVoltageInFortescueVector(busNum, deltaVdr, deltaVdi, deltaVor, deltaVoi, deltaVir, deltaVii); + } + } + + return res; + } + + public ShortCircuitResult buildUnbalancedCommunSuppportResult(Matrix mId, Matrix mIo, Matrix mIi, Matrix mI2d, Matrix mI2o, Matrix mI2i, Matrix mVd, Matrix mVo, Matrix mVi, double rdf, double xdf, double rof, double xof, MatrixFactory mf, + ImpedanceLinearResolution.ImpedanceLinearResolutionResult directResult, + ImpedanceLinearResolution.ImpedanceLinearResolutionResult homopolarResult, ShortCircuitFault scf, + LfBus lfBus1, double v1dxInit, double v1dyInit, ImpedanceLinearResolution directResolution, + LfBus lfBus2, double v2dxInit, double v2dyInit, + ImpedanceLinearResolution.ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased biphasedDirectResult, + ImpedanceLinearResolution.ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased biphasedHomopolarResult) { + + //record the results + EquationSystemFeeders equationSystemFeedersDirect = directResult.getEqSysFeeders(); + EquationSystemFeeders equationSystemFeedersHomopolar = homopolarResult.getEqSysFeeders(); + // TODO : adapt in case of a biphased common support + + ShortCircuitResult res = new ShortCircuitResult(scf, lfBus1, + mId.toDense().get(0, 0), mId.toDense().get(1, 0), + mIo.toDense().get(0, 0), mIo.toDense().get(1, 0), + mIi.toDense().get(0, 0), mIi.toDense().get(1, 0), + rdf, xdf, rof, xof, rdf, xdf, + v1dxInit, v1dyInit, + mVd.toDense().get(0, 0), mVd.toDense().get(1, 0), + mVo.toDense().get(0, 0), mVo.toDense().get(1, 0), + mVi.toDense().get(0, 0), mVi.toDense().get(1, 0), + mf, equationSystemFeedersDirect, equationSystemFeedersHomopolar, parameters.getNorm(), + mI2d.toDense().get(0, 0), mI2d.toDense().get(1, 0), + mI2o.toDense().get(0, 0), mI2o.toDense().get(1, 0), + mI2i.toDense().get(0, 0), mI2i.toDense().get(1, 0), + v2dxInit, v2dyInit, + mVd.toDense().get(2, 0), mVd.toDense().get(3, 0), + mVo.toDense().get(2, 0), mVo.toDense().get(3, 0), + mVi.toDense().get(2, 0), mVi.toDense().get(3, 0), + lfBus2); + + if (parameters.voltageUpdate) { + res.setLfNetwork(directResolution.lfNetworkResult); + res.setTrueVoltageProfileUpdate(); + // The post-fault voltage values for the network busses are computed as follow : + // [ Vof ] = -inv(Yo) * M * [ Iof ] + // [ Vdf ] = -inv(Yd) * M * [ Idf ] + [ V(init) ] + // [ Vif ] = -inv(Yd) * M * [ Iif ] + // dMo = inv(Yo) * M + // dMd = inv(Yd) * M + + int nbBusses = directResolution.lfNetworkResult.getBuses().size(); + res.createEmptyFortescueVoltageVector(nbBusses); + + for (Map.Entry vd : directResult.getDv().entrySet()) { + int busNum = vd.getKey(); + + //int numBus2 = biphasedDirectResult.getNumBus2Fault(); + + //direct + double edVr = vd.getValue().get(0, 0); + double edVi = vd.getValue().get(1, 0); + double edV2r = biphasedDirectResult.getDv2().get(busNum).get(0, 0); // TODO : check : probably wrong, should be busNum + double edV2i = biphasedDirectResult.getDv2().get(busNum).get(1, 0); // TODO : check order to access DV2 is OK + + double idr = -mId.toDense().get(0, 0); + double idi = -mId.toDense().get(1, 0); + double i2dr = -mI2d.toDense().get(0, 0); + double i2di = -mI2d.toDense().get(1, 0); + + double deltaVdr = -idr * edVr + idi * edVi - i2dr * edV2r + i2di * edV2i; + double deltaVdi = -idr * edVi - idi * edVr - i2dr * edV2i - i2di * edV2r; + + //inverse + double iir = -mIi.toDense().get(0, 0); + double iii = -mIi.toDense().get(1, 0); + double i2ir = -mI2i.toDense().get(0, 0); + double i2ii = -mI2i.toDense().get(1, 0); + + double deltaVir = -iir * edVr + iii * edVi - i2ir * edV2r - i2ii * edV2i; + double deltaVii = -iir * edVi - iii * edVr - i2ir * edV2i - i2ii * edV2r; + + //homopolar + double eoVr = homopolarResult.getDv().get(busNum).get(0, 0); + double eoVi = homopolarResult.getDv().get(busNum).get(1, 0); + double eoV2r = biphasedHomopolarResult.getDv2().get(busNum).get(0, 0); + double eoV2i = biphasedHomopolarResult.getDv2().get(busNum).get(1, 0); + + double ior = -mIo.toDense().get(0, 0); + double ioi = -mIo.toDense().get(1, 0); + double i2or = -mI2o.toDense().get(0, 0); + double i2oi = -mI2o.toDense().get(1, 0); + + double deltaVor = -ior * eoVr + ioi * eoVi - i2or * eoV2r + i2oi * eoV2i; + double deltaVoi = -ior * eoVi - ioi * eoVr - i2or * eoV2i - i2oi * eoV2r; + + //System.out.println(" dVth(" + vdr.getKey() + ") = " + edVr + " + j(" + edVi + ")"); + + res.fillVoltageInFortescueVector(busNum, deltaVdr, deltaVdi, deltaVor, deltaVoi, deltaVir, deltaVii); + } + } + + return res; + } + + public static Matrix getZ(double r, double x, MatrixFactory mf) { + Matrix z = mf.create(2, 2, 2); + z.add(0, 0, r); + z.add(0, 1, -x); + z.add(1, 0, x); + z.add(1, 1, r); + + return z; + } + +} diff --git a/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBalancedTest.java b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBalancedTest.java new file mode 100644 index 00000000..96c3fd6d --- /dev/null +++ b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBalancedTest.java @@ -0,0 +1,708 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.computation.ComputationManager; +import com.powsybl.computation.local.LocalComputationManager; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import com.powsybl.shortcircuit.*; +import org.apache.commons.math3.util.Pair; +import org.joda.time.DateTime; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitBalancedTest { + + private LoadFlowParameters parameters; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + parameters = new LoadFlowParameters(); + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + @Test + void computeIccTest() { + Network nt2 = create2n(NetworkFactory.findDefault()); + LoadFlowResult resultnt2 = loadFlowRunner.run(nt2, parameters); + + List tmpV = new ArrayList<>(); + ShortCircuitFault sc2 = new ShortCircuitFault("B2", true, "sc2", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + tmpV.add(sc2); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.TRANSIENT; + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(); //No specific additional info needed in this test + + ShortCircuitEngineParameters.VoltageProfileType vp = ShortCircuitEngineParameters.VoltageProfileType.CALCULATED; + ShortCircuitEngineParameters.AnalysisType at = ShortCircuitEngineParameters.AnalysisType.SELECTIVE; + + LoadFlowParameters loadFlowParameters = new LoadFlowParameters(); + ShortCircuitNormCourcirc shortCircuitNormCourcirc = new ShortCircuitNormCourcirc(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, at, tmpV, true, vp, false, periodType, additionalDataInfo, shortCircuitNormCourcirc); + ShortCircuitBalancedEngine scbEngine = new ShortCircuitBalancedEngine(nt2, scbParameters); + + scbEngine.run(); + + scbEngine.resultsPerFault.get(sc2).updateFeedersResult(); + + assertEquals(-0.4316661015058293, scbEngine.resultsPerFault.get(sc2).getIdx(), 0.000001); + assertEquals(-4.617486568622836, scbEngine.resultsPerFault.get(sc2).getIdy(), 0.000001); + assertEquals(-0.5197272846952616, scbEngine.resultsPerFault.get(sc2).getIxFeeder("VL_1_0", "G1"), 0.000001); + + } + + @Test + void openShortCircuitProvider2n() { + + //set up LF info + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + Network nt2 = create2n(NetworkFactory.findDefault()); + LoadFlowResult resultnt2 = LoadFlow.run(nt2, loadFlowParameters); + + //set up ShortCircuitProvider info + ShortCircuitAnalysisProvider provider = new OpenShortCircuitProvider(new DenseMatrixFactory()); + ComputationManager cm = LocalComputationManager.getDefault(); + ShortCircuitParameters scp = new ShortCircuitParameters(); + + //CompletableFuture scar = provider.run(nt2, scp, cm); + List faults = new ArrayList<>(); + BusFault bf1 = new BusFault("F1", "B1"); + BusFault bf2 = new BusFault("F2", "B2"); + faults.add(bf1); + faults.add(bf2); + + ShortCircuitAnalysisResult scar = provider.run(nt2, faults, scp, cm, Collections.emptyList()).join(); + + List frs = scar.getFaultResults(); + + String providerName = provider.getName(); + String providerVersion = provider.getVersion(); + + assertEquals(4.646530628204346, frs.get(1).getCurrent().getDirectMagnitude(), 0.00001); + assertEquals(5.100971698760986, frs.get(0).getCurrent().getDirectMagnitude(), 0.00001); + assertEquals("OpenShortCircuit", providerName); + assertEquals("0.1", providerVersion); + + } + + @Test + void openShortCircuitProvider4n() { + + //set up LF info + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + Network nt4 = create4n(NetworkFactory.findDefault()); + LoadFlowResult resultnt4 = LoadFlow.run(nt4, loadFlowParameters); + + //set up ShortCircuitProvider info + ShortCircuitAnalysisProvider provider = new OpenShortCircuitProvider(new DenseMatrixFactory()); + ComputationManager cm = LocalComputationManager.getDefault(); + ShortCircuitParameters scp = new ShortCircuitParameters(); + + //CompletableFuture scar = provider.run(nt2, scp, cm); + List faults = new ArrayList<>(); // TODO + + BusFault bf1 = new BusFault("F1", "B1"); + BusFault bf2 = new BusFault("F2", "B2"); + BusFault bf3 = new BusFault("F3", "B3"); + BusFault bf4 = new BusFault("F4", "B4"); + + faults.add(bf1); + faults.add(bf2); + faults.add(bf3); + faults.add(bf4); + + ShortCircuitAnalysisResult scar = provider.run(nt4, faults, scp, cm, Collections.emptyList()).join(); + + List frs = scar.getFaultResults(); + + assertEquals(6.143869876861572, frs.get(0).getCurrent().getDirectMagnitude(), 0.00001); + assertEquals(6.491052150726318, frs.get(1).getCurrent().getDirectMagnitude(), 0.00001); + assertEquals(6.222183704376221, frs.get(2).getCurrent().getDirectMagnitude(), 0.00001); + assertEquals(5.909138679504394, frs.get(3).getCurrent().getDirectMagnitude(), 0.00001); + + } + + @Test + void openShortCircuitProvider2nTfo() { + + //set up LF info + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + Network nt2 = create2nTfo(NetworkFactory.findDefault()); + LoadFlowResult resultnt2 = LoadFlow.run(nt2, loadFlowParameters); + + //set up ShortCircuitProvider info + ShortCircuitAnalysisProvider provider = new OpenShortCircuitProvider(new DenseMatrixFactory()); + ComputationManager cm = LocalComputationManager.getDefault(); + ShortCircuitParameters scp = new ShortCircuitParameters(); + + //CompletableFuture scar = provider.run(nt2, scp, cm); + List faults = new ArrayList<>(); // TODO + + BusFault bf1 = new BusFault("F1", "B1"); + BusFault bf2 = new BusFault("F2", "B2"); + faults.add(bf1); + faults.add(bf2); + + ShortCircuitAnalysisResult scar = provider.run(nt2, faults, scp, cm, Collections.emptyList()).join(); + + List frs = scar.getFaultResults(); + + assertEquals(4.888257026672363, frs.get(1).getCurrent().getDirectMagnitude(), 0.00001); + assertEquals(5.100976467132568, frs.get(0).getCurrent().getDirectMagnitude(), 0.00001); + + } + + @Test + void shortCircuitSystematic() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Network nt2 = create2n(NetworkFactory.findDefault()); + LoadFlowResult resultnt2 = LoadFlow.run(nt2, loadFlowParameters); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List tmpV = new ArrayList<>(); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.TRANSIENT; + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(); //No specific additional info needed in this test + + ShortCircuitNormCourcirc shortCircuitNormCourcirc = new ShortCircuitNormCourcirc(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SYSTEMATIC, tmpV, false, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormCourcirc); + ShortCircuitBalancedEngine scbEngine = new ShortCircuitBalancedEngine(nt2, scbParameters); + + scbEngine.run(); + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIdx()); + } + + assertEquals(0.0996007987855852, val.get(0), 0.00001); + assertEquals(0.0999999987871081, val.get(1), 0.00001); + + } + + @Test + void shortCircuitSubTransientReference() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitReference(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B7", true, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc1); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + + ShortCircuitNormCourcirc shortCircuitNormCourcirc = new ShortCircuitNormCourcirc(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, true, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormCourcirc); + ShortCircuitBalancedEngine scbEngine = new ShortCircuitBalancedEngine(network, scbParameters); + + scbEngine.run(); + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIcc()); + } + + // here Icc = 1/sqrt(3)*Eth(pu)/Zth(pu100)*Sb100/Vb*1000 + // here new Icc = 1/sqrt(3)*Eth(pu)/Zth(pu100)*Sb100*1000 + // and Idocumentation = Ib*Eth(pu)/Zth(pu15) then Idocumentation = Icc * Ib * sqrt(3) * Vb / (1000 * Sb15) with Ib = 18.064 + // in the documentation, expected Idocumentation ~ 35.656 kA + assertEquals(35.69309945355154, val.get(0) * 18.064 * 0.277 * Math.sqrt(3) / (1000. * 15.) / 0.277, 0.00001); + + } + + @Test + void shortCircuitIec31() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B3", true, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc1); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, true, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitBalancedEngine scbEngine = new ShortCircuitBalancedEngine(network, scbParameters); + + scbEngine.run(); + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIk()); + } + + // here Icc = 1/sqrt(3)*Eth(pu)/Zth(pu100)*Sb100/Vb*1000 + // and I"k = 1/sqrt(3) * cmax * Un /(Zeq) and expected I"k = 34.62 kA + assertEquals(34.62398968800272, val.get(0), 0.00001); + + } + + @Test + void shortCircuitIec31TestNetwork() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31testNetwork(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B1", true, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc1); + ShortCircuitFault sc2 = new ShortCircuitFault("B2", true, "sc2", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc2); + ShortCircuitFault sc3 = new ShortCircuitFault("B3", true, "sc3", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc3); + ShortCircuitFault sc4 = new ShortCircuitFault("B4", true, "sc4", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc4); + ShortCircuitFault sc5 = new ShortCircuitFault("B5", true, "sc5", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc5); + ShortCircuitFault sc6 = new ShortCircuitFault("B6", true, "sc6", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc6); + ShortCircuitFault sc7 = new ShortCircuitFault("B7", true, "sc7", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc7); + ShortCircuitFault sc8 = new ShortCircuitFault("B8", true, "sc8", 0., 0., ShortCircuitFault.ShortCircuitType.TRIPHASED_GROUND); + faultList.add(sc8); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, true, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitBalancedEngine scbEngine = new ShortCircuitBalancedEngine(network, scbParameters); + + scbEngine.run(); + List values = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + values.add(res.getValue().getIk()); + } + + // I"k = 1/sqrt(3) * cmax * Un /(Zeq) + assertEquals(40.64478476116188, values.get(0), 0.001); // bus 1 : expected in doc = 40.6447 kA + assertEquals(31.783052222534174, values.get(1), 0.001); // bus 2 : expected in doc = 31.7831 kA + assertEquals(19.672955775750143, values.get(2), 0.001); // bus 3 : expected in doc = 19.673 kA + assertEquals(16.227655866910894, values.get(3), 0.001); // bus 4 : expected in doc = 16.2277 kA + assertEquals(33.18941481677016, values.get(4), 0.001); // bus 5 : expected in doc = 33.1894 kA + assertEquals(37.56287899040728, values.get(5), 0.001); // bus 6 : expected in doc = 37.5629 kA + assertEquals(25.589463480212533, values.get(6), 0.001); // bus 7 : expected in doc = 25.5895 kA + assertEquals(13.577771545200052, values.get(7), 0.001); // bus 8 : expected in doc = 13.5778 kA + + //assertEquals(4039.8610235151364, values.get(8), 0.1); // T3 U0 node : for check only + //assertEquals(4039.8610235151364, values.get(8), 0.1); // T4 U0 node : for check only + + } + + public static Network create2n(NetworkFactory networkFactory) { + Objects.requireNonNull(networkFactory); + + double p0l2 = 10; + double q0l2 = 10; + double pgen = 10; + double xl = 2.; + + Network network = networkFactory.createNetwork("2n", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substation1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1.newVoltageLevel() + .setId("VL_1") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(100.0).setAngle(0.); + Generator gen1 = vl1.newGenerator() + .setId("G1") + .setBus(bus1.getId()) + .setMinP(0.0) + .setMaxP(150) + .setTargetP(pgen) + .setTargetV(100.0) + .setVoltageRegulatorOn(true) + .add(); + + gen1.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(20) + .withDirectTransX(20) + .withStepUpTransformerX(0.) + .add(); + + Substation substation2 = network.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = substation2.newVoltageLevel() + .setId("VL_2") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(100.0).setAngle(0); + vl2.newLoad() + .setId("LOAD_2") + .setBus(bus2.getId()) + .setP0(p0l2) + .setQ0(q0l2) + .add(); + + network.newLine() + .setId("B1_B2") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl2.getId()) + .setBus2(bus2.getId()) + .setConnectableBus2(bus2.getId()) + .setR(0.0) + .setX(xl) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + return network; + } + + public static Network create4n(NetworkFactory networkFactory) { + Objects.requireNonNull(networkFactory); + // 2 3 + // (~)-|--------------X23--------------|-[X] Po= 10. Qo = 100. + // |--+ +--| + // | / |--+ + // | / | + // | / | + // X12 / | + // | +-----X13-----+ X34 + // | / | + // 1 | / | + // |--+ / | + // |-----+ |--+ + // +--|--------------X14--------------|-----[X] Po= 20. Qo = 10. + // | |--B4 + // B1 + + Network network = networkFactory.createNetwork("4n", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substation1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1.newVoltageLevel() + .setId("VL_1") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(100.0).setAngle(0.); + // test with shunt (could be removed) + vl1.newShuntCompensator() + .setId("SHUNT_1") + .setBus(bus1.getId()) + .setSectionCount(1) + .setVoltageRegulatorOn(false) + .newLinearModel().setMaximumSectionCount(1).setBPerSection(-0.003).add() + .add(); + + Substation substation2 = network.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = substation2.newVoltageLevel() + .setId("VL_2") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(100.0).setAngle(0); + Generator gen2 = vl2.newGenerator() + .setId("G2") + .setBus(bus2.getId()) + .setMinP(0.0) + .setMaxP(150) + .setTargetP(30) + .setTargetV(100.0) + .setVoltageRegulatorOn(true) + .add(); + gen2.newExtension(GeneratorShortCircuitAdder.class) + .withDirectTransX(20.) + .withDirectSubtransX(20.) + .withStepUpTransformerX(0.) + .add(); + + Substation substation3 = network.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = substation3.newVoltageLevel() + .setId("VL_3") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(100.0).setAngle(0.); + vl3.newLoad() + .setId("LOAD_3") + .setBus(bus3.getId()) + .setP0(10.0) + .setQ0(100.) + .add(); + + Substation substation4 = network.newSubstation() + .setId("S4") + .setCountry(Country.FR) + .add(); + VoltageLevel vl4 = substation4.newVoltageLevel() + .setId("VL_4") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus4 = vl4.getBusBreakerView().newBus() + .setId("B4") + .add(); + bus4.setV(100.0).setAngle(0.); + vl4.newShuntCompensator() + .setId("SHUNT_4") + .setBus(bus4.getId()) + .setSectionCount(1) + .setVoltageRegulatorOn(false) + .newLinearModel().setMaximumSectionCount(1).setBPerSection(-0.00105).add() + .add(); + vl4.newLoad() + .setId("LOAD_4") + .setBus(bus4.getId()) + .setP0(20.) + .setQ0(10.) + .add(); + + network.newLine() + .setId("B1_B2") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl2.getId()) + .setBus2(bus2.getId()) + .setConnectableBus2(bus2.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B3") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B4") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B2_B3") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.6) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B3_B4") + .setVoltageLevel1(vl3.getId()) + .setBus1(bus3.getId()) + .setConnectableBus1(bus3.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + return network; + } + + public static Network create2nTfo(NetworkFactory networkFactory) { + Objects.requireNonNull(networkFactory); + + double p0l2 = 10; + double q0l2 = 10; + double pGen = 10; + double xl = 2.; + + Network network = networkFactory.createNetwork("2nTfo", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substation1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1.newVoltageLevel() + .setId("VL_1") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(100.0).setAngle(0.); + + Generator gen1 = vl1.newGenerator() + .setId("G1") + .setBus(bus1.getId()) + .setMinP(0.0) + .setMaxP(150) + .setTargetP(pGen) + .setTargetV(100.0) + .setVoltageRegulatorOn(true) + .add(); + + gen1.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(20) + .withDirectTransX(20) + .withStepUpTransformerX(0.) + .add(); + + VoltageLevel vl2 = substation1.newVoltageLevel() + .setId("VL_2") + .setNominalV(150.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(150.0).setAngle(0); + vl2.newLoad() + .setId("LOAD_2") + .setBus(bus2.getId()) + .setP0(p0l2) + .setQ0(q0l2) + .add(); + + TwoWindingsTransformer t2w = substation1.newTwoWindingsTransformer() + .setId("B1_B2") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl2.getId()) + .setBus2(bus2.getId()) + .setConnectableBus2(bus2.getId()) + .setR(0.0) + .setX(xl) + .setRatedU1(100.0) + .setRatedU2(150.0) + .setG(0.0) + .setB(0.0) + .add(); + + return network; + } +} diff --git a/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBiphasedGroundTest.java b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBiphasedGroundTest.java new file mode 100644 index 00000000..5c99f337 --- /dev/null +++ b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBiphasedGroundTest.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import org.apache.commons.math3.util.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitBiphasedGroundTest { + + private LoadFlowParameters parameters; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + parameters = new LoadFlowParameters(); + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + @Test + void shortCircuitIec31Mono() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B3", false, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED_GROUND); + faultList.add(sc1); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, false, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitUnbalancedEngine scbEngine = new ShortCircuitUnbalancedEngine(network, scbParameters); + + scbEngine.run(); + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIk()); + } + + assertEquals(52.3687065865033, 3 * val.get(0), 0.00001); + + } +} diff --git a/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBiphasedTest.java b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBiphasedTest.java new file mode 100644 index 00000000..984ebd67 --- /dev/null +++ b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitBiphasedTest.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import org.apache.commons.math3.util.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitBiphasedTest { + + private LoadFlowParameters parameters; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + parameters = new LoadFlowParameters(); + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + @Test + void shortCircuitIec31Mono() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B3", false, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED); + faultList.add(sc1); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, false, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitUnbalancedEngine scbEngine = new ShortCircuitUnbalancedEngine(network, scbParameters); + + scbEngine.run(); + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIk()); + } + + assertEquals(67.51864695211795, 3 * val.get(0), 0.00001); + + } + +} diff --git a/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitMonophasedTest.java b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitMonophasedTest.java new file mode 100644 index 00000000..a0b4fb0c --- /dev/null +++ b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShortCircuitMonophasedTest.java @@ -0,0 +1,343 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import org.apache.commons.math3.util.Pair; +import org.joda.time.DateTime; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitMonophasedTest { + + private LoadFlowParameters parameters; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + parameters = new LoadFlowParameters(); + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + @Test + void shortCircuitIec31Mono() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B3", false, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.MONOPHASED); + faultList.add(sc1); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, false, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitUnbalancedEngine scbEngine = new ShortCircuitUnbalancedEngine(network, scbParameters); + + scbEngine.run(); + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIk()); + } + + // here Icc = 1/sqrt(3)*Eth(pu)/Zth(pu100)*Sb100/Vb*1000 + // here new Icc = 1/sqrt(3)*Eth(pu)/Zth(pu100)*Sb100*1000 + // and I"k = sqrt(3) * cmax * Un /(Zeq) and expected I"k = 35.64 kA with some approximations on the impedance values + + //assertEquals(35.70435548244156, 3 * val.get(0) * 1.05 / 1000. / 0.4, 0.00001); + assertEquals(35.70435548244156, 3 * val.get(0), 0.00001); + + } + + @Test + void shortCircuitIecTestNetworkMono() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31testNetwork(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B2", false, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.MONOPHASED); + faultList.add(sc1); + ShortCircuitFault sc2 = new ShortCircuitFault("B3", false, "sc2", 0., 0., ShortCircuitFault.ShortCircuitType.MONOPHASED); + faultList.add(sc2); + ShortCircuitFault sc3 = new ShortCircuitFault("B4", false, "sc3", 0., 0., ShortCircuitFault.ShortCircuitType.MONOPHASED); + faultList.add(sc3); + ShortCircuitFault sc4 = new ShortCircuitFault("B5", false, "sc4", 0., 0., ShortCircuitFault.ShortCircuitType.MONOPHASED); + faultList.add(sc4); + + // additional faults + ShortCircuitFault sc5 = new ShortCircuitFault("B2", false, "sc5", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED); + faultList.add(sc5); + ShortCircuitFault sc6 = new ShortCircuitFault("B3", false, "sc6", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED_GROUND); + faultList.add(sc6); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, false, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitUnbalancedEngine scbEngine = new ShortCircuitUnbalancedEngine(network, scbParameters); + + scbEngine.run(); + Map values = new HashMap<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + values.put(res.getKey().getFaultId(), res.getValue().getIk()); + } + + //I"k = sqrt(3) * cmax * Un /(Zeq) + assertEquals(15.9722, 3 * values.get("sc1"), 0.00001); // bus 2 : expected doc value : 15.9722 kA + assertEquals(10.410558286260768, 3 * values.get("sc2"), 0.00001); // bus 3 : expected doc value : 10.4106 kA + assertEquals(9.049787523396647, 3 * values.get("sc3"), 0.00001); // bus 4 : expected doc value : 9.0498 kA + assertEquals(17.0452, 3 * values.get("sc4"), 0.00001); // bus 5 : expected doc value : 17.0452 kA + + // test to check that non monophased faults does not have an impact on monophased results + assertEquals(57.48674948061683, 3 * values.get("sc5"), 0.00001); // biphased not in ref doc + assertEquals(25.21494837446988, 3 * values.get("sc6"), 0.00001); // biphased not in ref doc + + } + + @Test + void computeIoTestNew() { + Pair result = createGiard(NetworkFactory.findDefault()); + + Network network = result.getKey(); + AdditionalDataInfo additionalDataInfo = result.getValue(); + + LoadFlowResult resultntg = loadFlowRunner.run(network, parameters); + + Map machineToGround = new HashMap<>(); //true if the generating unit has its neutral linked to the ground + machineToGround.put("GA", false); + machineToGround.put("GB", true); + + additionalDataInfo.setMachineToGround(machineToGround); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("BP", true, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.MONOPHASED); + + faultList.add(sc1); + + LoadFlowParameters loadFlowParameters = new LoadFlowParameters(); + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.TRANSIENT; + ShortCircuitNormCourcirc shortCircuitNormCourcirc = new ShortCircuitNormCourcirc(); + ShortCircuitEngineParameters scunbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, true, ShortCircuitEngineParameters.VoltageProfileType.CALCULATED, false, periodType, additionalDataInfo, shortCircuitNormCourcirc); + ShortCircuitUnbalancedEngine scunbEngine = new ShortCircuitUnbalancedEngine(network, scunbParameters); + + scunbEngine.run(); + + //assertEquals(2.8286512112174034, scunbEngine.results.get(sc1).getIox() / Math.sqrt(3), 0.000001); // results changed with modification of input data + //assertEquals(1.6331225382399293, scunbEngine.results.get(sc1).getIoy() / Math.sqrt(3), 0.000001); + assertEquals(2.7099928109273916, scunbEngine.resultsPerFault.get(sc1).getIox() / Math.sqrt(3), 0.000001); + assertEquals(1.5646150788908801, scunbEngine.resultsPerFault.get(sc1).getIoy() / Math.sqrt(3), 0.000001); + + } + + public static Pair createGiard(NetworkFactory networkFactory) { + + // M1 P M2 machine M1: isolated neutral with X'd1, Xmi1, Xmo1 + // (~)-|---Xd1---|---Xd2---|--(~) machine M2: grounded neutral through Xn, with X'd2, Xmi2, Xmo2 + // Xi1 Xi2 | In P, there is a direct monophased fault to the ground (phase 1) + // Xo1 Xo2 Xn + // | + // ///// + // + // Direct schema: + // P + // --X'd1--|---Xd1---|---Xd2---|--X'd2-- Xd'1 = 65 ohms Xd'2 = 130 ohms + // ^ ^ Xd1 = 30 ohms Xd2 = 15 ohms + // | Ed1 | Ed2 + // | | + // /// /// + // + // Inverse P + // --Xmi1--|---Xi1---|---Xi2---|--Xmi2-- Xi = Xd + // | | + // | | + // /// /// + // + // Homopolar P + // --XmO1--|---Xo1---|---Xo2---|--Xmo2-- Xmo1 = Xd'1 / 3 + // | Xmo2 = Xd'2 / 3 + // 3.Xn Xo1 = 3 * Xd1 + // | Xo2 = 3 * Xd2 + // /// Xn = 25 ohms + + Objects.requireNonNull(networkFactory); + + double pgen = 0.; + double xd1 = 30.; + double xd2 = 15.; + double coeffXo1 = 3.; + double coeffXo2 = 3.; + double uac = 420.; + double ubc = 410.; + + Network network = networkFactory.createNetwork("Giard exemple", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substationA = network.newSubstation() + .setId("SA") + .setCountry(Country.FR) + .add(); + VoltageLevel vlA = substationA.newVoltageLevel() + .setId("VL_A") + .setNominalV(380.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(500) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus busA = vlA.getBusBreakerView().newBus() + .setId("BA") + .add(); + busA.setV(uac).setAngle(0.); + Generator genA = vlA.newGenerator() + .setId("GA") + .setBus(busA.getId()) + .setMinP(-10.0) + .setMaxP(150) + .setTargetP(pgen) + .setTargetV(uac) + .setVoltageRegulatorOn(true) + .add(); + + genA.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(65) + .withDirectTransX(65) + .withStepUpTransformerX(0.) + .add(); + + Substation substationP = network.newSubstation() + .setId("SP") + .setCountry(Country.FR) + .add(); + VoltageLevel vlP = substationP.newVoltageLevel() + .setId("VL_P") + .setNominalV(380.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(500) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus busP = vlP.getBusBreakerView().newBus() + .setId("BP") + .add(); + busP.setV(413.0).setAngle(0); + + Substation substationB = network.newSubstation() + .setId("SB") + .setCountry(Country.FR) + .add(); + VoltageLevel vlB = substationB.newVoltageLevel() + .setId("VL_B") + .setNominalV(380.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(500) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus busB = vlB.getBusBreakerView().newBus() + .setId("BB") + .add(); + busB.setV(ubc).setAngle(0.); + Generator genB = vlB.newGenerator() + .setId("GB") + .setBus(busB.getId()) + .setMinP(-10.0) + .setMaxP(150) + .setTargetP(pgen) + .setTargetV(ubc) + .setVoltageRegulatorOn(true) + .add(); + + genB.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(130) + .withDirectTransX(130) + .withStepUpTransformerX(0.) + .add(); + + network.newLine() + .setId("BA_BP") + .setVoltageLevel1(vlA.getId()) + .setBus1(busA.getId()) + .setConnectableBus1(busA.getId()) + .setVoltageLevel2(vlP.getId()) + .setBus2(busP.getId()) + .setConnectableBus2(busP.getId()) + .setR(0.0) + .setX(xd1) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("BP_BB") + .setVoltageLevel1(vlP.getId()) + .setBus1(busP.getId()) + .setConnectableBus1(busP.getId()) + .setVoltageLevel2(vlB.getId()) + .setBus2(busB.getId()) + .setConnectableBus2(busB.getId()) + .setR(0.0) + .setX(xd2) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + Map machineToTransRd = new HashMap<>(); //map to store the real part of the transient impedance + Map machineToSubTransRd = new HashMap<>(); + Map machineToGround = new HashMap<>(); + + Map leg1type = new HashMap<>(); + Map leg2type = new HashMap<>(); + + Map lineIdToCoeffRo = new HashMap<>(); + Map lineIdToCoeffXo = new HashMap<>(); + Map tfo2wIdToCoeffRo = new HashMap<>(); + Map tfo2wIdToCoeffXo = new HashMap<>(); + + lineIdToCoeffXo.put("BA_BP", coeffXo1); + lineIdToCoeffXo.put("BP_BB", coeffXo2); + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(machineToTransRd, machineToSubTransRd, machineToGround, + leg1type, leg2type, lineIdToCoeffRo, lineIdToCoeffXo, tfo2wIdToCoeffRo, tfo2wIdToCoeffXo); + + Pair result = new Pair<>(network, additionalDataInfo); + return result; + } +} diff --git a/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShotCircuitBiphasedCommonSupportTest.java b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShotCircuitBiphasedCommonSupportTest.java new file mode 100644 index 00000000..df050f60 --- /dev/null +++ b/simulator/short-circuit/src/test/java/com/powsybl/incubator/simulator/shortcircuit/ShotCircuitBiphasedCommonSupportTest.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.shortcircuit; + +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.*; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import org.apache.commons.math3.util.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShotCircuitBiphasedCommonSupportTest { + + private LoadFlowParameters parameters; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + parameters = new LoadFlowParameters(); + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + @Test + void shortCircuitIec31BiphasedCommonSupport() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B2", "B3", true, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT, ShortCircuitFault.ShortCircuitBiphasedType.C1_A2); + faultList.add(sc1); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, true, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitUnbalancedEngine scbEngine = new ShortCircuitUnbalancedEngine(network, scbParameters); + + scbEngine.run(); + + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIk()); + } + + assertEquals(31.16265030753145, 3 * val.get(0), 0.00001); // TODO : check manually result + + } + + @Test + void shortCircuitIec31MultiBiphasedCommonSupport() { + + LoadFlowParameters loadFlowParameters = LoadFlowParameters.load(); + loadFlowParameters.setTwtSplitShuntAdmittance(true); + + Pair result = ReferenceNetwork.createShortCircuitIec31(); + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + MatrixFactory matrixFactory = new DenseMatrixFactory(); + + List faultList = new ArrayList<>(); + ShortCircuitFault sc1 = new ShortCircuitFault("B2", "B3", true, "sc1", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT, ShortCircuitFault.ShortCircuitBiphasedType.C1_B2); + ShortCircuitFault sc2 = new ShortCircuitFault("B4", "B5", true, "sc2", 0., 0., ShortCircuitFault.ShortCircuitType.BIPHASED_COMMON_SUPPORT, ShortCircuitFault.ShortCircuitBiphasedType.C1_C2); + // TODO : a list that contains BIPHASED_COMMON_SUPPORT with the same nodes is not supported yet : FIX_ME + faultList.add(sc1); + faultList.add(sc2); + + ShortCircuitEngineParameters.PeriodType periodType = ShortCircuitEngineParameters.PeriodType.SUB_TRANSIENT; + ShortCircuitNormIec shortCircuitNormIec = new ShortCircuitNormIec(); + ShortCircuitEngineParameters scbParameters = new ShortCircuitEngineParameters(loadFlowParameters, matrixFactory, ShortCircuitEngineParameters.AnalysisType.SELECTIVE, faultList, true, ShortCircuitEngineParameters.VoltageProfileType.NOMINAL, false, periodType, additionalDataInfo, shortCircuitNormIec); + ShortCircuitUnbalancedEngine scbEngine = new ShortCircuitUnbalancedEngine(network, scbParameters); + + scbEngine.run(); + + List val = new ArrayList<>(); + for (Map.Entry res : scbEngine.resultsPerFault.entrySet()) { + val.add(res.getValue().getIk()); + } + + assertEquals(31.16265030753145, 3 * val.get(0), 0.00001); // TODO : check manually result + assertEquals(0., 3 * val.get(1), 0.00001); // TODO : check manually result + + } + +} diff --git a/simulator/util/pom.xml b/simulator/util/pom.xml new file mode 100644 index 00000000..6bd03897 --- /dev/null +++ b/simulator/util/pom.xml @@ -0,0 +1,68 @@ + + + + 4.0.0 + + + powsybl-incubator-simulator + com.powsybl + 1.0.0-SNAPSHOT + + + powsybl-incubator-simulator-util + Simulator utilities + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + + + + com.powsybl + powsybl-open-loadflow + + + + com.powsybl + powsybl-iidm-impl + test + + + com.powsybl + powsybl-config-test + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.slf4j + slf4j-simple + test + + + + \ No newline at end of file diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AbstractAdmittanceEquationTerm.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AbstractAdmittanceEquationTerm.java new file mode 100644 index 00000000..5067c56c --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AbstractAdmittanceEquationTerm.java @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.equations.AbstractNamedEquationTerm; +import com.powsybl.openloadflow.equations.Variable; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.ElementType; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.PiModel; + +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public abstract class AbstractAdmittanceEquationTerm extends AbstractNamedEquationTerm implements LinearEquationTerm { + + private final LfBranch branch; + + protected final Variable v1rVar; + + protected final Variable v1iVar; + + protected final Variable v2rVar; + + protected final Variable v2iVar; + + protected final List> variables; + + protected double rho; + + protected double zInvSquare; + + protected double r; + + protected double x; + + protected double cosA; + + protected double sinA; + + protected double cos2A; + + protected double sin2A; + + protected double gPi1; + + protected double bPi1; + + protected double gPi2; + + protected double bPi2; + + // zero sequence additional attributes + // + // Proposed Transformer model : + // Ia Yg A' rho B' Yg Ib Zga : grounding impedance on A side (in ohms expressed on A side) + // A-->--3*Zga--+ +--(())--+--Zoa--+--Zob--+ +--3*ZGb--<--B Zoa : leakage impedance of A-winding (in ohms expressed on B side) + // Y + | + Y Zob : leakage impdedance of B-winding (in ohms expressed on B side) + // + D Zom + D Zom : magnetizing impedance of the two windings (in ohms expressed on B side) + // | | | Zgb : grounding impedance on B side (in ohms expressed on B side) + // | | | rho might be a complex value + // | free fluxes \ | + // | | | + // ///// ///// ///// A' and B' are connected to Yg, Y or D depending on the winding connection type (Y to ground, Y or Delta) + // + protected AbstractAdmittanceEquationTerm(LfBranch branch, LfBus bus1, LfBus bus2, VariableSet variableSet) { + this.branch = Objects.requireNonNull(branch); + Objects.requireNonNull(bus1); + Objects.requireNonNull(bus2); + Objects.requireNonNull(variableSet); + + v1rVar = variableSet.getVariable(bus1.getNum(), VariableType.BUS_VR); + v2rVar = variableSet.getVariable(bus2.getNum(), VariableType.BUS_VR); + v1iVar = variableSet.getVariable(bus1.getNum(), VariableType.BUS_VI); + v2iVar = variableSet.getVariable(bus2.getNum(), VariableType.BUS_VI); + + variables = List.of(v1rVar, v2rVar, v1iVar, v2iVar); + + PiModel piModel = branch.getPiModel(); + if (piModel.getX() == 0) { + throw new IllegalArgumentException("Branch '" + branch.getId() + "' has reactance equal to zero"); + } + rho = piModel.getR1(); + if (piModel.getZ() == 0) { + throw new IllegalArgumentException("Branch '" + branch.getId() + "' has Z equal to zero"); + } + zInvSquare = 1 / (piModel.getZ() * piModel.getZ()); + r = piModel.getR(); + x = piModel.getX(); + double alpha = piModel.getA1(); + cosA = Math.cos(Math.toRadians(alpha)); + sinA = Math.sin(Math.toRadians(alpha)); + cos2A = Math.cos(Math.toRadians(2 * alpha)); + sin2A = Math.sin(Math.toRadians(2 * alpha)); + + gPi1 = piModel.getG1(); + bPi1 = piModel.getB1(); + gPi2 = piModel.getG2(); + bPi2 = piModel.getB2(); + } + + @Override + public List> getVariables() { + return variables; + } + + @Override + public ElementType getElementType() { + return ElementType.BRANCH; + } + + @Override + public int getElementNum() { + return branch.getNum(); + } + + @Override + public double eval() { + throw new UnsupportedOperationException("Not needed"); + } + + @Override + public double der(Variable variable) { + throw new UnsupportedOperationException("Not needed"); + } + + @Override + public boolean hasRhs() { + return false; + } + + @Override + public double rhs() { + return 0; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceConstants.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceConstants.java new file mode 100644 index 00000000..fd1891f2 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceConstants.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +/** + * @author Geoffroy Jamgotchian {@literal } + */ +public final class AdmittanceConstants { + + private AdmittanceConstants() { + } + + public static final double XN_PU = 0.0173; //default value if data not available + + public static final double COEF_XO_XD = 0.33; // xd/xo = 1/3 and xmo/x"d = 1/3 + + public static final double INFINITE_IMPEDANCE_ADMITTANCE_VALUE = 0.00000001; + // This value represents the case where have a hompoloar transformer conecting two different connex areas through an infinite impedance. + // This may create a singular matrix. As a consequence we replace the zero admittance value by a very small one. +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationSystem.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationSystem.java new file mode 100644 index 00000000..70577cf3 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationSystem.java @@ -0,0 +1,292 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.incubator.simulator.util.extensions.ShortCircuitExtensions; +import com.powsybl.incubator.simulator.util.extensions.ShortCircuitGenerator; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowContext; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import com.powsybl.openloadflow.ac.outerloop.AcloadFlowEngine; +import com.powsybl.openloadflow.equations.EquationSystem; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.*; +import net.jafama.FastMath; +import org.apache.commons.math3.util.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Jean-Baptiste Heyberger + */ +public final class AdmittanceEquationSystem { + + private static final Logger LOGGER = LoggerFactory.getLogger(AdmittanceEquationSystem.class); + + private static final double SB = 100.; + + private static final double EPSILON = 0.00000001; + + private AdmittanceEquationSystem() { + } + + //Equations are created based on the branches connections + private static void createImpedantBranch(VariableSet variableSet, EquationSystem equationSystem, + LfBranch branch, LfBus bus1, LfBus bus2, AdmittanceType admittanceType) { + if (bus1 != null && bus2 != null) { //TODO: check case when one bus is OK + // Equation system Y*V = I (expressed in cartesian coordinates x,y) + equationSystem.createEquation(bus1.getNum(), EquationType.BUS_YR) + .addTerm(new AdmittanceEquationTermX1(branch, bus1, bus2, variableSet, admittanceType)); + + equationSystem.createEquation(bus1.getNum(), EquationType.BUS_YI) + .addTerm(new AdmittanceEquationTermY1(branch, bus1, bus2, variableSet, admittanceType)); + + equationSystem.createEquation(bus2.getNum(), EquationType.BUS_YR) + .addTerm(new AdmittanceEquationTermX2(branch, bus1, bus2, variableSet, admittanceType)); + + equationSystem.createEquation(bus2.getNum(), EquationType.BUS_YI) + .addTerm(new AdmittanceEquationTermY2(branch, bus1, bus2, variableSet, admittanceType)); + } + } + + public enum AdmittanceVoltageProfileType { + CALCULATED, // use the computed values at nodes to compute Y elements + NOMINAL; // use the nominal voltage values at nodes to get Y elements + } + + public enum AdmittanceType { + //TODO : adapt for the different kinds of admittance matrices + ADM_INJ, // all external nodal injections that does not come from branches are considered as current injectors (including shunts elements) + ADM_SHUNT, // all external nodal injections that does not come from branches are considered as current injectors (but not shunt elements) + ADM_ADMIT, // all external nodal injections are transformed into passive shunt elements included in the Y matrix (then [Ie] should be [0]) + ADM_THEVENIN, // used to compute the Zth Thevenin Equivalent: shunts remain shunts, synchronous machines are transformed into X" equivalent shunts, remaining injections are transformed into passive shunt elements included in the Y matrix + ADM_THEVENIN_HOMOPOLAR; // used to compute the homopolar admittance matrix for unbalanced short circuits + } + + public enum AdmittancePeriodType { + ADM_SUB_TRANSIENT, // all external nodal injections that does not come from branches are considered as current injectors (including shunts elements) + ADM_TRANSIENT, // all external nodal injections that does not come from branches are considered as current injectors (but not shunt elements) + ADM_STEADY_STATE, // all external nodal injections are transformed into passive shunt elements included in the Y matrix (then [Ie] should be [0]) + } + + private static void createBranches(LfNetwork network, VariableSet variableSet, EquationSystem equationSystem, AdmittanceType admittanceType) { + for (LfBranch branch : network.getBranches()) { + LfBus bus1 = branch.getBus1(); + LfBus bus2 = branch.getBus2(); + PiModel piModel = branch.getPiModel(); + if (FastMath.abs(piModel.getX()) < LfBranch.LOW_IMPEDANCE_THRESHOLD) { + if (bus1 != null && bus2 != null) { + LOGGER.warn("Warning: Branch = {} : Non impedant lines not supported in the current version of the reduction method", + branch.getId()); + } + } else { + //System.out.println("X(" + branch.getId() + ")= " + piModel.getX()); + createImpedantBranch(variableSet, equationSystem, branch, bus1, bus2, admittanceType); + } + } + } + + private static double getBfromShunt(LfBus bus) { + List feederList = new ArrayList<>(); + return getBfromShunt(bus, feederList); + } + + private static double getBfromShunt(LfBus bus, List feederList) { + LfShunt shunt = bus.getShunt().orElse(null); + double tmpB = 0.; + if (shunt != null) { + tmpB += shunt.getB(); + EquationSystemFeeder shuntFeeder = new EquationSystemFeeder(shunt.getB(), 0., shunt.getId(), EquationSystemFeeder.FeederType.SHUNT); + feederList.add(shuntFeeder); + //check if g will be implemented + } + LfShunt controllerShunt = bus.getControllerShunt().orElse(null); + if (controllerShunt != null) { + tmpB += controllerShunt.getB(); + EquationSystemFeeder shuntFeeder = new EquationSystemFeeder(shunt.getB(), 0., shunt.getId(), EquationSystemFeeder.FeederType.CONTROLLED_SHUNT); + feederList.add(shuntFeeder); + //check if g will be implemented + } + + return tmpB; + } + + private static Pair getYtransfromRdXd(LfBus bus, AdmittancePeriodType admittancePeriodType, List feederList, AdmittanceType admittanceType) { + double vnomVl = bus.getNominalV(); + + double tmpG = 0.; + double tmpB = 0.; + for (LfGenerator lfgen : bus.getGenerators()) { //compute R'd or R"d from generators at bus + ShortCircuitGenerator scGen = (ShortCircuitGenerator) lfgen.getProperty(ShortCircuitExtensions.PROPERTY_NAME); + double rd = scGen.getTransRd() + scGen.getStepUpTfoR(); + double xd = scGen.getTransXd() + scGen.getStepUpTfoX(); + if (admittancePeriodType == AdmittancePeriodType.ADM_SUB_TRANSIENT) { + xd = scGen.getSubTransXd() + scGen.getStepUpTfoX(); + rd = scGen.getSubTransRd() + scGen.getStepUpTfoR(); + } + + double coeffR = 1.0; // coeff used to deduce Ro from Rd. It is equal to 1.0 if we are looking for direct values. If the machine is not grounded, homopolar values are zero, then we set coeffs to 0. + double coeffX = 1.0; + if (admittanceType == AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + coeffR = 0.; + coeffX = 0.; + if (scGen.isGrounded()) { + coeffR = scGen.getCoeffRo(); + coeffX = scGen.getCoeffXo(); + } + } + + double epsilon = 0.0000001; + + rd = rd * coeffR; + xd = xd * coeffX; + + if (Math.abs(rd) > epsilon || Math.abs(xd) > epsilon) { + double gGen = (vnomVl * vnomVl / SB) * rd / (rd * rd + xd * xd); + double bGen = -(vnomVl * vnomVl / SB) * xd / (rd * rd + xd * xd); + tmpG = tmpG + gGen; + tmpB = tmpB + bGen; // TODO: check: for now X'd = 0 not allowed + EquationSystemFeeder shuntFeeder = new EquationSystemFeeder(bGen, gGen, lfgen.getId(), EquationSystemFeeder.FeederType.GENERATOR); + feederList.add(shuntFeeder); + } + } + + Pair result = new Pair<>(tmpG, tmpB); + return result; + } + + private static void createShunts(LfNetwork network, VariableSet variableSet, EquationSystem equationSystem, AdmittanceType admittanceType, + AdmittanceVoltageProfileType admittanceVoltageProfileType, AdmittancePeriodType admittancePeriodType, + boolean isShuntsIgnore, EquationSystemFeeders feeders) { + for (LfBus bus : network.getBuses()) { + + //total shunt at bus to be integrated in the admittance matrix + double g = 0.; + double b = 0.; + + //shunts created to represent the equivalence of loads and to be integrated in the total admittance matrix shunt at bus + double gLoadEq = 0.; + double bLoadEq = 0.; + + //shunts created to represent the equivalence of generating units sand to be integrated in the total admittance matrix shunt at bus + double gGenEq = 0.; + double bGenEq = 0.; + + //choice of vbase to be used to transform power injections into equivalent shunts + double vr = bus.getV() * Math.cos(bus.getAngle()); + double vi = bus.getV() * Math.sin(bus.getAngle()); + if (admittanceVoltageProfileType == AdmittanceVoltageProfileType.NOMINAL) { + vr = 1.0; //TODO: check if Vnom or Vnom/Vbase + vi = 0.; + } + boolean isBusPv = bus.isVoltageControlled(); + + if (admittanceType == AdmittanceType.ADM_SHUNT) { + if (!isShuntsIgnore) { + b = getBfromShunt(bus); // Handling shunts that physically exist + } + } else if (admittanceType == AdmittanceType.ADM_ADMIT) { + if (!isShuntsIgnore) { + b = getBfromShunt(bus); // Handling shunts that physically exist + } + gLoadEq = bus.getLoadTargetP() / (vr * vr + vi * vi); // Handling transformation of bus loads into equivalent shunts + bLoadEq = -bus.getLoadTargetQ() / (vr * vr + vi * vi); + // Handling transformation of generators into equivalent shunts + // Warning !!! : evaluation of power injections mandatory + gGenEq = -(bus.getP().eval() + bus.getLoadTargetP()) / (vr * vr + vi * vi); // full nodal P injection without the load + + if (isBusPv) { + bGenEq = (bus.getQ().eval() + bus.getLoadTargetQ()) / (vr * vr + vi * vi); // full nodal Q injection without the load + } else { + bGenEq = bus.getGenerationTargetQ() / (vr * vr + vi * vi); + } + } else if (admittanceType == AdmittanceType.ADM_THEVENIN) { + + List feederList = new ArrayList<>(); + + if (!isShuntsIgnore) { + // Handling shunts that physically exist + b = getBfromShunt(bus, feederList); // ! updates feederList + } + gLoadEq = bus.getLoadTargetP() / (vr * vr + vi * vi); // Handling transformation of bus loads into equivalent shunts + bLoadEq = -bus.getLoadTargetQ() / (vr * vr + vi * vi); + + EquationSystemFeeder shuntFeeder = new EquationSystemFeeder(bLoadEq, gLoadEq, bus.getId(), EquationSystemFeeder.FeederType.LOAD); + feederList.add(shuntFeeder); + + Pair bAndG = getYtransfromRdXd(bus, admittancePeriodType, feederList, admittanceType); // ! updates feederList + bGenEq = bAndG.getValue(); //TODO : check how the verify that the generators are operating + gGenEq = bAndG.getKey(); + + //shortCircuitNetwork.busToFeeder.put(bus, feederList); + EquationSystemBusFeeders shortCircuitEquationSystemBusFeeders = new EquationSystemBusFeeders(feederList, bus); + feeders.busToFeeders.put(bus, shortCircuitEquationSystemBusFeeders); + + } else if (admittanceType == AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + + List feederList = new ArrayList<>(); // not used yet in homopolar + + Pair bAndG = getYtransfromRdXd(bus, admittancePeriodType, feederList, admittanceType); // ! updates feederList + bGenEq = bAndG.getValue(); //TODO : check how the verify that the generators are operating + gGenEq = bAndG.getKey(); + + } + + g = g + gLoadEq + gGenEq; + b = b + bLoadEq + bGenEq; + + //System.out.println("result = g(" + bus.getId() + ")= " + g); + //System.out.println("result = b(" + bus.getId() + ")= " + b); + + if (Math.abs(g) > EPSILON || Math.abs(b) > EPSILON) { + equationSystem.createEquation(bus.getNum(), EquationType.BUS_YR) + .addTerm(new AdmittanceEquationTermShunt(g, b, bus, variableSet, true)); + equationSystem.createEquation(bus.getNum(), EquationType.BUS_YI) + .addTerm(new AdmittanceEquationTermShunt(g, b, bus, variableSet, false)); + } + } + } + + public static EquationSystem create(LfNetwork network, VariableSet variableSet, + AdmittanceType admittanceType, AdmittanceVoltageProfileType admittanceVoltageProfileType, + AcLoadFlowParameters acLoadFlowParameters) { + + // Following data Not needed for reduction methods + AdmittanceEquationSystem.AdmittancePeriodType admittancePeriodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_TRANSIENT; //TODO: not relevant for reduction: see how to improve that + EquationSystemFeeders equationsSystemFeeders = new EquationSystemFeeders(); + boolean isShuntsIgnore = false; + + return create(network, variableSet, + admittanceType, admittanceVoltageProfileType, admittancePeriodType, isShuntsIgnore, + equationsSystemFeeders, acLoadFlowParameters); + } + + public static EquationSystem create(LfNetwork network, VariableSet variableSet, + AdmittanceType admittanceType, AdmittanceVoltageProfileType admittanceVoltageProfileType, + AdmittancePeriodType admittancePeriodType, boolean isShuntsIgnore, EquationSystemFeeders feeders, + AcLoadFlowParameters acLoadFlowParameters) { + + EquationSystem equationSystem = new EquationSystem<>(); + + if (admittanceType == AdmittanceType.ADM_ADMIT) { + try (AcLoadFlowContext context = new AcLoadFlowContext(network, acLoadFlowParameters)) { + new AcloadFlowEngine(context) + .run(); + } + } + + createBranches(network, variableSet, equationSystem, admittanceType); + if (admittanceType != AdmittanceType.ADM_INJ) { //shunts created in the admittance matrix are only those that really exist in the network + createShunts(network, variableSet, equationSystem, admittanceType, admittanceVoltageProfileType, admittancePeriodType, isShuntsIgnore, feeders); // TODO : shuntIgnore was set at false + } + + return equationSystem; + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermShunt.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermShunt.java new file mode 100644 index 00000000..202212e4 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermShunt.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.equations.AbstractNamedEquationTerm; +import com.powsybl.openloadflow.equations.Variable; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.ElementType; +import com.powsybl.openloadflow.network.LfBus; + +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class AdmittanceEquationTermShunt extends AbstractNamedEquationTerm implements LinearEquationTerm { + + private final LfBus bus; + + protected final Variable v1rVar; + + protected final Variable v1iVar; + + protected final List> variables; + + protected double g; + + protected double b; + + protected boolean isReal; + // v1r v1i + // | | + //Eq1r - [ y1r1r y1r1i ] [ g -b ] + //Eq1i - [ y1i1r y1i1i ] = [ b g ] = + + public AdmittanceEquationTermShunt(double g, double b, LfBus bus, VariableSet variableSet, boolean isReal) { + this.bus = Objects.requireNonNull(bus); + Objects.requireNonNull(variableSet); + + v1rVar = variableSet.getVariable(bus.getNum(), VariableType.BUS_VR); + v1iVar = variableSet.getVariable(bus.getNum(), VariableType.BUS_VI); + + variables = List.of(v1rVar, v1iVar); + + this.g = g; + this.b = b; + this.isReal = isReal; + + } + + @Override + public ElementType getElementType() { + return ElementType.BUS; + } + + @Override + public int getElementNum() { + return bus.getNum(); + } + + public List> getVariables() { + return variables; + } + + @Override + public double eval() { + throw new UnsupportedOperationException("Not needed"); + } + + @Override + public double der(Variable variable) { + throw new UnsupportedOperationException("Not needed"); + } + + @Override + public boolean hasRhs() { + return false; + } + + @Override + public double rhs() { + return 0; + } + + @Override + public double getCoefficient(Variable variable) { + if (variable.equals(v1rVar)) { + if (isReal) { + return g; + } else { + return b; + } + } else if (variable.equals(v1iVar)) { + if (isReal) { + return -b; + } else { + return g; + } + } else { + throw new IllegalArgumentException("Unknown variable " + variable); + } + } + + @Override + protected String getName() { + return "yshunt"; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermX1.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermX1.java new file mode 100644 index 00000000..72366109 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermX1.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.equations.Variable; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; + +/** + * @author Jean-Baptiste Heyberger + */ +public class AdmittanceEquationTermX1 extends AbstractAdmittanceEquationTerm { + + private final double g12; + + private final double b12; + + private final double g1g12sum; + + private final double b1b12sum; + + public AdmittanceEquationTermX1(LfBranch branch, LfBus bus1, LfBus bus2, VariableSet variableSet, AdmittanceEquationSystem.AdmittanceType admittanceType) { + super(branch, bus1, bus2, variableSet); + // Direct component: + // I1x = (g1 + g12)V1x - (b1 + b12)V1y - g12 * V2x + b12 * V2y + if (admittanceType == AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + HomopolarModel homopolarModel = HomopolarModel.build(branch); + if (branch.getBranchType() == LfBranch.BranchType.LINE) { + // default if branch type is a line + g12 = rho * homopolarModel.getZoInvSquare() * (homopolarModel.getRo() * cosA + homopolarModel.getXo() * sinA); + b12 = -rho * homopolarModel.getZoInvSquare() * (homopolarModel.getXo() * cosA + homopolarModel.getRo() * sinA); + g1g12sum = rho * rho * (homopolarModel.getGom() + homopolarModel.getRo() * homopolarModel.getZoInvSquare()); + b1b12sum = rho * rho * (homopolarModel.getBom() - homopolarModel.getXo() * homopolarModel.getZoInvSquare()); + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_1 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_3) { + // case where branch is part of a transformer + DenseMatrix mo = homopolarModel.computeHomopolarAdmittanceMatrix(); + b1b12sum = -mo.get(0, 1); + g1g12sum = mo.get(0, 0); + b12 = mo.get(0, 3); + g12 = -mo.get(0, 2); + } else { + throw new IllegalArgumentException("branch type not yet handled"); + } + } else { + g12 = rho * zInvSquare * (r * cosA + x * sinA); + b12 = -rho * zInvSquare * (x * cosA + r * sinA); + g1g12sum = rho * rho * (gPi1 + r * zInvSquare); + b1b12sum = rho * rho * (bPi1 - x * zInvSquare); + } + } + + @Override + public double getCoefficient(Variable variable) { + if (variable.equals(v1rVar)) { + return g1g12sum; + } else if (variable.equals(v2rVar)) { + return -g12; + } else if (variable.equals(v1iVar)) { + return -b1b12sum; + } else if (variable.equals(v2iVar)) { + return b12; + } else { + throw new IllegalArgumentException("Unknown variable " + variable); + } + } + + @Override + protected String getName() { + return "yr1"; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermX2.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermX2.java new file mode 100644 index 00000000..ed906415 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermX2.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.equations.Variable; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; + +/** + * @author Jean-Baptiste Heyberger + */ +public class AdmittanceEquationTermX2 extends AbstractAdmittanceEquationTerm { + + private final double g21; + + private final double b21; + + private final double g2g21sum; + + private final double b2b21sum; + + public AdmittanceEquationTermX2(LfBranch branch, LfBus bus1, LfBus bus2, VariableSet variableSet, AdmittanceEquationSystem.AdmittanceType admittanceType) { + super(branch, bus1, bus2, variableSet); + // Direct component: + // I2x = -g21 * V1x + b21 * V1y + (g2 + g21)V2x - (b2 + b21)V2y + if (admittanceType == AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + HomopolarModel homopolarModel = HomopolarModel.build(branch); + if (branch.getBranchType() == LfBranch.BranchType.LINE) { + g21 = rho * homopolarModel.getZoInvSquare() * (homopolarModel.getRo() * cosA + homopolarModel.getXo() * sinA); + b21 = rho * homopolarModel.getZoInvSquare() * (homopolarModel.getRo() * sinA - homopolarModel.getXo() * cosA); + g2g21sum = homopolarModel.getRo() * homopolarModel.getZoInvSquare() + gPi2 * AdmittanceConstants.COEF_XO_XD; + b2b21sum = -homopolarModel.getXo() * homopolarModel.getZoInvSquare() + bPi2 * AdmittanceConstants.COEF_XO_XD; + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_1 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_3) { + // case where branch is part of a transformer + DenseMatrix mo = homopolarModel.computeHomopolarAdmittanceMatrix(); + b2b21sum = -mo.get(2, 3); + g2g21sum = mo.get(2, 2); + b21 = mo.get(2, 1); + g21 = -mo.get(2, 0); + } else { + throw new IllegalArgumentException("branch type not yet handled"); + } + } else { + double g12 = rho * zInvSquare * (r * cosA + x * sinA); + g21 = g12; + b21 = rho * zInvSquare * (r * sinA - x * cosA); + g2g21sum = r * zInvSquare + gPi2; + b2b21sum = -x * zInvSquare + bPi2; + } + } + + @Override + public double getCoefficient(Variable variable) { + if (variable.equals(v1rVar)) { + return -g21; + } else if (variable.equals(v2rVar)) { + return g2g21sum; + } else if (variable.equals(v1iVar)) { + return b21; + } else if (variable.equals(v2iVar)) { + return -b2b21sum; + } else { + throw new IllegalArgumentException("Unknown variable " + variable); + } + } + + @Override + protected String getName() { + return "yr2"; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermY1.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermY1.java new file mode 100644 index 00000000..e25d11d9 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermY1.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.equations.Variable; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; + +/** + * @author Jean-Baptiste Heyberger + */ +public class AdmittanceEquationTermY1 extends AbstractAdmittanceEquationTerm { + + private final double g12; + + private final double b12; + + private final double g1g12sum; + + private final double b1b12sum; + + public AdmittanceEquationTermY1(LfBranch branch, LfBus bus1, LfBus bus2, VariableSet variableSet, AdmittanceEquationSystem.AdmittanceType admittanceType) { + super(branch, bus1, bus2, variableSet); + // Direct component: + // I1y = (b1 + b12)V1x + (g1 + g12)V1y - b12 * V2x - g12 * V2y + if (admittanceType == AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + HomopolarModel homopolarModel = HomopolarModel.build(branch); + if (branch.getBranchType() == LfBranch.BranchType.LINE) { + // case where branch is a line with available homopolar parameters + g12 = rho * homopolarModel.getZoInvSquare() * (homopolarModel.getRo() * cosA + homopolarModel.getXo() * sinA); + b12 = -rho * homopolarModel.getZoInvSquare() * (homopolarModel.getXo() * cosA + homopolarModel.getRo() * sinA); + g1g12sum = rho * rho * (homopolarModel.getGom() + homopolarModel.getRo() * homopolarModel.getZoInvSquare()); + b1b12sum = rho * rho * (homopolarModel.getBom() - homopolarModel.getXo() * homopolarModel.getZoInvSquare()); + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_1 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_3) { + // case where branch is part of a transformer + DenseMatrix mo = homopolarModel.computeHomopolarAdmittanceMatrix(); + b1b12sum = mo.get(1, 0); + g1g12sum = mo.get(1, 1); + b12 = -mo.get(1, 2); + g12 = -mo.get(1, 3); + } else { + throw new IllegalArgumentException("branch type not yet handled"); + } + } else { + g12 = rho * zInvSquare * (r * cosA + x * sinA); + b12 = -rho * zInvSquare * (x * cosA + r * sinA); + g1g12sum = rho * rho * (gPi1 + r * zInvSquare); + b1b12sum = rho * rho * (bPi1 - x * zInvSquare); + } + } + + @Override + public double getCoefficient(Variable variable) { + if (variable.equals(v1rVar)) { + return b1b12sum; + } else if (variable.equals(v2rVar)) { + return -b12; + } else if (variable.equals(v1iVar)) { + return g1g12sum; + } else if (variable.equals(v2iVar)) { + return -g12; + } else { + throw new IllegalArgumentException("Unknown variable " + variable); + } + } + + @Override + protected String getName() { + return "yi1"; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermY2.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermY2.java new file mode 100644 index 00000000..3d86f92c --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceEquationTermY2.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.equations.Variable; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; + +/** + * @author Jean-Baptiste Heyberger + */ +public class AdmittanceEquationTermY2 extends AbstractAdmittanceEquationTerm { + + private final double g21; + + private final double b21; + + private final double g2g21sum; + + private final double b2b21sum; + + public AdmittanceEquationTermY2(LfBranch branch, LfBus bus1, LfBus bus2, VariableSet variableSet, AdmittanceEquationSystem.AdmittanceType admittanceType) { + super(branch, bus1, bus2, variableSet); + // Direct component: + // I2y = -b21 * V1x - g21 * V1y + (b2 + b21)V2x + (g2 + g21)V2y + if (admittanceType == AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN_HOMOPOLAR) { + HomopolarModel homopolarModel = HomopolarModel.build(branch); + if (branch.getBranchType() == LfBranch.BranchType.LINE) { + // case where branch is a line with available homopolar parameters + g21 = rho * homopolarModel.getZoInvSquare() * (homopolarModel.getRo() * cosA + homopolarModel.getXo() * sinA); + b21 = rho * homopolarModel.getZoInvSquare() * (homopolarModel.getRo() * sinA - homopolarModel.getXo() * cosA); + g2g21sum = homopolarModel.getRo() * homopolarModel.getZoInvSquare() + gPi2 * AdmittanceConstants.COEF_XO_XD; + b2b21sum = -homopolarModel.getXo() * homopolarModel.getZoInvSquare() + bPi2 * AdmittanceConstants.COEF_XO_XD; + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_1 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_3) { + // case where branch is part of a transformer + DenseMatrix mo = homopolarModel.computeHomopolarAdmittanceMatrix(); + b2b21sum = mo.get(3, 2); + g2g21sum = mo.get(3, 3); + b21 = -mo.get(3, 0); + g21 = -mo.get(3, 1); + } else { + throw new IllegalArgumentException("branch type not yet handled"); + } + } else { + double g12 = rho * zInvSquare * (r * cosA + x * sinA); + g21 = g12; + b21 = rho * zInvSquare * (r * sinA - x * cosA); + g2g21sum = r * zInvSquare + gPi2; + b2b21sum = -x * zInvSquare + bPi2; + } + } + + @Override + public double getCoefficient(Variable variable) { + if (variable.equals(v1rVar)) { + return -b21; + } else if (variable.equals(v2rVar)) { + return b2b21sum; + } else if (variable.equals(v1iVar)) { + return -g21; + } else if (variable.equals(v2iVar)) { + return g2g21sum; + } else { + throw new IllegalArgumentException("Unknown variable " + variable); + } + } + + @Override + protected String getName() { + return "yi2"; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceMatrix.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceMatrix.java new file mode 100644 index 00000000..ef69777b --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/AdmittanceMatrix.java @@ -0,0 +1,390 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.math.matrix.LUDecomposition; +import com.powsybl.math.matrix.Matrix; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.equations.*; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfNetwork; +import com.powsybl.openloadflow.network.util.VoltageInitializer; + +import java.util.*; + +/** + * @author Jean-Baptiste Heyberger + */ +public class AdmittanceMatrix implements AutoCloseable { + + public class AdmittanceSystem { + + //created to extract a subset of the equationSystem to easily create admittance subMatrices for the reduction problem while keeping consistency on the global equation system + private Set numRowBusses; + + private Set numColBusses; + + public Map, Integer> eqToRowNum; + + public Map, Integer> varToColNum; + + public boolean isSubAdmittance; + + AdmittanceSystem() { + + numRowBusses = new HashSet<>(); + numColBusses = new HashSet<>(); + + eqToRowNum = new HashMap<>(); + varToColNum = new HashMap<>(); + + //Convert rowBusses and columnBusses Sets into eq number Sets + if (rowBusses != null) { + for (LfBus b : rowBusses) { + numRowBusses.add(b.getNum()); + } + } + + if (columnBusses != null) { + for (LfBus b : columnBusses) { + numColBusses.add(b.getNum()); + } + } + + isSubAdmittance = numRowBusses.size() > 0 || numColBusses.size() > 0; //if false then build the full admittance system based on the equationSystem infos + + if (isSubAdmittance) { + int nbRow = 0; + int nbCol = 0; + for (var eq : equationSystem.getIndex().getSortedEquationsToSolve()) { + int numBusEq = eq.getElementNum(); + if (numRowBusses.contains(numBusEq)) { + eqToRowNum.put(eq, nbRow++); + } + } + + for (Variable v : equationSystem.getIndex().getSortedVariablesToFind()) { + int numBusVar = v.getElementNum(); + if (numColBusses.contains(numBusVar)) { + varToColNum.put(v, nbCol++); + } + } + } + } + } + + private final EquationSystem equationSystem; + + private final LfNetwork lfNetwork; + + private final MatrixFactory matrixFactory; + + private Matrix matrix; + + private LUDecomposition lu; + + private Set rowBusses; + + private Set columnBusses; + + private AdmittanceSystem admSys; + + private List busNumToRowR; //given a number of bus, provides the Row and Column to the matrix + private List busNumToColR; + private List busNumToRowI; //given a number of bus, provides the Row and Column to the matrix + private List busNumToColI; + + public AdmittanceMatrix(EquationSystem equationSystem, MatrixFactory matrixFactory, LfNetwork network) { + this.equationSystem = Objects.requireNonNull(equationSystem); + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.admSys = new AdmittanceSystem(); + this.lfNetwork = Objects.requireNonNull(network); + initAdmittanceSystem(); + } + + public AdmittanceMatrix(EquationSystem equationSystem, MatrixFactory matrixFactory, Set rowBusses, Set columnBusses, LfNetwork network) { + this.equationSystem = Objects.requireNonNull(equationSystem); + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.rowBusses = Objects.requireNonNull(rowBusses); + this.columnBusses = Objects.requireNonNull(columnBusses); + this.admSys = new AdmittanceSystem(); + this.lfNetwork = Objects.requireNonNull(network); + initAdmittanceSystem(); + } + + public AdmittanceSystem getAdmSys() { + return admSys; + } + + public EquationSystem getEquationSystem() { + return equationSystem; + } + + private void clear() { + matrix = null; + if (lu != null) { + lu.close(); + } + lu = null; + } + + public int getRowCount() { + int rowCount = equationSystem.getIndex().getSortedEquationsToSolve().size(); + if (admSys.isSubAdmittance) { + rowCount = admSys.eqToRowNum.size(); + } + return rowCount; + } + + public int getColCount() { + int columnCount = equationSystem.getIndex().getSortedVariablesToFind().size(); + if (admSys.isSubAdmittance) { + columnCount = admSys.varToColNum.size(); + } + return columnCount; + } + + private Map, List>> indexTermsByVariable(Equation eq) { + Map, List>> termsByVariable = new TreeMap<>(); + for (EquationTerm term : eq.getTerms()) { + for (Variable v : term.getVariables()) { + termsByVariable.computeIfAbsent(v, k -> new ArrayList<>()) + .add(term); + } + } + return termsByVariable; + } + + private void initAdmittanceSystem() { + //if if no busses specified in input, we build the admittance of the full system + int rowCount = getRowCount(); + int columnCount = getColCount(); + + //intialization of accessors + busNumToRowR = new ArrayList<>(); + busNumToColR = new ArrayList<>(); + busNumToRowI = new ArrayList<>(); + busNumToColI = new ArrayList<>(); + for (int i = 0; i < 2 * lfNetwork.getBuses().size(); i++) { + busNumToRowR.add(0); + busNumToColR.add(0); + busNumToRowI.add(0); + busNumToColI.add(0); + } + + int estimatedNonZeroValueCount = rowCount * 3; + matrix = matrixFactory.create(columnCount, rowCount, estimatedNonZeroValueCount); //matrix is the transposed of the standard admittance matrix + + for (var eq : equationSystem.getIndex().getSortedEquationsToSolve()) { + int yRow = eq.getColumn(); // equations are the rows of Y (and the columns of "matrix", the transposed of Y) + if (admSys.isSubAdmittance && admSys.eqToRowNum.containsKey(eq)) { + yRow = admSys.eqToRowNum.get(eq); // the matrix is the transposed of the admittance matrix as equations represent the columns and the vars represent the rows + } + + //Init of the row accessors + int busNum = eq.getElementNum(); + EquationType eqType = eq.getType(); + if (eqType == EquationType.BUS_YR) { + busNumToRowR.set(busNum, yRow); + } else if (eqType == EquationType.BUS_YI) { + busNumToRowI.set(busNum, yRow); + } + + if (!admSys.isSubAdmittance || admSys.eqToRowNum.containsKey(eq)) { + for (Map.Entry, List>> e2 : indexTermsByVariable(eq).entrySet()) { + Variable var = e2.getKey(); + int yColumn = var.getRow(); // vars are the columns of Y (and the rows of "matrix", the transposed of Y) + if (admSys.isSubAdmittance && admSys.varToColNum.containsKey(var)) { + yColumn = admSys.varToColNum.get(var); // the matrix is the transposed of the admittance matrix + } + if (!admSys.isSubAdmittance || admSys.varToColNum.containsKey(var)) { + for (EquationTerm equationTerm : e2.getValue()) { + double value = ((LinearEquationTerm) equationTerm).getCoefficient(var); + //System.out.println(" add term i = " + yRow + " j = " + yColumn + " value = " + value); + matrix.add(yColumn, yRow, value); //matrix is here the transposed of Y + } + } + } + } + } + + //Init of the column accessors + for (Variable var : equationSystem.getIndex().getSortedVariablesToFind()) { + int busNum = var.getElementNum(); + VariableType varType = var.getType(); + + int yColumn = var.getRow(); // vars are the columns of Y (and the rows of "matrix", the transposed of Y) + if (admSys.isSubAdmittance && admSys.varToColNum.containsKey(var)) { + yColumn = admSys.varToColNum.get(var); // the matrix is the transposed of the admittance matrix + } + if (varType == VariableType.BUS_VR) { + busNumToColR.set(busNum, yColumn); + } else if (varType == VariableType.BUS_VI) { + busNumToColI.set(busNum, yColumn); + } + } + + } + + private double[] createStateVector(LfNetwork network, VoltageInitializer initializer) { + double[] x = new double[equationSystem.getIndex().getSortedVariablesToFind().size()]; + for (Variable v : equationSystem.getIndex().getSortedVariablesToFind()) { + switch (v.getType()) { + case BUS_VR: + x[v.getRow()] = initializer.getMagnitude(network.getBus(v.getElementNum())) * Math.cos(Math.toRadians(initializer.getAngle(network.getBus(v.getElementNum())))); + break; + + case BUS_VI: + x[v.getRow()] = initializer.getMagnitude(network.getBus(v.getElementNum())) * Math.sin(Math.toRadians(initializer.getAngle(network.getBus(v.getElementNum())))); + break; + + default: + throw new IllegalStateException("Unknown variable type " + v.getType()); + } + } + return x; + } + + private Matrix initVoltageVector(LfNetwork network, VoltageInitializer voltageInitializer) { + //if if no busses specified in input, we build the voltage vector of the full system + int columnCount = getColCount(); + + double[] v = createStateVector(network, voltageInitializer); + double[] vPart = new double[columnCount]; + Matrix mV; + + if (admSys.isSubAdmittance) { + for (Variable var : equationSystem.getIndex().getSortedVariablesToFind()) { + int row = var.getRow(); + if (admSys.varToColNum.containsKey(var)) { + vPart[admSys.varToColNum.get(var)] = v[row]; + } + } + mV = Matrix.createFromRow(vPart, matrixFactory); + } else { + mV = Matrix.createFromRow(v, matrixFactory); + } + + return mV; + } + + public Map getDeltaV(DenseMatrix m, int numColumn) { + Map tmpV = new HashMap<>(); + for (Variable var : equationSystem.getIndex().getSortedVariablesToFind()) { + int row = var.getRow(); + VariableType type = var.getType(); + if (admSys.isSubAdmittance) { + if (admSys.varToColNum.containsKey(var)) { + row = admSys.varToColNum.get(var); + } else { + throw new IllegalArgumentException("Could not update variable V num = " + var.getElementNum() + ", index not found in the subsystem"); + } + } + + DenseMatrix tmpMat = this.matrixFactory.create(2, 2, 4).toDense(); + if (!tmpV.containsKey(var.getElementNum())) { + tmpV.put(var.getElementNum(), tmpMat); + } + if (type == VariableType.BUS_VR) { + tmpV.get(var.getElementNum()).add(0, 0, m.get(row, 2 * numColumn)); + tmpV.get(var.getElementNum()).add(0, 1, m.get(row, 2 * numColumn + 1)); + + } else if (type == VariableType.BUS_VI) { + tmpV.get(var.getElementNum()).add(1, 0, m.get(row, 2 * numColumn)); + tmpV.get(var.getElementNum()).add(1, 1, m.get(row, 2 * numColumn + 1)); + } + + } + + return tmpV; + } + + public List getDeltaVFortescue(List busNum2Dv, DenseMatrix md, DenseMatrix mo, DenseMatrix mi) { + for (Variable var : equationSystem.getIndex().getSortedVariablesToFind()) { + int row = var.getRow(); + VariableType type = var.getType(); + int busNum = var.getElementNum(); + if (admSys.isSubAdmittance) { + if (admSys.varToColNum.containsKey(var)) { + row = admSys.varToColNum.get(var); + } else { + throw new IllegalArgumentException("Could not update variable V num = " + var.getElementNum() + ", index not found in the subsystem"); + } + } + + if (type == VariableType.BUS_VR) { + busNum2Dv.get(busNum).add(0, 0, mo.get(row, 0)); + busNum2Dv.get(busNum).add(2, 0, md.get(row, 0)); + busNum2Dv.get(busNum).add(4, 0, mi.get(row, 0)); + + } else if (type == VariableType.BUS_VI) { + busNum2Dv.get(busNum).add(1, 0, mo.get(row, 0)); + busNum2Dv.get(busNum).add(3, 0, md.get(row, 0)); + busNum2Dv.get(busNum).add(5, 0, mi.get(row, 0)); + } + + } + + return busNum2Dv; + } + + public int getRowBus(int numBus, EquationType eqType) { + int yRow = 0; + if (eqType == EquationType.BUS_YR) { + yRow = busNumToRowR.get(numBus); + } else if (eqType == EquationType.BUS_YI) { + yRow = busNumToRowI.get(numBus); + } + + return yRow; //TODO : send an exception when bus is not found in equation set + } + + public int getColBus(int numBus, VariableType varType) { + int yColumn = 0; + if (varType == VariableType.BUS_VR) { + yColumn = busNumToColR.get(numBus); + } else if (varType == VariableType.BUS_VI) { + yColumn = busNumToColI.get(numBus); + } + + return yColumn; //TODO : send an exception when bus is not found in var set + } + + public Matrix getMatrix() { + return matrix; + } + + public Matrix getVoltageVector(LfNetwork network, VoltageInitializer voltageInitializer) { + return initVoltageVector(network, voltageInitializer); + } + + private LUDecomposition getLUDecomposition() { + if (lu == null) { + lu = matrix.decomposeLU(); + } + return lu; + } + + public void solveTransposed(double[] b) { + getLUDecomposition().solveTransposed(b); + } + + public void solveTransposed(DenseMatrix b) { + getLUDecomposition().solveTransposed(b); + } + + public DenseMatrix transpose() { + return matrix.toDense().transpose(); + } + + @Override + public void close() { + clear(); + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/CalculationLocation.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/CalculationLocation.java new file mode 100644 index 00000000..b9547323 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/CalculationLocation.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import org.apache.commons.math3.util.Pair; + +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class CalculationLocation { + + private final String busLocation; + + private final String busLocationBiPhased; + + private final boolean voltageUpdate; + + private Pair iidmBusInfo; // additional iidm info to make the correspondence between iidm info and lfNetwork info + + private Pair iidmBus2Info; // additional iidm info to make the correspondence between iidm info and lfNetwork info in case of a biphased common support fault + + private String lfBusInfo; // additional info to have the correspondence between iidm and lfNetwork + + public CalculationLocation(String busLocation, boolean voltageUpdate) { + this(busLocation, "", voltageUpdate); + } + + public CalculationLocation(String busLocation, String busLocationBiPhased, boolean voltageUpdate) { + this.busLocation = Objects.requireNonNull(busLocation); + this.busLocationBiPhased = Objects.requireNonNull(busLocationBiPhased); + this.voltageUpdate = voltageUpdate; + } + + public String getBusLocation() { + return busLocation; + } + + public String getBusLocationBiPhased() { + return busLocationBiPhased; + } + + public boolean isVoltageUpdate() { + return voltageUpdate; + } + + public void setIidmBusInfo(Pair iidmBusInfo) { + this.iidmBusInfo = iidmBusInfo; + } + + public void setIidmBus2Info(Pair iidmBus2Info) { + this.iidmBus2Info = iidmBus2Info; + } + + public Pair getIidmBus2Info() { + return iidmBus2Info; + } + + public Pair getIidmBusInfo() { + return iidmBusInfo; + } + + public void setLfBusInfo(String lfBusInfo) { + this.lfBusInfo = lfBusInfo; + } + + public String getLfBusInfo() { + return lfBusInfo; + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemBusFeeders.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemBusFeeders.java new file mode 100644 index 00000000..f099af68 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemBusFeeders.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.network.LfBus; + +import java.util.List; + +/** + * @author Jean-Baptiste Heyberger + */ +public class EquationSystemBusFeeders { + + private List feeders; + + private LfBus feedersBus; + + public EquationSystemBusFeeders(List feeders, LfBus bus) { + this.feeders = feeders; + this.feedersBus = bus; + } + + public List getFeeders() { + return feeders; + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemBusFeedersResult.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemBusFeedersResult.java new file mode 100644 index 00000000..15184fcc --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemBusFeedersResult.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.network.LfBus; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Jean-Baptiste Heyberger + */ +public class EquationSystemBusFeedersResult { + + private static final double EPSILON = 0.000001; + + private double ixFeedersSum; //sum of currents coming from branches, which is also the sum of feeders'currents + private double iyFeedersSum; + + private List feedersInputData; // input data of feeders at bus + + private List busFeedersResults; // output data of feeders at bus + + private LfBus feedersBus; + + public EquationSystemBusFeedersResult(List feeders, LfBus bus) { + this.ixFeedersSum = 0.; + this.iyFeedersSum = 0.; + this.feedersInputData = feeders; + this.feedersBus = bus; + this.busFeedersResults = new ArrayList<>(); + + // init on feeder results based on equation system feeders + for (EquationSystemFeeder feeder : feedersInputData) { + EquationSystemResultFeeder feederResult = new EquationSystemResultFeeder(feeder.getId(), feeder.getFeederType(), 0., 0., feeder.getG(), feeder.getB()); + busFeedersResults.add(feederResult); + } + } + + public void addIfeeders(double ix, double iy) { + this.ixFeedersSum = ix + this.ixFeedersSum; + this.iyFeedersSum = iy + this.iyFeedersSum; + } + + public void updateContributions() { + double bSum = 0.; + double gSum = 0.; + + for (EquationSystemResultFeeder feeder : busFeedersResults) { + bSum = bSum + feeder.getB(); + gSum = gSum + feeder.getG(); + } + + if (Math.abs(gSum) > EPSILON || Math.abs(bSum) > EPSILON) { + double det = 1 / (gSum * gSum + bSum * bSum); + for (EquationSystemResultFeeder feederResult : busFeedersResults) { + double gk = feederResult.getG(); + double bk = feederResult.getB(); + double ixk = det * ((gk * gSum + bk * bSum) * ixFeedersSum + (gk * bSum - gSum * bk) * iyFeedersSum); + double iyk = det * ((-gk * bSum + gSum * bk) * ixFeedersSum + (gk * gSum + bk * bSum) * iyFeedersSum); + + feederResult.updateIcontribution(ixk, iyk); + feederResult.printContributions(feedersBus); + } + } + } + + public List getBusFeedersResults() { + return busFeedersResults; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemFeeder.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemFeeder.java new file mode 100644 index 00000000..b6eba68b --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemFeeder.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +/** + * @author Jean-Baptiste Heyberger + */ +public class EquationSystemFeeder { + + //Feeder class is used to post process the results of a short circuit computation to get the feeder contribution in short-circuit current + public EquationSystemFeeder(double b, double g, String id, EquationSystemFeeder.FeederType feederType) { + + this.b = b; + this.g = g; + this.id = id; + this.feederType = feederType; + + } + + public enum FeederType { + GENERATOR, + SHUNT, + CONTROLLED_SHUNT, + LOAD; + } + + private double b; + + private double g; + + private String id; // id in LfNetwork + + private EquationSystemFeeder.FeederType feederType; + + public double getB() { + return b; + } + + public double getG() { + return g; + } + + public String getId() { + return id; + } + + public EquationSystemFeeder.FeederType getFeederType() { + return feederType; + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemFeeders.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemFeeders.java new file mode 100644 index 00000000..f527e7f0 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemFeeders.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.network.LfBus; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Jean-Baptiste Heyberger + */ +// This class aims at organizing the information related to the feeders for a given equation system +public class EquationSystemFeeders { + + public EquationSystemFeeders() { + this.busToFeeders = new HashMap<>(); + } + + public Map busToFeeders; + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemResultFeeder.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemResultFeeder.java new file mode 100644 index 00000000..cde2427d --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationSystemResultFeeder.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.network.LfBus; + +/** + * @author Jean-Baptiste Heyberger + */ +public class EquationSystemResultFeeder { + + private double b; + + private double g; + + private String id; // id in LfNetwork + + private EquationSystemFeeder.FeederType feederType; + + private double ixContribution; + + private double iyContribution; + + public EquationSystemResultFeeder(String id, EquationSystemFeeder.FeederType feederType, double ix, double iy, double g, double b) { + this.id = id; + this.feederType = feederType; + this.ixContribution = ix; + this.iyContribution = iy; + this.g = g; + this.b = b; + } + + public EquationSystemFeeder.FeederType getFeederType() { + return feederType; + } + + public void updateIcontribution(double ix, double iy) { + ixContribution = ixContribution + ix; + iyContribution = iyContribution + iy; + } + + public void printContributions(LfBus bus) { + System.out.println(" ix(" + id + ", " + feederType + ") = " + ixContribution + " + j(" + iyContribution + ") Module I = " + + 1000. * 100. / bus.getNominalV() * Math.sqrt((ixContribution * ixContribution + iyContribution * iyContribution) / 3.)); //TODO : issue with a 3x factor + } + + double getB() { + return b; + } + + double getG() { + return g; + } + + public String getId() { + return id; + } + + public double getIxContribution() { + return ixContribution; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationType.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationType.java new file mode 100644 index 00000000..af84a474 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/EquationType.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.equations.Quantity; +import com.powsybl.openloadflow.network.ElementType; + +/** + * @author Geoffroy Jamgotchian + */ +public enum EquationType implements Quantity { + BUS_YR("yr"), // real part + BUS_YI("yi"); // imaginary part + + private final String symbol; + + EquationType(String symbol) { + this.symbol = symbol; + } + + @Override + public String getSymbol() { + return symbol; + } + + @Override + public ElementType getElementType() { + return ElementType.BUS; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/HomopolarModel.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/HomopolarModel.java new file mode 100644 index 00000000..eccbc0cd --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/HomopolarModel.java @@ -0,0 +1,433 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.incubator.simulator.util.extensions.*; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.network.LfBranch; + +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + * @author Geoffroy Jamgotchian + */ +public class HomopolarModel { + + private final LfBranch branch; + + // values here are expressed in pu (Vnom_B, Sbase = 100.) + private double ro = 0; // ro = roa + rob + private double xo = 0; // xo = xoa + xob + + private double gom = 0; // Zom = 1 / Yom with Yom = gom + j*bom + private double bom = 0; + + private double rga = 0; + private double xga = 0; + + private double rgb = 0; + private double xgb = 0; + + // if the branch is not a transfo, then it is the correct default behaviour + private ShortCircuitTransformerLeg.LegConnectionType leg1ConnectionType = ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED; + private ShortCircuitTransformerLeg.LegConnectionType leg2ConnectionType = ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED; + + private boolean freeFluxes = false; + + protected HomopolarModel(LfBranch branch) { + this.branch = Objects.requireNonNull(branch); + } + + public double getRo() { + return ro; + } + + public double getXo() { + return xo; + } + + public double getGom() { + return gom; + } + + public double getBom() { + return bom; + } + + public double getRga() { + return rga; + } + + public double getXga() { + return xga; + } + + public double getRgb() { + return rgb; + } + + public double getXgb() { + return xgb; + } + + public ShortCircuitTransformerLeg.LegConnectionType getLeg1ConnectionType() { + return leg1ConnectionType; + } + + public ShortCircuitTransformerLeg.LegConnectionType getLeg2ConnectionType() { + return leg2ConnectionType; + } + + public boolean isFreeFluxes() { + return freeFluxes; + } + + public double getZoInvSquare() { + return ro != 0 || xo != 0 ? 1 / (ro * ro + xo * xo) : 0; + } + + public static HomopolarModel build(LfBranch branch) { + Objects.requireNonNull(branch); + + var piModel = branch.getPiModel(); + double r = piModel.getR(); + double x = piModel.getX(); + double gPi1 = piModel.getG1(); + double bPi1 = piModel.getB1(); + + var homopolarExtension = new HomopolarModel(branch); + + // default initialization if no homopolar values available + homopolarExtension.ro = r / AdmittanceConstants.COEF_XO_XD; + homopolarExtension.xo = x / AdmittanceConstants.COEF_XO_XD; + + homopolarExtension.gom = gPi1 * AdmittanceConstants.COEF_XO_XD; //TODO : adapt + homopolarExtension.bom = bPi1 * AdmittanceConstants.COEF_XO_XD; //TODO : adapt + + if (branch.getBranchType() == LfBranch.BranchType.LINE) { + // branch is a line and homopolar data available + ShortCircuitLine shortCircuitLine = (ShortCircuitLine) branch.getProperty(ShortCircuitExtensions.PROPERTY_NAME); + if (shortCircuitLine != null) { + double rCoeff = shortCircuitLine.getCoeffRo(); + double xCoeff = shortCircuitLine.getCoeffXo(); + homopolarExtension.ro = r * rCoeff; + homopolarExtension.xo = x * xCoeff; + homopolarExtension.gom = gPi1 / rCoeff; //TODO : adapt + homopolarExtension.bom = bPi1 / xCoeff; //TODO : adapt + } + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_2) { + // branch is a 2 windings transformer and homopolar data available + ShortCircuitT2W shortCircuitT2W = (ShortCircuitT2W) branch.getProperty(ShortCircuitExtensions.PROPERTY_NAME); + if (shortCircuitT2W != null) { + double rCoeff = shortCircuitT2W.getCoeffRo(); + double xCoeff = shortCircuitT2W.getCoeffXo(); + homopolarExtension.ro = r * rCoeff; + homopolarExtension.xo = x * xCoeff; + homopolarExtension.gom = gPi1 / rCoeff; //TODO : adapt + homopolarExtension.bom = bPi1 / xCoeff; //TODO : adapt + + homopolarExtension.leg1ConnectionType = shortCircuitT2W.getLeg1().getLegConnectionType(); + homopolarExtension.leg2ConnectionType = shortCircuitT2W.getLeg2().getLegConnectionType(); + } + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_1 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_2 + || branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_3) { + // branch is leg1 of a 3 windings transformer and homopolar data available + ShortCircuitT3W shortCircuitT3W = (ShortCircuitT3W) branch.getProperty(ShortCircuitExtensions.PROPERTY_NAME); + if (shortCircuitT3W != null) { + double rCoeff; + double xCoeff; + if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_1) { + rCoeff = shortCircuitT3W.getLeg1().getCoeffRo(); + xCoeff = shortCircuitT3W.getLeg1().getCoeffXo(); + homopolarExtension.leg1ConnectionType = shortCircuitT3W.getLeg1().getLegConnectionType(); + homopolarExtension.freeFluxes = shortCircuitT3W.getLeg1().isFreeFluxes(); + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_2) { + rCoeff = shortCircuitT3W.getLeg2().getCoeffRo(); + xCoeff = shortCircuitT3W.getLeg2().getCoeffXo(); + homopolarExtension.leg1ConnectionType = shortCircuitT3W.getLeg2().getLegConnectionType(); + homopolarExtension.freeFluxes = shortCircuitT3W.getLeg2().isFreeFluxes(); + } else if (branch.getBranchType() == LfBranch.BranchType.TRANSFO_3_LEG_3) { + rCoeff = shortCircuitT3W.getLeg3().getCoeffRo(); + xCoeff = shortCircuitT3W.getLeg3().getCoeffXo(); + homopolarExtension.leg1ConnectionType = shortCircuitT3W.getLeg3().getLegConnectionType(); + homopolarExtension.freeFluxes = shortCircuitT3W.getLeg3().isFreeFluxes(); + } else { + throw new IllegalArgumentException("Branch " + branch.getId() + " has unknown 3-winding leg number"); + } + + homopolarExtension.ro = r * rCoeff; + homopolarExtension.xo = x * xCoeff; + homopolarExtension.gom = gPi1 / rCoeff; //TODO : adapt + homopolarExtension.bom = bPi1 / xCoeff; //TODO : adapt + } + } else { + throw new IllegalArgumentException("Branch '" + branch.getId() + "' has a not yet supported type"); + } + + return homopolarExtension; + } + + public DenseMatrix computeHomopolarAdmittanceMatrix() { + DenseMatrix mo = new DenseMatrix(4, 4); + + var piModel = branch.getPiModel(); + double rho = piModel.getR1(); + double alpha = piModel.getA1(); + double cosA = Math.cos(Math.toRadians(alpha)); + double sinA = Math.sin(Math.toRadians(alpha)); + double cos2A = Math.cos(Math.toRadians(2 * alpha)); + double sin2A = Math.sin(Math.toRadians(2 * alpha)); + + double infiniteImpedanceAdmittance = AdmittanceConstants.INFINITE_IMPEDANCE_ADMITTANCE_VALUE; + + // if the free fluxes option is false, we suppose that if Yom given in input is zero, then Zom = is zero : TODO : see if there is a more robust way to handle this + // if the free fluxes option is true, Zom is infinite and Yom is then considered as zero + double rm = 0.; + double xm = 0.; + if (bom != 0. || gom != 0.) { + rm = gom / (bom * bom + gom * gom); + xm = -bom / (bom * bom + gom * gom); + } + + // we suppose that zob = zoa = Zo / 2 : TODO : check this is an acceptable approximation + double roa = ro / 2.; + double xoa = xo / 2.; + double rob = ro / 2.; + double xob = xo / 2.; + + // we suppose that all impedance and admittance terms of the homopolar extension are per-unitized on Sbase = 100 MVA and Vnom = Vnom on B side + if ((leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y) + || (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.DELTA) + || (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.DELTA && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y) + || (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.DELTA && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.DELTA) + || (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y && freeFluxes) + || (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED && freeFluxes)) { + // homopolar admittance matrix is zero-Matrix + mo.set(0, 0, infiniteImpedanceAdmittance); + mo.set(1, 1, infiniteImpedanceAdmittance); + mo.set(2, 2, infiniteImpedanceAdmittance); + mo.set(3, 3, infiniteImpedanceAdmittance); + + } else if (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y) { + // we suppose that Zoa = Zo given in input for the transformer + // we suppose that if Yom given in input is zero, then Zom = is zero : TODO : see if there is a more robust way to handle this + + // we have yo11 = 1 / ( 3Zga(pu) + (Zoa(pu)+ Zom(pu))/(rho*e(jAlpha))² ) + // and yo12 = yo22 = yo21 = 0. + // 3Zga(pu) + Zoa(pu)/(rho*e(jAlpha))² + Zom/(rho*e(jAlpha))² = 3*rg + 1/rho²*((roa+rom)cos2A-(xoa+xom)sin2A) + j(3*xg + 1/rho²*((xoa+xom)cos2A+(roa+rom)sin2A) ) + double req = 3 * rga + 1 / (rho * rho) * ((ro + rm) * cos2A - (xo + xm) * sin2A); + double xeq = 3 * xga + 1 / (rho * rho) * ((xo + xm) * cos2A + (ro + rm) * sin2A); + double bo11 = -xeq / (xeq * xeq + req * req); + double go11 = req / (xeq * xeq + req * req); + mo.set(0, 0, go11); + mo.set(0, 1, -bo11); + mo.set(1, 0, bo11); + mo.set(1, 1, go11); + mo.set(2, 2, infiniteImpedanceAdmittance); + mo.set(3, 3, infiniteImpedanceAdmittance); + } else if (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED) { + // we suppose that zob = Zo given in input for the transformer + // we suppose that if Yom given in input is zero, then Zom = is zero : TODO : see if there is a more robust way to handle this + + // we have yo22 = 1 / ( 3Zga(pu) + Zob(pu) + Zom(pu) ) + // and yo12 = yo11 = yo21 = 0. + // 3Zgb(pu) + Zob(pu) + Zom = 3*rg + rob + rom + j(3*xg + xob + xom ) + double req = 3 * rgb + ro + rm; + double xeq = 3 * xgb + xo + xm; + double bo22 = -xeq / (xeq * xeq + req * req); + double go22 = req / (xeq * xeq + req * req); + mo.set(0, 0, infiniteImpedanceAdmittance); + mo.set(1, 1, infiniteImpedanceAdmittance); + mo.set(2, 2, go22); + mo.set(2, 3, -bo22); + mo.set(3, 2, bo22); + mo.set(3, 3, go22); + } else if (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.DELTA) { + + // we suppose that if Yom given in input is zero, then Zom = is zero : TODO : see if there is a more robust way to handle this + + // we have yo11 = 1 / ( 3Zga(pu) + (Zoa(pu) + 1 / (1/Zom + 1/Zob))/(rho*e(jAlpha))² ) + // and yo12 = yo22 = yo21 = 0. + // using Ztmp = Zoa(pu) + 1 / (Yom + 1/Zob) + // 3Zga(pu) + (Zoa(pu) + 1 / (Yom + 1/Zob))/(rho*e(jAlpha))² = 3*rg + 1/rho²*((rtmp)cos2A-(xtmp)sin2A) + j(3*xg + 1/rho²*((xtmp)cos2A+(rtmp)sin2A) ) + double bob = -xob / (rob * rob + xob * xob); + double gob = rob / (rob * rob + xob * xob); + double bombob = bom + bob; + double gomgob = gom + gob; + double rtmp = roa + gomgob / (gomgob * gomgob + bombob * bombob); + double xtmp = xoa - bombob / (gomgob * gomgob + bombob * bombob); + double req = 3 * rga + 1 / (rho * rho) * (rtmp * cos2A - xtmp * sin2A); + double xeq = 3 * xga + 1 / (rho * rho) * (xtmp * cos2A + rtmp * sin2A); + double bo11 = -xeq / (xeq * xeq + req * req); + double go11 = req / (xeq * xeq + req * req); + + if (freeFluxes) { + // we have Zm = infinity : yo11 = 1 / ( 3Zga(pu) + (Zoa(pu) + 1/Zob(pu))/(rho*e(jAlpha))² ) + rtmp = roa + rob; + xtmp = xoa + xob; + req = 3 * rga + 1 / (rho * rho) * (rtmp * cos2A - xtmp * sin2A); + xeq = 3 * xga + 1 / (rho * rho) * (xtmp * cos2A + rtmp * sin2A); + bo11 = -xeq / (xeq * xeq + req * req); + go11 = req / (xeq * xeq + req * req); + } + + mo.set(0, 0, go11); + mo.set(0, 1, -bo11); + mo.set(1, 0, bo11); + mo.set(1, 1, go11); + mo.set(2, 2, infiniteImpedanceAdmittance); + mo.set(3, 3, infiniteImpedanceAdmittance); + + } else if (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.DELTA && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED) { + + // we have yo22 = 1 / ( 3Zga(pu) + Zob(pu) + 1/(1/Zom(pu)+1/Zoa(pu)) ) + // and yo12 = yo11 = yo21 = 0. + // 3Zgb(pu) + Zob(pu) + Zom = 3*rg + rob + rom + j(3*xg + xob + xom ) + double boa = -xoa / (roa * roa + xoa * xoa); + double goa = roa / (roa * roa + xoa * xoa); + double bomboa = bom + boa; + double gomgoa = gom + goa; + + double req = 3 * rgb + rob + gomgoa / (gomgoa * gomgoa + bomboa * bomboa); + double xeq = 3 * xgb + xob - bomboa / (gomgoa * gomgoa + bomboa * bomboa); + + double bo22 = -xeq / (xeq * xeq + req * req); + double go22 = req / (xeq * xeq + req * req); + + if (freeFluxes) { + // we have Zm = infinity : yo22 = 1 / ( 3Zga(pu) + Zob(pu) + Zoa(pu) ) + // and yo12 = yo11 = yo21 = 0. + + req = 3 * rgb + rob + roa; + xeq = 3 * xgb + xob + xoa; + + bo22 = -xeq / (xeq * xeq + req * req); + go22 = req / (xeq * xeq + req * req); + } + + mo.set(0, 0, infiniteImpedanceAdmittance); + mo.set(1, 1, infiniteImpedanceAdmittance); + mo.set(2, 2, go22); + mo.set(2, 3, -bo22); + mo.set(3, 2, bo22); + mo.set(3, 3, go22); + + } else if (leg1ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED && leg2ConnectionType == ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED) { + + double go11; + double bo11; + double go12; + double bo12; + double go22; + double bo22; + + if (!freeFluxes) { + // Case where fluxes are forced, meaning that Zm is not ignored (and could be zero with a direct connection to ground) + // + // k = rho*e(jAlpha)) + // + // [Ia] 1 [ Zom+Zob+3Zgs -Zom/k ] [Va] + // [ ] = ------------------------------------------ * [ ] [ ] + // [Ib] (Zom/k²+3Zga+Zoa/k²)(Zom+Zob+3Zgb)-(Zom/k)² [ -Zom/k Zom/k²+3Zga+Zoa/k² ] [Vb] + // [ ] [ ] [ ] + // + // Zc = Zom/k²+3Zga+Zoa/k² + // Zd = Zom+Zob+3Zgb + // Ze = Zom/k + // + // we suppose that if Yom given in input is zero, then Zom = is zero : TODO : see if there is a more robust way to handle this + + double rc = 1 / (rho * rho) * (cos2A * (rm + roa) + sin2A * (xm + xoa)) + 3 * rga; + double xc = 1 / (rho * rho) * (cos2A * (xm + xoa) - sin2A * (rm + roa)) + 3 * xga; + double rd = rm + rob + 3 * rgb; + double xd = xm + xob + 3 * xgb; + double re = (rm * cosA + xm * sinA) / rho; + double xe = (xm * cosA - rm * sinA) / rho; + + // this gives : + // [Ia] 1 [ Zd -Ze ] [Va] + // [ ] = ------------- * [ ] [ ] + // [Ib] Zc * Zd - Ze² [-Ze Zc ] [Vb] + // [ ] [ ] [ ] + // + // We set Z2denom = Zc * Zd - Ze² + double r2denom = rc * rd - xc * xd - re * re + xe * xe; + double x2demon = rc * xd + xc * rd - 2 * re * xe; + double g2demon = r2denom / (r2denom * r2denom + x2demon * x2demon); + double b2demon = -x2demon / (r2denom * r2denom + x2demon * x2demon); + go11 = g2demon * rd - b2demon * xd; + bo11 = b2demon * rd + g2demon * xd; + go12 = -g2demon * re + b2demon * xe; + bo12 = -b2demon * re - g2demon * xe; + go22 = g2demon * rc - b2demon * xc; + bo22 = b2demon * rc + g2demon * xc; + + } else { + // + // Zm = infinity + // k = rho*e(jAlpha)) + // + // [Ia] 1 [ 1 -1/k ] [Va] + // [ ] = --------------------------- * [ ] [ ] + // [Ib] 3Zga+Zoa/k²+Zob/k²+3Zgb/k²) [ -1/k 1/k² ] [Vb] + // [ ] [ ] [ ] + // + // Zc = 3Zga+Zoa/k²+Zob/k²+3Zgb/k²) + // Zd = Zom+Zob+3Zgb + // Ze = Zom/k + + double rc = 1 / (rho * rho) * (cos2A * (rob + roa + 3 * rgb) + sin2A * (xoa + xob + 3 * xgb)) + 3 * rga; + double xc = 1 / (rho * rho) * (cos2A * (xob + xoa + 3 * xgb) - sin2A * (rob + roa + 3 * rgb)) + 3 * xga; + + //double re = (rm * cosA + xm * sinA) / rho; + //double xe = (xm * cosA - rm * sinA) / rho; + + // this gives : + // [Ia] 1 [ 1 -1/k ] [Va] + // [ ] = ------------- * [ ] [ ] + // [Ib] Zc [-1/k 1/k²] [Vb] + // [ ] [ ] [ ] + // + // We set Z2denom = Zc * Zd - Ze² + double gcdemon = rc / (rc * rc + xc * xc); + double bcdemon = -xc / (rc * rc + xc * xc); + + go11 = gcdemon; + bo11 = bcdemon; + go12 = -(gcdemon * cosA + bcdemon * sinA) / rho; + bo12 = -(bcdemon * cosA - gcdemon * sinA) / rho; + go22 = (gcdemon * cos2A + bcdemon * sin2A) / (rho * rho); + bo22 = (bcdemon * cos2A - gcdemon * sin2A) / (rho * rho); + } + mo.set(0, 0, go11); + mo.set(0, 1, -bo11); + mo.set(1, 0, bo11); + mo.set(1, 1, go11); + + mo.set(2, 2, go22); + mo.set(2, 3, -bo22); + mo.set(3, 2, bo22); + mo.set(3, 3, go22); + + mo.set(0, 2, go12); + mo.set(0, 3, -bo12); + mo.set(1, 2, bo12); + mo.set(1, 3, go12); + + mo.set(2, 0, go12); + mo.set(2, 1, -bo12); + mo.set(3, 0, bo12); + mo.set(3, 1, go12); + } else { + throw new IllegalArgumentException("Branch " + branch.getId() + " configuration is not supported yet : " + leg1ConnectionType + " --- " + leg2ConnectionType); + } + + return mo; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/ImpedanceLinearResolution.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/ImpedanceLinearResolution.java new file mode 100644 index 00000000..d3d560a4 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/ImpedanceLinearResolution.java @@ -0,0 +1,569 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.extensions.ShortCircuitExtensions; +import com.powsybl.math.matrix.DenseMatrix; +import com.powsybl.openloadflow.equations.EquationSystem; +import com.powsybl.openloadflow.equations.VariableSet; +import com.powsybl.openloadflow.network.*; +import com.powsybl.openloadflow.network.impl.LfNetworkLoaderImpl; +import org.apache.commons.math3.util.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ImpedanceLinearResolution { + + // This class is used to resolve problems with a similar structure + // [ Vof ] = -tM * inv(Yo) * M * [ Iof ] + // [ Vdf ] = -tM * inv(Yd) * M * [ Idf ] + tM * [ V(init) ] + // [ Vif ] = -tM * inv(Yd) * M * [ Iif ] + + // + // [ Vx ] [ Ix ] [ Vx_init ] + // [ Vy ] = -t[En]*inv(Y)*[En] * [ Iy ] + t[En] * [ Vy_init ] + // + + private final List networks; + + private final ImpedanceLinearResolutionParameters parameters; + + public List results = new ArrayList<>(); + + public LfNetwork lfNetworkResult; + + public ImpedanceLinearResolution(Network network, ImpedanceLinearResolutionParameters parameters) { + this.networks = LfNetwork.load(network, new LfNetworkLoaderImpl(), new LfNetworkParameters(new FirstSlackBusSelector())); + this.parameters = Objects.requireNonNull(parameters); + ShortCircuitExtensions.add(network, networks, parameters.getAdditionalDataInfo()); + } + + public class ImpedanceLinearResolutionResult { + + private LfBus bus; + + private double xEq12; + private double xEq21; + private double rEq11; + private double rEq22; + + private double ethx; //real part of Thevenin voltage + private double ethy; //imaginary part of Thevenin voltage + + private DenseMatrix enBus; + + private Map dE; // the key stores the number of the bus, the value stores the resolved value [Res] = inv(Y)*[En], with n of vector [En] corresponding to the studied short circuit fault and values at lines of [Res] corresponding real and imaginary parts at bus in key + + private EquationSystemFeeders eqSysFeeders; + + private List biphasedResultsAtBus; // we store here all necessary information for all biphased common ground faults with first bus equal to LfBus = bus + + public class ImpedanceLinearResolutionResultBiphased { + + private LfBus bus2; + + private int numBus2Fault; // stored to easily access the extraction vector at bus2 to get the full voltage export if required + + private double v2x; + private double v2y; + + private double z22txx; //additional impedance matrix terms to keep as they are needed for biphased common support faults + private double z22txy; + private double z22tyx; + private double z22tyy; + + private double z21txx; + private double z21txy; + private double z21tyx; + private double z21tyy; + + private double z12txx; + private double z12txy; + private double z12tyx; + private double z12tyy; + + private Map dE2; // store necessary data to compute voltage delta of the full grid for a common support biphased fault + // the key stores the number of the bus2 for a biphased common support fault, the value stores the resolved value [Res] = inv(Y)*[En], with n of vector [En] corresponding to the studied short circuit fault and values at lines of [Res] corresponding real and imaginary parts at bus2 in key + + ImpedanceLinearResolutionResultBiphased(LfBus bus2, double v2x, double v2y, double z22txx, double z22txy, double z22tyx, double z22tyy, + double z21txx, double z21txy, double z21tyx, double z21tyy, + double z12txx, double z12txy, double z12tyx, double z12tyy, int numBus2Fault) { + this.bus2 = bus2; + + this.numBus2Fault = numBus2Fault; + + this.v2x = v2x; + this.v2y = v2y; + + this.z22txx = z22txx; + this.z22txy = z22txy; + this.z22tyx = z22tyx; + this.z22tyy = z22tyy; + + this.z21txx = z21txx; + this.z21txy = z21txy; + this.z21tyx = z21tyx; + this.z21tyy = z21tyy; + + this.z12txx = z12txx; + this.z12txy = z12txy; + this.z12tyx = z12tyx; + this.z12tyy = z12tyy; + + } + + public void updateWithVoltagesdelta2(AdmittanceMatrix y, DenseMatrix dEn) { + dE2 = y.getDeltaV(dEn, numBus2Fault); + //eqSysFeeders = feeders; // TODO : check if feeder are necessary for v2 : contains necessary data to update the contribution of feeders for each shortcircuit + } + + public LfBus getBus2() { + return bus2; + } + + public double getZ12txx() { + return z12txx; + } + + public double getZ12txy() { + return z12txy; + } + + public double getZ12tyx() { + return z12tyx; + } + + public double getZ12tyy() { + return z12tyy; + } + + public double getZ21txx() { + return z21txx; + } + + public double getZ21txy() { + return z21txy; + } + + public double getZ21tyx() { + return z21tyx; + } + + public double getZ21tyy() { + return z21tyy; + } + + public double getZ22txx() { + return z22txx; + } + + public double getZ22txy() { + return z22txy; + } + + public double getZ22tyx() { + return z22tyx; + } + + public double getZ22tyy() { + return z22tyy; + } + + public double getV2x() { + return v2x; + } + + public double getV2y() { + return v2y; + } + + public int getNumBus2Fault() { + return numBus2Fault; + } + + public Map getDv2() { + return dE2; + } + } + + ImpedanceLinearResolutionResult(LfBus bus, double rEq11, double rEq22, double xEq12, double xEq21, double ethx, double ethy) { + this.bus = bus; + this.rEq11 = rEq11; + this.rEq22 = rEq22; + this.xEq12 = xEq12; + this.xEq21 = xEq21; + + this.ethx = ethx; + this.ethy = ethy; + + checkResults(); + } + + public LfBus getBus() { + return bus; + } + + public double getXthz12() { + return xEq12; + } + + public double getRthz11() { + return rEq11; + } + + public double getEthr() { + return ethx; + } + + public double getEthi() { + return ethy; + } + + public Map getDv() { + return dE; + } + + public DenseMatrix getEnBus() { + return enBus; + } + + public EquationSystemFeeders getEqSysFeeders() { + return eqSysFeeders; + } + + public List getBiphasedResultsAtBus() { + return biphasedResultsAtBus; + } + + private void checkResults() { + double epsilon = 0.00001; + if (Math.abs(rEq11 - rEq22) > epsilon) { + throw new IllegalArgumentException("Impedance block values rth : z11 and Z22 of node {" + bus.getId() + "} have inconsitant values z11= " + rEq11 + " y1i2i=" + rEq22); + } + + if (Math.abs(xEq12 - xEq21) > epsilon) { + throw new IllegalArgumentException("Impedance block values xth : z12 and Z21 of node {" + bus.getId() + "} have inconsitant values z12= " + xEq12 + " z21=" + xEq21); + } + } + + public void updateEnBus(double enBus11, double enBus12, double enBus21, double enBus22) { + enBus = parameters.getMatrixFactory().create(2, 2, 4).toDense(); + enBus.add(0, 0, enBus11); + enBus.add(0, 1, enBus12); + enBus.add(1, 0, enBus21); + enBus.add(1, 1, enBus22); + } + + public void updateWithVoltagesdelta(AdmittanceMatrix y, DenseMatrix dEn, int numDef, EquationSystemFeeders feeders) { + dE = y.getDeltaV(dEn, numDef); + eqSysFeeders = feeders; // contains necessary data to update the contribution of feeders for each short circuit + } + + public void printResult() { + System.out.println(" Zth(" + bus.getId() + ") = "); + System.out.println(" [ rth=" + rEq11 + " -xth=" + -xEq12 + "]"); + System.out.println(" [ xth=" + xEq21 + " rth=" + rEq22 + "]"); + + if (parameters.isVoltageUpdate()) { + /*for (Map.Entry b : dvr1.entrySet()) { + int busi = b.getKey(); + double dv = b.getValue(); + System.out.println(" busNum[" + busi + "] : dvr = " + dv); + System.out.println(" busNum[" + busi + "] : dvi = " + dvi1.get(busi)); + }*/ + } + } + + public void addBiphasedResult(LfBus bus2, double initV2x, double initV2y, double z22txx, double z22txy, double z22tyx, double z22tyy, + double z21txx, double z21txy, double z21tyx, double z21tyy, + double z12txx, double z12txy, double z12tyx, double z12tyy, int numBus2Fault) { + // numBus2Fault is store to easily get the extraction vector for the second bus, in order to compute the full voltage exprt if required + ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased biphasedResult = new ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased(bus2, initV2x, initV2y, z22txx, z22txy, z22tyx, z22tyy, + z21txx, z21txy, z21tyx, z21tyy, + z12txx, z12txy, z12tyx, z12tyy, numBus2Fault); + + if (biphasedResultsAtBus == null) { + biphasedResultsAtBus = new ArrayList<>(); + } + biphasedResultsAtBus.add(biphasedResult); + } + } + + public static LfBus getLfBusFromIidmBranch(String iidmBranchId, int branchSide, LfNetwork lfNetwork) { + LfBus bus = null; + for (LfBranch lfBranch : lfNetwork.getBranches()) { + String branchId = lfBranch.getId(); + LfBranch.BranchType lfType = lfBranch.getBranchType(); + + if (lfType == LfBranch.BranchType.LINE || lfType == LfBranch.BranchType.TRANSFO_2) { + if (iidmBranchId.equals(branchId)) { + if (branchSide == 1) { + bus = lfBranch.getBus1(); + + } else { + bus = lfBranch.getBus2(); + } + break; + } + } else if (lfType == LfBranch.BranchType.TRANSFO_3_LEG_1) { + String legId = iidmBranchId + "_leg_1"; + if (legId.equals(branchId) && branchSide == 1) { + // TODO : check that for each leg, side 2 bus is always the star bus of the T3W + bus = lfBranch.getBus1(); + break; + } + + } else if (lfType == LfBranch.BranchType.TRANSFO_3_LEG_2) { + String legId = iidmBranchId + "_leg_2"; + if (legId.equals(branchId) && branchSide == 2) { + // TODO : check that for each leg, side 2 bus is always the star bus of the T3W + bus = lfBranch.getBus1(); + break; + } + + } else if (lfType == LfBranch.BranchType.TRANSFO_3_LEG_3) { + String legId = iidmBranchId + "_leg_3"; + if (legId.equals(branchId) && branchSide == 3) { + // TODO : check that for each leg, side 2 bus is always the star bus of the T3W + bus = lfBranch.getBus1(); + break; + } + } + } + return bus; + + } + + public void run() { + + LfNetwork lfNetwork = networks.get(0); + lfNetworkResult = lfNetwork; + + EquationSystemFeeders equationsSystemFeeders = new EquationSystemFeeders(); + EquationSystem equationSystem + = AdmittanceEquationSystem.create(lfNetwork, new VariableSet<>(), parameters.getAdmittanceType(), parameters.getTheveninVoltageProfileType(), parameters.getTheveninPeriodType(), parameters.isTheveninIgnoreShunts(), equationsSystemFeeders, parameters.getAcLoadFlowParameters()); + + //Get bus by voltage level + List inputBusses = new ArrayList<>(); + for (CalculationLocation faultBranchLocationInfo : parameters.getCalculationLocations()) { + String iidmBranchId = faultBranchLocationInfo.getIidmBusInfo().getKey(); + int branchSide = faultBranchLocationInfo.getIidmBusInfo().getValue(); + + LfBus bus = getLfBusFromIidmBranch(iidmBranchId, branchSide, lfNetwork); + if (bus != null) { + inputBusses.add(bus); + faultBranchLocationInfo.setLfBusInfo(bus.getId()); + } + } + + // case it is a biphased common support input, supposing that the number of such input contingencies is low + List> biphasedinputBusses = new ArrayList<>(); + if (parameters.getBiphasedCalculationLocations() != null) { // TODO : change name + for (CalculationLocation biphasedFaultBranchLocationInfo : parameters.getBiphasedCalculationLocations()) { + + String iidmBranchId = biphasedFaultBranchLocationInfo.getIidmBusInfo().getKey(); + int branchSide = biphasedFaultBranchLocationInfo.getIidmBusInfo().getValue(); + + String iidmBranch2Id = biphasedFaultBranchLocationInfo.getIidmBus2Info().getKey(); + int branch2Side = biphasedFaultBranchLocationInfo.getIidmBus2Info().getValue(); + + LfBus bus1 = getLfBusFromIidmBranch(iidmBranchId, branchSide, lfNetwork); + LfBus bus2 = getLfBusFromIidmBranch(iidmBranch2Id, branch2Side, lfNetwork); + + if (bus1 != null && bus2 != null) { + Pair bussesPair = new Pair<>(bus1, bus2); + biphasedinputBusses.add(bussesPair); + biphasedFaultBranchLocationInfo.setLfBusInfo(bus1.getId()); + } + } + } + + // Addition of biphased faults in the inputBusses : TODO : check how biphased inputs are not used for other faults + for (Pair pairBusses : biphasedinputBusses) { + LfBus bus1 = pairBusses.getKey(); + LfBus bus2 = pairBusses.getValue(); + if (!inputBusses.contains(bus1)) { + inputBusses.add(bus1); + } + if (!inputBusses.contains(bus2)) { + inputBusses.add(bus2); + } + } + + // Build of the structure of the extraction matrices + // <-------------------> N + // ^ [ ..... 0 0 ..... ] + // | [ 0 0 ] + // | [ ] M = y.getRowCount() + // [En] = M | [ 1 0 ] N = 2 * inputBusses.size() + // | [ 0 1 ] + // | [ ] + // | [ 0 0 ] + // - [ ..... 0 0 ......] + // ^ ^ + // En_x_k | + // En_y_k + // + // - En_x_k is the vector t[ 0 0 ... 0 0 1 0 0 0 ... 0 0 ] where 1 corresponds to the line/column of the bus k where the real part of Z matrix is modelled + // - En_y_k is the vector t[ 0 0 ... 0 0 0 1 0 0 ... 0 0 ] where 1 corresponds to the line/column of the bus k where the imaginary part of Z matrix is modelled + + // Step 1 : build the extraction vectors + try (AdmittanceMatrix yd = new AdmittanceMatrix(equationSystem, parameters.getMatrixFactory(), lfNetwork)) { + + DenseMatrix en = new DenseMatrix(yd.getRowCount(), 2 * inputBusses.size()); + List tEn2Col = new ArrayList<>(); + + int numBusFault = 0; + for (LfBus lfBus : inputBusses) { + + int yRowx = yd.getRowBus(lfBus.getNum(), EquationType.BUS_YR); + int yColx = yd.getColBus(lfBus.getNum(), VariableType.BUS_VR); + int yRowy = yd.getRowBus(lfBus.getNum(), EquationType.BUS_YI); + int yColy = yd.getColBus(lfBus.getNum(), VariableType.BUS_VI); + + //Step 2: fill the extraction matrices based on each extraction vector + // [tEn_x][1,j]= 1 if j = yColRth and 0 else + // [tEn_y][1,j]= 1 if j = yColXth and 0 else + //tEn.add(2 * numBusFault, yColx, 1.0); + //tEn.add(2 * numBusFault + 1, yColy, 1.0); + + //the extraction matrix tEn is replaced by a list to directly get the elements rth and xth in inv(Y) * En as tEn is very sparse + tEn2Col.add(yColx); + tEn2Col.add(yColy); + + // [En_x][i,1]= 1 if i = yRowRth and 0 else + // [En_y][i,1]= 1 if i = yRowXth and 0 else + en.add(yRowx, 2 * numBusFault, 1.0); + en.add(yRowy, 2 * numBusFault + 1, 1.0); + + numBusFault++; + } + + //Step 3 : use the LU inversion of Y to get Rth and Xth + yd.solveTransposed(en); + + // Each diagonal bloc of tEn * inv(Y) * En is: + // [Zkk] = [ r -x ] + // [ x r ] + + //DenseMatrix z = (DenseMatrix) tEn.times(en); + + double ethx = 1.0; + double ethy = 0.0; + numBusFault = 0; + for (LfBus lfBus : inputBusses) { + + int yRow1x = yd.getRowBus(lfBus.getNum(), EquationType.BUS_YR); + int yRow1y = yd.getRowBus(lfBus.getNum(), EquationType.BUS_YI); + + if (parameters.getTheveninVoltageProfileType() == AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED) { + ethx = lfBus.getV() * Math.cos(lfBus.getAngle()); + ethy = lfBus.getV() * Math.sin(lfBus.getAngle()); + } + + //this is equivalent to get the diagonal blocks of tEn * inv(Y) * En but taking advantage of the sparsity of tEn + ImpedanceLinearResolutionResult res = new ImpedanceLinearResolutionResult(lfBus, + en.get(tEn2Col.get(2 * numBusFault), 2 * numBusFault), + en.get(tEn2Col.get(1 + 2 * numBusFault), 1 + 2 * numBusFault), + -en.get(tEn2Col.get(2 * numBusFault), 1 + 2 * numBusFault), + en.get(tEn2Col.get(1 + 2 * numBusFault), 2 * numBusFault), + ethx, + ethy); + + //step 4 : add deltaVoltage vectors if required + //extract values at the faulting bus that will be used to compute the post-fault voltage delta at bus + // This equivalent to compute t[En]*inv(Y)*[En] in : + // [ Vx ] [ Ix ] [ Vx_init ] + // [ Vy ] = -t[En]*inv(Y)*[En] * [ Iy ] + t[En] * [ Vy_init ] + double enBusxx = en.get(yRow1x, 2 * numBusFault); + double enBusyx = en.get(yRow1x, 2 * numBusFault + 1); + double enBusxy = en.get(yRow1y, 2 * numBusFault); + double enBusyy = en.get(yRow1y, 2 * numBusFault + 1); + + res.updateEnBus(enBusxx, enBusyx, enBusxy, enBusyy); + + // handle biphased common support faults extra data + for (Pair pairBusses : biphasedinputBusses) { + LfBus bus1 = pairBusses.getKey(); + if (bus1 == lfBus) { + // lfbus is also the first bus for a biphased common support, we store as an extension necessary additional data for the linear resolution post-processing + LfBus bus2 = pairBusses.getValue(); + int yCol1x = yd.getColBus(lfBus.getNum(), VariableType.BUS_VR); + int yCol1y = yd.getColBus(lfBus.getNum(), VariableType.BUS_VI); + int yCol2x = yd.getColBus(bus2.getNum(), VariableType.BUS_VR); + int yCol2y = yd.getColBus(bus2.getNum(), VariableType.BUS_VI); + + int numBus2Fault = 0; // get the right column of extraction matrix of bus2 + boolean bus2found = false; + for (LfBus lfBus2 : inputBusses) { + if (lfBus2 == bus2) { + bus2found = true; + break; + } + numBus2Fault++; + } + + if (!bus2found) { + throw new IllegalArgumentException(" Biphased fault second bus = " + bus2.getId() + " : not found in the extraction matrix"); + } + + double enZ22txx = en.get(yCol2x, 2 * numBus2Fault); + double enZ22tyx = en.get(yCol2y, 2 * numBus2Fault); + double enZ22txy = en.get(yCol2x, 2 * numBus2Fault + 1); + double enZ22tyy = en.get(yCol2y, 2 * numBus2Fault + 1); + + double enZ21txx = en.get(yCol2x, 2 * numBusFault); + double enZ21tyx = en.get(yCol2y, 2 * numBusFault); + double enZ21txy = en.get(yCol2x, 2 * numBusFault + 1); + double enZ21tyy = en.get(yCol2y, 2 * numBusFault + 1); + + double enZ12txx = en.get(yCol1x, 2 * numBus2Fault); + double enZ12tyx = en.get(yCol1y, 2 * numBus2Fault); + double enZ12txy = en.get(yCol1x, 2 * numBus2Fault + 1); + double enZ12tyy = en.get(yCol1y, 2 * numBus2Fault + 1); + + double eth2x = 1.0; + double eth2y = 0.; + if (parameters.getTheveninVoltageProfileType() == AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED) { + eth2x = bus2.getV() * Math.cos(lfBus.getAngle()); + eth2y = bus2.getV() * Math.sin(lfBus.getAngle()); + } + + res.addBiphasedResult(bus2, eth2x, eth2y, enZ22txx, enZ22txy, enZ22tyx, enZ22tyy, + enZ21txx, enZ21txy, enZ21tyx, enZ21tyy, + enZ12txx, enZ12txy, enZ12tyx, enZ12tyy, numBus2Fault); + } + } + + //if required, do the same for all busses from the grid + if (parameters.isVoltageUpdate()) { + // This equivalent to store inv(Y)*[En] + res.updateWithVoltagesdelta(yd, en, numBusFault, equationsSystemFeeders); + if (res.biphasedResultsAtBus != null) { + // update for each biphased common support fault + for (ImpedanceLinearResolutionResult.ImpedanceLinearResolutionResultBiphased biphasedResultPart : res.biphasedResultsAtBus) { + biphasedResultPart.updateWithVoltagesdelta2(yd, en); + } + } + } + + //res.printResult(); + + this.results.add(res); + numBusFault++; + } + } + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/ImpedanceLinearResolutionParameters.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/ImpedanceLinearResolutionParameters.java new file mode 100644 index 00000000..90cf27f0 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/ImpedanceLinearResolutionParameters.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; + +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ImpedanceLinearResolutionParameters { + + /*public enum AdmittanceLinearVoltageProfileType { + CALCULATED, // use the computed values at nodes to compute Zth and Eth + NOMINAL; // use the nominal voltage values at nodes to get Zth and Eth + } + + public enum AdmittanceLinearPeriodType { + ADM_SUB_TRANSIENT, //uses subTransient parameters x"d + ADM_TRANSIENT, //uses transient parameters x'd + ADM_STEADY_STATE; + }*/ + + public static final double XSUBTRANSIENT = 0.2; //default value if data not available + + private final boolean voltageUpdate; + + private final AcLoadFlowParameters acLoadFlowParameters; + + private final MatrixFactory matrixFactory; + + private final List calculationLocations; + + private List biphasedCalculationLocations; + + private AdditionalDataInfo additionalDataInfo; + + private final boolean ignoreShunts; + + private final AdmittanceEquationSystem.AdmittanceVoltageProfileType voltageProfileType; + + private final AdmittanceEquationSystem.AdmittancePeriodType periodType; + + private final AdmittanceEquationSystem.AdmittanceType admittanceType; + + public ImpedanceLinearResolutionParameters(AcLoadFlowParameters acLoadFlowParameters, MatrixFactory matrixFactory, List calculationLocations, boolean voltageUpdate, + AdmittanceEquationSystem.AdmittanceVoltageProfileType theveninVoltageProfileType, AdmittanceEquationSystem.AdmittancePeriodType theveninPeriodType, AdmittanceEquationSystem.AdmittanceType admittanceType, + boolean theveninIgnoreShunts, AdditionalDataInfo additionalDataInfo) { + this.acLoadFlowParameters = Objects.requireNonNull(acLoadFlowParameters); + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.calculationLocations = Objects.requireNonNull(calculationLocations); + this.voltageUpdate = voltageUpdate; + this.ignoreShunts = theveninIgnoreShunts; + this.voltageProfileType = theveninVoltageProfileType; + this.additionalDataInfo = additionalDataInfo; + this.periodType = theveninPeriodType; + this.admittanceType = admittanceType; + } + + public ImpedanceLinearResolutionParameters(AcLoadFlowParameters acLoadFlowParameters, MatrixFactory matrixFactory, List calculationLocations, boolean voltageUpdate, + AdmittanceEquationSystem.AdmittanceVoltageProfileType theveninVoltageProfileType, AdmittanceEquationSystem.AdmittancePeriodType theveninPeriodType, AdmittanceEquationSystem.AdmittanceType admittanceType, + boolean theveninIgnoreShunts, AdditionalDataInfo additionalDataInfo, List biphasedVoltageLevelLocation) { + this(acLoadFlowParameters, matrixFactory, calculationLocations, voltageUpdate, theveninVoltageProfileType, theveninPeriodType, admittanceType, theveninIgnoreShunts, additionalDataInfo); + this.biphasedCalculationLocations = biphasedVoltageLevelLocation; + + } + + public AcLoadFlowParameters getAcLoadFlowParameters() { + return acLoadFlowParameters; + } + + public MatrixFactory getMatrixFactory() { + return matrixFactory; + } + + public List getCalculationLocations() { + return calculationLocations; + } + + public boolean isVoltageUpdate() { + return voltageUpdate; + } + + public boolean isTheveninIgnoreShunts() { + return ignoreShunts; + } + + public AdmittanceEquationSystem.AdmittanceVoltageProfileType getTheveninVoltageProfileType() { + return voltageProfileType; + } + + public List getBiphasedCalculationLocations() { + return biphasedCalculationLocations; + } + + public AdditionalDataInfo getAdditionalDataInfo() { + return additionalDataInfo; + } + + public AdmittanceEquationSystem.AdmittancePeriodType getTheveninPeriodType() { + return periodType; + } + + public AdmittanceEquationSystem.AdmittanceType getAdmittanceType() { + return admittanceType; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/LinearEquationTerm.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/LinearEquationTerm.java new file mode 100644 index 00000000..5c99b293 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/LinearEquationTerm.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.equations.EquationTerm; +import com.powsybl.openloadflow.equations.Variable; + +/** + * @author Geoffroy Jamgotchian + */ +public interface LinearEquationTerm extends EquationTerm { + + double getCoefficient(Variable variable); +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/TheveninEquivalent.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/TheveninEquivalent.java new file mode 100644 index 00000000..715b4aeb --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/TheveninEquivalent.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import org.apache.commons.math3.util.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class TheveninEquivalent { + + private final Network network; + + private final TheveninEquivalentParameters parameters; + + public ImpedanceLinearResolution impedanceLinearResolution; + + public TheveninEquivalent(Network network, TheveninEquivalentParameters parameters) { + this.network = Objects.requireNonNull(network); + this.parameters = Objects.requireNonNull(parameters); + impedanceLinearResolution = new ImpedanceLinearResolution(network, generateAdmittanceLinearResolutionParam(network, parameters)); + } + + public ImpedanceLinearResolution getImpedanceLinearResolution() { + return impedanceLinearResolution; + } + + public void run() { + impedanceLinearResolution.run(); + + } + + private static Pair buildFaultBranchFromBusId(String busId, Network tmpNetwork) { + // TODO : remove code duplication with AbstractShortCircuitEngine + Bus bus = tmpNetwork.getBusBreakerView().getBus(busId); + String branchId = ""; + int branchSide = 0; + boolean isFound = false; + for (Branch branch : tmpNetwork.getBranches()) { + Bus bus1 = branch.getTerminal1().getBusBreakerView().getBus(); + Bus bus2 = branch.getTerminal2().getBusBreakerView().getBus(); + if (bus == bus1) { + branchId = branch.getId(); + branchSide = 1; + isFound = true; + break; + } else if (bus == bus2) { + branchId = branch.getId(); + branchSide = 2; + isFound = true; + break; + } + } + + if (!isFound) { + System.out.println(" input CC Bus " + busId + " could not be associated with a bipole"); + } + + return new Pair<>(branchId, branchSide); + } + + private static Pair buildFaultT3WbranchFromBusId(String busId, Network tmpNetwork) { + // TODO : remove code duplication with AbstractShortCircuitEngine + Bus bus = tmpNetwork.getBusBreakerView().getBus(busId); + String branchId = ""; + int legNum = 0; + boolean isFound = false; + for (ThreeWindingsTransformer t3w : tmpNetwork.getThreeWindingsTransformers()) { + Bus bus1 = t3w.getLeg1().getTerminal().getBusBreakerView().getBus(); + Bus bus2 = t3w.getLeg2().getTerminal().getBusBreakerView().getBus(); + Bus bus3 = t3w.getLeg3().getTerminal().getBusBreakerView().getBus(); + + if (bus == bus1) { + branchId = t3w.getId(); + legNum = 1; + isFound = true; + break; + } else if (bus == bus2) { + branchId = t3w.getId(); + legNum = 2; + isFound = true; + break; + } else if (bus == bus3) { + branchId = t3w.getId(); + legNum = 3; + isFound = true; + break; + } + } + + if (!isFound) { + System.out.println(" Bus " + busId + " could not be associated with a tripole"); + } + + return new Pair<>(branchId, legNum); + } + + private static ImpedanceLinearResolutionParameters generateAdmittanceLinearResolutionParam(Network network, TheveninEquivalentParameters parameters) { + + boolean voltageUpdate = parameters.isVoltageUpdate(); // TODO: check that in example, no voltage update is asked + + AcLoadFlowParameters acLoadFlowParameters = parameters.getAcLoadFlowParameters(); + + AdmittanceEquationSystem.AdmittanceVoltageProfileType admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.CALCULATED; //TODO: put nominal if Thevenin requires nominal voltage use + if (parameters.getTheveninVoltageProfileType() == TheveninEquivalentParameters.TheveninVoltageProfileType.NOMINAL) { + admittanceVoltageProfileType = AdmittanceEquationSystem.AdmittanceVoltageProfileType.NOMINAL; + } + + AdmittanceEquationSystem.AdmittancePeriodType periodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_TRANSIENT; + if (parameters.getTheveninPeriodType() == TheveninEquivalentParameters.TheveninPeriodType.THEVENIN_SUB_TRANSIENT) { + periodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_SUB_TRANSIENT; + } else if (parameters.getTheveninPeriodType() == TheveninEquivalentParameters.TheveninPeriodType.THEVENIN_STEADY_STATE) { + periodType = AdmittanceEquationSystem.AdmittancePeriodType.ADM_STEADY_STATE; + } + + List locations = new ArrayList<>(); + for (CalculationLocation calculationLocation : parameters.getLocations()) { + String busName = calculationLocation.getBusLocation(); + Pair branchFaultInfo = buildFaultBranchFromBusId(busName, network); + + if (branchFaultInfo.getKey().equals("")) { + // Bus not found in branches, try three windings transformers + branchFaultInfo = buildFaultT3WbranchFromBusId(busName, network); + } + + calculationLocation.setIidmBusInfo(branchFaultInfo); + locations.add(calculationLocation); + } + + return new ImpedanceLinearResolutionParameters(acLoadFlowParameters, parameters.getMatrixFactory(), + locations, voltageUpdate, admittanceVoltageProfileType, periodType, + AdmittanceEquationSystem.AdmittanceType.ADM_THEVENIN, parameters.isTheveninIgnoreShunts(), + parameters.getAdditionalDataInfo()); + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/TheveninEquivalentParameters.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/TheveninEquivalentParameters.java new file mode 100644 index 00000000..51c4f532 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/TheveninEquivalentParameters.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; + +import java.util.List; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class TheveninEquivalentParameters { + + public enum TheveninVoltageProfileType { + CALCULATED, // use the computed values at nodes to compute Zth and Eth + NOMINAL; // use the nominal voltage values at nodes to get Zth and Eth + } + + public enum TheveninPeriodType { + THEVENIN_SUB_TRANSIENT, //uses subTransient parameters x"d + THEVENIN_TRANSIENT, //uses transient parameters x'd + THEVENIN_STEADY_STATE; + } + + public static final double XSUBTRANSIENT = 0.2; //default value if data not available + + private final boolean voltageUpdate; + + private final AcLoadFlowParameters acLoadFlowParameters; + + private final MatrixFactory matrixFactory; + + private final List theveninCalculationLocation; + + private AdditionalDataInfo additionalDataInfo; + + private final boolean theveninIgnoreShunts; + + private final TheveninVoltageProfileType theveninVoltageProfileType; + + private final TheveninPeriodType theveninPeriodType; + + public TheveninEquivalentParameters(AcLoadFlowParameters acLoadFlowParameters, MatrixFactory matrixFactory, List voltageLevels, boolean voltageUpdate, TheveninVoltageProfileType theveninVoltageProfileType, TheveninPeriodType theveninPeriodType, boolean theveninIgnoreShunts, AdditionalDataInfo additionalDataInfo) { + this.acLoadFlowParameters = Objects.requireNonNull(acLoadFlowParameters); + this.matrixFactory = Objects.requireNonNull(matrixFactory); + this.theveninCalculationLocation = Objects.requireNonNull(voltageLevels); + this.voltageUpdate = voltageUpdate; + this.theveninIgnoreShunts = theveninIgnoreShunts; + this.theveninVoltageProfileType = theveninVoltageProfileType; + this.additionalDataInfo = additionalDataInfo; + this.theveninPeriodType = theveninPeriodType; + } + + public AcLoadFlowParameters getAcLoadFlowParameters() { + return acLoadFlowParameters; + } + + public MatrixFactory getMatrixFactory() { + return matrixFactory; + } + + public List getLocations() { + return theveninCalculationLocation; + } + + public boolean isVoltageUpdate() { + return voltageUpdate; + } + + public boolean isTheveninIgnoreShunts() { + return theveninIgnoreShunts; + } + + public TheveninVoltageProfileType getTheveninVoltageProfileType() { + return theveninVoltageProfileType; + } + + public AdditionalDataInfo getAdditionalDataInfo() { + return additionalDataInfo; + } + + public TheveninPeriodType getTheveninPeriodType() { + return theveninPeriodType; + } + +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/VariableType.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/VariableType.java new file mode 100644 index 00000000..c611901d --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/VariableType.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.openloadflow.equations.Quantity; +import com.powsybl.openloadflow.network.ElementType; + +/** + * @author Geoffroy Jamgotchian + */ +public enum VariableType implements Quantity { + BUS_VR("vr"), // real part + BUS_VI("vi"); // imaginary part + + private final String symbol; + + VariableType(String symbol) { + this.symbol = symbol; + } + + @Override + public String getSymbol() { + return symbol; + } + + @Override + public ElementType getElementType() { + return ElementType.BUS; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/AdditionalDataInfo.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/AdditionalDataInfo.java new file mode 100644 index 00000000..7b9ac238 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/AdditionalDataInfo.java @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Jean-Baptiste Heyberger + */ +//This class is used to carry additional info necessary for shortcircuit calculation that are not yet modeled in the iidm network or in the short circuit API +public class AdditionalDataInfo { + + public enum LegType { + Y, + Y_GROUNDED, + DELTA; + } + + private Map machineIdToTransRd; //gives the values of transient resistance + private Map machineIdToSubTransRd; //gives the values of sub-transient resistance + private Map machineToGround; + private Map machineCoeffRo; + private Map machineCoeffXo; + + private Map leg1type; + private Map leg2type; + private Map leg3type; + + private Map lineIdToCoeffRo; // Ro = Rd * CoeffRo + private Map lineIdToCoeffXo; + + private Map tfo2wIdToCoeffRo; + private Map tfo2wIdToCoeffXo; + + private Map feederIdToCoeffRo; + private Map feederIdToCoeffXo; + + private Map tfo3wIdToLeg1CoeffRo; + private Map tfo3wIdToLeg1CoeffXo; + private Map tfo3wIdToLeg2CoeffRo; + private Map tfo3wIdToLeg2CoeffXo; + private Map tfo3wIdToLeg3CoeffRo; + private Map tfo3wIdToLeg3CoeffXo; + + private Map tfo2wIdToFreeFluxes; // free fluxes mean that magnetizing impedance Zm is infinite, by default, fluxes are forced and Zm exists + private Map tfo3wIdLeg1ToFreeFluxes; + private Map tfo3wIdLeg2ToFreeFluxes; + private Map tfo3wIdLeg3ToFreeFluxes; + + public AdditionalDataInfo() { + this.machineIdToTransRd = new HashMap<>(); //empty list by default, which means that Rd will always be 0. in the following computations + this.machineIdToSubTransRd = new HashMap<>(); + this.machineToGround = new HashMap<>(); + + } + + public AdditionalDataInfo(Map machineIdToRd, Map machineIdToSubTransRd) { + this.machineIdToTransRd = machineIdToRd; + this.machineIdToSubTransRd = machineIdToSubTransRd; + this.machineToGround = new HashMap<>(); + } + + public AdditionalDataInfo(Map machineIdToRd, Map machineIdToSubTransRd, Map machineToGround) { + this.machineIdToTransRd = machineIdToRd; + this.machineIdToSubTransRd = machineIdToSubTransRd; + this.machineToGround = machineToGround; + } + + public AdditionalDataInfo(Map machineIdToRd, Map machineIdToSubTransRd, Map machineToGround, Map leg1type, Map leg2type) { + this.machineIdToTransRd = machineIdToRd; + this.machineIdToSubTransRd = machineIdToSubTransRd; + this.machineToGround = machineToGround; + this.leg1type = leg1type; + this.leg2type = leg2type; + } + + public AdditionalDataInfo(Map machineIdToRd, Map machineIdToSubTransRd, Map machineToGround, + Map leg1type, Map leg2type, Map lineIdToCoeffRo, Map lineIdToCoeffXo, Map tfo2wIdToCoeffRo, Map tfo2wIdToCoeffXo) { + this(machineIdToRd, machineIdToSubTransRd, machineToGround, leg1type, leg2type); + this.lineIdToCoeffRo = lineIdToCoeffRo; + this.lineIdToCoeffXo = lineIdToCoeffXo; + this.tfo2wIdToCoeffRo = tfo2wIdToCoeffRo; + this.tfo2wIdToCoeffXo = tfo2wIdToCoeffXo; + + } + + public AdditionalDataInfo(Map machineIdToRd, Map machineIdToSubTransRd, Map machineToGround, + Map leg1type, Map leg2type, Map lineIdToCoeffRo, Map lineIdToCoeffXo, Map tfo2wIdToCoeffRo, Map tfo2wIdToCoeffXo, + Map feederIdToCoeffRo, Map feederIdToCoeffXo, Map machineCoeffRo, Map machineCoeffXo, Map leg3type, + Map tfo3wIdToLeg1CoeffRo, Map tfo3wIdToLeg1CoeffXo, + Map tfo3wIdToLeg2CoeffRo, Map tfo3wIdToLeg2CoeffXo, + Map tfo3wIdToLeg3CoeffRo, Map tfo3wIdToLeg3CoeffXo, + Map tfo2wIdToFreeFluxes, Map tfo3wIdLeg1ToFreeFluxes, Map tfo3wIdLeg2ToFreeFluxes, Map tfo3wIdLeg3ToFreeFluxes) { + this(machineIdToRd, machineIdToSubTransRd, machineToGround, leg1type, leg2type, lineIdToCoeffRo, lineIdToCoeffXo, tfo2wIdToCoeffRo, tfo2wIdToCoeffXo); + this.feederIdToCoeffRo = feederIdToCoeffRo; // not used yet, grounded loads are modelled as machines for now + this.feederIdToCoeffXo = feederIdToCoeffXo; // not used yet, grounded loads are modelled as machines for now + this.machineCoeffRo = machineCoeffRo; + this.machineCoeffXo = machineCoeffXo; + this.leg3type = leg3type; + this.tfo3wIdToLeg1CoeffRo = tfo3wIdToLeg1CoeffRo; + this.tfo3wIdToLeg1CoeffXo = tfo3wIdToLeg1CoeffXo; + this.tfo3wIdToLeg2CoeffRo = tfo3wIdToLeg2CoeffRo; + this.tfo3wIdToLeg2CoeffXo = tfo3wIdToLeg2CoeffXo; + this.tfo3wIdToLeg3CoeffRo = tfo3wIdToLeg3CoeffRo; + this.tfo3wIdToLeg3CoeffXo = tfo3wIdToLeg3CoeffXo; + this.tfo2wIdToFreeFluxes = tfo2wIdToFreeFluxes; + this.tfo3wIdLeg1ToFreeFluxes = tfo3wIdLeg1ToFreeFluxes; + this.tfo3wIdLeg2ToFreeFluxes = tfo3wIdLeg2ToFreeFluxes; + this.tfo3wIdLeg3ToFreeFluxes = tfo3wIdLeg3ToFreeFluxes; + + } + + public Map getMachineIdToTransRd() { + return machineIdToTransRd; + } + + public Map getMachineIdToSubTransRd() { + return machineIdToSubTransRd; + } + + public Map getMachineToGround() { + return machineToGround; + } + + public Map getLeg1type() { + return leg1type; + } + + public Map getLeg2type() { + return leg2type; + } + + public Map getLeg3type() { + return leg3type; + } + + public void setMachineToGround(Map machineToGround) { + this.machineToGround = machineToGround; + } + + public Map getLineIdToCoeffRo() { + return lineIdToCoeffRo; + } + + public Map getLineIdToCoeffXo() { + return lineIdToCoeffXo; + } + + public Map getTfo2wIdToCoeffRo() { + return tfo2wIdToCoeffRo; + } + + public Map getTfo2wIdToCoeffXo() { + return tfo2wIdToCoeffXo; + } + + public Map getMachineCoeffRo() { + return machineCoeffRo; + } + + public Map getMachineCoeffXo() { + return machineCoeffXo; + } + + public Map getTfo3wIdToLeg1CoeffRo() { + return tfo3wIdToLeg1CoeffRo; + } + + public Map getTfo3wIdToLeg1CoeffXo() { + return tfo3wIdToLeg1CoeffXo; + } + + public Map getTfo3wIdToLeg2CoeffRo() { + return tfo3wIdToLeg2CoeffRo; + } + + public Map getTfo3wIdToLeg2CoeffXo() { + return tfo3wIdToLeg2CoeffXo; + } + + public Map getTfo3wIdToLeg3CoeffRo() { + return tfo3wIdToLeg3CoeffRo; + } + + public Map getTfo3wIdToLeg3CoeffXo() { + return tfo3wIdToLeg3CoeffXo; + } + + public Map getTfo2wIdToFreeFluxes() { + return tfo2wIdToFreeFluxes; + } + + public Map getTfo3wIdLeg1ToFreeFluxes() { + return tfo3wIdLeg1ToFreeFluxes; + } + + public Map getTfo3wIdLeg2ToFreeFluxes() { + return tfo3wIdLeg2ToFreeFluxes; + } + + public Map getTfo3wIdLeg3ToFreeFluxes() { + return tfo3wIdLeg3ToFreeFluxes; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitExtensions.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitExtensions.java new file mode 100644 index 00000000..955e15b3 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitExtensions.java @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuit; +import com.powsybl.openloadflow.network.LfBranch; +import com.powsybl.openloadflow.network.LfBus; +import com.powsybl.openloadflow.network.LfGenerator; +import com.powsybl.openloadflow.network.LfNetwork; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public final class ShortCircuitExtensions { + + public static final String PROPERTY_NAME = "ShortCircuit"; + + private ShortCircuitExtensions() { + } + + public static void add(Network network, List lfNetworks, AdditionalDataInfo additionalDataInfo) { + Objects.requireNonNull(network); + Objects.requireNonNull(lfNetworks); + Objects.requireNonNull(additionalDataInfo); + for (LfNetwork lfNetwork : lfNetworks) { + for (LfBus lfBus : lfNetwork.getBuses()) { + for (LfGenerator lfGenerator : lfBus.getGenerators()) { + addGeneratorExtension(network, additionalDataInfo, lfGenerator); + } + } + + for (LfBranch lfBranch : lfNetwork.getBranches()) { + switch (lfBranch.getBranchType()) { + case LINE: + addLineExtension(additionalDataInfo, lfBranch); + break; + + case TRANSFO_2: + addTransfo2Extension(additionalDataInfo, lfBranch); + break; + + case TRANSFO_3_LEG_1: + case TRANSFO_3_LEG_2: + case TRANSFO_3_LEG_3: + addTransfo3Extension(additionalDataInfo, lfBranch); + break; + + case DANGLING_LINE: + // FIXME something to do? + break; + } + } + } + } + + private static T getParameterValue(Map parameters, String id, T defaultValue) { + if (parameters != null) { + if (parameters.containsKey(id)) { + return parameters.get(id); + } + } + return defaultValue; + } + + private static void addTransfo3Extension(AdditionalDataInfo additionalDataInfo, LfBranch lfBranch) { + String t3wId = lfBranch.getOriginalIds().get(0); + + double coeffLeg1Ro = getParameterValue(additionalDataInfo.getTfo3wIdToLeg1CoeffRo(), t3wId, 1.0); + double coeffLeg1Xo = getParameterValue(additionalDataInfo.getTfo3wIdToLeg1CoeffXo(), t3wId, 1.0); + + double coeffLeg2Ro = getParameterValue(additionalDataInfo.getTfo3wIdToLeg2CoeffRo(), t3wId, 1.0); + double coeffLeg2Xo = getParameterValue(additionalDataInfo.getTfo3wIdToLeg2CoeffXo(), t3wId, 1.0); + + double coeffLeg3Ro = getParameterValue(additionalDataInfo.getTfo3wIdToLeg3CoeffRo(), t3wId, 1.0); + double coeffLeg3Xo = getParameterValue(additionalDataInfo.getTfo3wIdToLeg3CoeffXo(), t3wId, 1.0); + + boolean leg1FreeFluxes = getParameterValue(additionalDataInfo.getTfo3wIdLeg1ToFreeFluxes(), t3wId, false); + boolean leg2FreeFluxes = getParameterValue(additionalDataInfo.getTfo3wIdLeg2ToFreeFluxes(), t3wId, false); + boolean leg3FreeFluxes = getParameterValue(additionalDataInfo.getTfo3wIdLeg3ToFreeFluxes(), t3wId, false); + + ShortCircuitTransformerLeg leg1 = new ShortCircuitTransformerLeg(ShortCircuitTransformerLeg.LegConnectionType.DELTA, coeffLeg1Ro, coeffLeg1Xo, leg1FreeFluxes); // TODO : check if default connection acceptable + ShortCircuitTransformerLeg leg2 = new ShortCircuitTransformerLeg(ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED, coeffLeg2Ro, coeffLeg2Xo, leg2FreeFluxes); // TODO : check if default connection acceptable + ShortCircuitTransformerLeg leg3 = new ShortCircuitTransformerLeg(ShortCircuitTransformerLeg.LegConnectionType.DELTA, coeffLeg3Ro, coeffLeg3Xo, leg3FreeFluxes); + + AdditionalDataInfo.LegType leg1Type = getParameterValue(additionalDataInfo.getLeg1type(), t3wId, null); + if (leg1Type == AdditionalDataInfo.LegType.Y) { + leg1.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y); + } else if (leg1Type == AdditionalDataInfo.LegType.Y_GROUNDED) { + leg1.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED); + } + + AdditionalDataInfo.LegType leg2Type = getParameterValue(additionalDataInfo.getLeg2type(), t3wId, null); + if (leg2Type == AdditionalDataInfo.LegType.Y) { + leg2.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y); + } else if (leg2Type == AdditionalDataInfo.LegType.DELTA) { + leg2.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.DELTA); + } + + AdditionalDataInfo.LegType leg3Type = getParameterValue(additionalDataInfo.getLeg3type(), t3wId, null); + if (leg3Type == AdditionalDataInfo.LegType.Y) { + leg3.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y); + } else if (leg3Type == AdditionalDataInfo.LegType.Y_GROUNDED) { + leg3.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED); + } + + lfBranch.setProperty(PROPERTY_NAME, new ShortCircuitT3W(leg1, leg2, leg3)); + } + + private static void addLineExtension(AdditionalDataInfo additionalDataInfo, LfBranch lfBranch) { + String lineId = lfBranch.getOriginalIds().get(0); + + double coeffRo = getParameterValue(additionalDataInfo.getLineIdToCoeffRo(), lineId, 1.0); + double coeffXo = getParameterValue(additionalDataInfo.getLineIdToCoeffXo(), lineId, 1.0); + + lfBranch.setProperty(PROPERTY_NAME, new ShortCircuitLine(coeffRo, coeffXo)); + } + + private static void addTransfo2Extension(AdditionalDataInfo additionalDataInfo, LfBranch lfBranch) { + String t2wId = lfBranch.getOriginalIds().get(0); + + double coeffRo = getParameterValue(additionalDataInfo.getTfo2wIdToCoeffRo(), t2wId, 1.0); + double coeffXo = getParameterValue(additionalDataInfo.getTfo2wIdToCoeffXo(), t2wId, 1.0); + + ShortCircuitTransformerLeg leg1 = new ShortCircuitTransformerLeg(ShortCircuitTransformerLeg.LegConnectionType.DELTA); // TODO : check if default connection acceptable + ShortCircuitTransformerLeg leg2 = new ShortCircuitTransformerLeg(ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED); // TODO : check if default connection acceptable + + AdditionalDataInfo.LegType leg1Type = getParameterValue(additionalDataInfo.getLeg1type(), t2wId, null); + if (leg1Type == AdditionalDataInfo.LegType.Y) { + leg1.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y); + } else if (leg1Type == AdditionalDataInfo.LegType.Y_GROUNDED) { + leg1.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y_GROUNDED); + } + + AdditionalDataInfo.LegType leg2Type = getParameterValue(additionalDataInfo.getLeg2type(), t2wId, null); + if (leg2Type == AdditionalDataInfo.LegType.Y) { + leg2.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.Y); + } else if (leg2Type == AdditionalDataInfo.LegType.DELTA) { + leg2.setLegConnectionType(ShortCircuitTransformerLeg.LegConnectionType.DELTA); + } + + boolean freeFluxes = getParameterValue(additionalDataInfo.getTfo2wIdToFreeFluxes(), t2wId, false); + + lfBranch.setProperty(PROPERTY_NAME, new ShortCircuitT2W(leg1, leg2, coeffRo, coeffXo, freeFluxes)); + } + + private static void addGeneratorExtension(Network network, AdditionalDataInfo additionalDataInfo, LfGenerator lfGenerator) { + Generator generator = network.getGenerator(lfGenerator.getOriginalId()); + + GeneratorShortCircuit extension = generator.getExtension(GeneratorShortCircuit.class); + if (extension == null) { // TODO: use a default value if extension is missing + throw new PowsyblException("Short circuit extension not found for generator '" + generator.getId() + "'"); + } + double transX = extension.getDirectTransX(); + double subtransX = extension.getDirectSubtransX(); + double stepUpTfoX = extension.getStepUpTransformerX(); + + double transRd = getParameterValue(additionalDataInfo.getMachineIdToTransRd(), generator.getId(), 0.); + double subTransRd = getParameterValue(additionalDataInfo.getMachineIdToSubTransRd(), generator.getId(), 0.); + boolean toGround = getParameterValue(additionalDataInfo.getMachineToGround(), generator.getId(), false); + double coeffRo = getParameterValue(additionalDataInfo.getMachineCoeffRo(), generator.getId(), 1.); + double coeffXo = getParameterValue(additionalDataInfo.getMachineCoeffXo(), generator.getId(), 1.); + + lfGenerator.setProperty(PROPERTY_NAME, new ShortCircuitGenerator(transX, + stepUpTfoX, + ShortCircuitGenerator.MachineType.SYNCHRONOUS_GEN, + transRd, + 0., + subTransRd, + subtransX, + toGround, + 0., + 0., + coeffRo, + coeffXo)); // TODO: set the right type when info available + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitGenerator.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitGenerator.java new file mode 100644 index 00000000..b65057df --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitGenerator.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitGenerator { + + public enum MachineType { + SYNCHRONOUS_GEN, + ASYNCHRONOUS_GEN, + SYNCHRONOUS_MOTOR, + ASYNCHRONOUS_MOTOR; + } + + private final double transXd; + private final double transRd; + + private final double subTransXd; + private final double subTransRd; + + private final double stepUpTfoX; + private final double stepUpTfoR; + + private final boolean grounded; + private final double groundR; + private final double groundX; + + private final double coeffRo; // coeff used to get Ro from Rd + private final double coeffXo; // coeff used to get Xo from Xd + + private final MachineType machineType; + + public ShortCircuitGenerator(double transXd, double stepUpTfoX, MachineType machineType, double transRd, double stepUpTfoR, double subTransRd, double subTransXd, + boolean grounded, double groundR, double groundX, double coeffRo, double coeffXo) { + this.transXd = transXd; + this.stepUpTfoX = stepUpTfoX; + this.machineType = machineType; + this.transRd = transRd; + this.stepUpTfoR = stepUpTfoR; + this.subTransRd = subTransRd; + this.subTransXd = subTransXd; + this.grounded = grounded; + this.groundR = groundR; + this.groundX = groundX; + this.coeffRo = coeffRo; + this.coeffXo = coeffXo; + } + + public double getTransXd() { + return transXd; + } + + public double getSubTransXd() { + return subTransXd; + } + + public double getStepUpTfoX() { + return stepUpTfoX; + } + + public double getTransRd() { + return transRd; + } + + public double getSubTransRd() { + return subTransRd; + } + + public double getStepUpTfoR() { + return stepUpTfoR; + } + + public boolean isGrounded() { + return grounded; + } + + public double getGroundR() { + return groundR; + } + + public double getGroundX() { + return groundX; + } + + public double getCoeffRo() { + return coeffRo; + } + + public double getCoeffXo() { + return coeffXo; + } + + public void printInfos() { + System.out.println(" transXd =" + transXd + " transRd=" + transRd); + System.out.println(" subTransXd =" + subTransXd + " subtransRd=" + subTransRd); + System.out.println(" Machine Type =" + machineType + " isGrounded=" + grounded); + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitLine.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitLine.java new file mode 100644 index 00000000..9764cc90 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitLine.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitLine { + + private final double coeffXo; // Xo = Xd * coeffXo : value of the homopolar admittance (in ohms) expressed at the leg2 side + private final double coeffRo; // Ro = Rd * coeffRo : value of the homopolar resistance (in ohms) expressed at the leg2 side + + ShortCircuitLine(double coeffRo, double coeffXo) { + this.coeffRo = coeffRo; + this.coeffXo = coeffXo; + } + + public double getCoeffRo() { + return coeffRo; + } + + public double getCoeffXo() { + return coeffXo; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitT2W.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitT2W.java new file mode 100644 index 00000000..e1ee4c39 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitT2W.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitT2W { + + private final ShortCircuitTransformerLeg leg1; + private final ShortCircuitTransformerLeg leg2; + + private final double coeffXo; // Xo = Xd * coeffXo : value of the homopolar admittance (in ohms) expressed at the leg2 side + private final double coeffRo; // Ro = Rd * coeffRo : value of the homopolar resistance (in ohms) expressed at the leg2 side + + private final boolean freeFluxes; + + private final double kT; //correction factor of the Two Windings Transformer + + ShortCircuitT2W(ShortCircuitTransformerLeg leg1, ShortCircuitTransformerLeg leg2, double coeffRo, double coeffXo, boolean freeFluxes) { + this(leg1, leg2, coeffRo, coeffXo, freeFluxes, 1d); + } + + ShortCircuitT2W(ShortCircuitTransformerLeg leg1, ShortCircuitTransformerLeg leg2, double coeffRo, double coeffXo, boolean freeFluxes, double kT) { + this.leg1 = Objects.requireNonNull(leg1); + this.leg2 = Objects.requireNonNull(leg2); + this.coeffRo = coeffRo; + this.coeffXo = coeffXo; + this.freeFluxes = freeFluxes; + this.kT = kT; + } + + public ShortCircuitTransformerLeg getLeg1() { + return leg1; + } + + public ShortCircuitTransformerLeg getLeg2() { + return leg2; + } + + public double getCoeffXo() { + return coeffXo; + } + + public double getCoeffRo() { + return coeffRo; + } + + public boolean isFreeFluxes() { + return freeFluxes; + } + + public double getkT() { + return kT; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitT3W.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitT3W.java new file mode 100644 index 00000000..895f772a --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitT3W.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitT3W { + + private final ShortCircuitTransformerLeg leg1; + private final ShortCircuitTransformerLeg leg2; + private final ShortCircuitTransformerLeg leg3; + + private final double kT1; //correction factor of the Two Windings Transformer + private final double kT2; + private final double kT3; + + ShortCircuitT3W(ShortCircuitTransformerLeg leg1, ShortCircuitTransformerLeg leg2, ShortCircuitTransformerLeg leg3) { + this(leg1, leg2, leg3, 1d, 1d, 1d); + } + + ShortCircuitT3W(ShortCircuitTransformerLeg leg1, ShortCircuitTransformerLeg leg2, ShortCircuitTransformerLeg leg3, double kT1, double kT2, double kT3) { + this.leg1 = Objects.requireNonNull(leg1); + this.leg2 = Objects.requireNonNull(leg2); + this.leg3 = Objects.requireNonNull(leg3); + this.kT1 = kT1; + this.kT2 = kT2; + this.kT3 = kT3; + } + + public ShortCircuitTransformerLeg getLeg1() { + return leg1; + } + + public ShortCircuitTransformerLeg getLeg2() { + return leg2; + } + + public ShortCircuitTransformerLeg getLeg3() { + return leg3; + } + + public double getkT1() { + return kT1; + } + + public double getkT2() { + return kT2; + } + + public double getkT3() { + return kT3; + } +} diff --git a/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitTransformerLeg.java b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitTransformerLeg.java new file mode 100644 index 00000000..585a9957 --- /dev/null +++ b/simulator/util/src/main/java/com/powsybl/incubator/simulator/util/extensions/ShortCircuitTransformerLeg.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util.extensions; + +import java.util.Objects; + +/** + * @author Jean-Baptiste Heyberger + */ +public class ShortCircuitTransformerLeg { + + public enum LegConnectionType { + Y, + Y_GROUNDED, + DELTA; + } + + private LegConnectionType legConnectionType; + + private final double coeffRo; // only used for now for 3 windings transformers + private final double coeffXo; + + private final boolean freeFluxes; // only used for now for 3 windings transformers + + private final double rGround = 0; + private final double xGround = 0; + + public ShortCircuitTransformerLeg(LegConnectionType legConnectionType) { + this(legConnectionType, 0, 0, false); + } + + public ShortCircuitTransformerLeg(LegConnectionType legConnectionType, double coeffRo, double coeffXo, boolean freeFluxes) { + this.legConnectionType = legConnectionType; + this.coeffRo = coeffRo; + this.coeffXo = coeffXo; + this.freeFluxes = freeFluxes; + } + + public LegConnectionType getLegConnectionType() { + return legConnectionType; + } + + public void setLegConnectionType(LegConnectionType legConnectionType) { + this.legConnectionType = Objects.requireNonNull(legConnectionType); + } + + public double getCoeffRo() { + return coeffRo; + } + + public double getCoeffXo() { + return coeffXo; + } + + public boolean isFreeFluxes() { + return freeFluxes; + } + + public double getrGround() { + return rGround; + } + + public double getxGround() { + return xGround; + } +} diff --git a/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/Networks.java b/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/Networks.java new file mode 100644 index 00000000..e7e8fa17 --- /dev/null +++ b/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/Networks.java @@ -0,0 +1,513 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import org.joda.time.DateTime; + +/** + * @author Jean-Baptiste Heyberger + */ +public final class Networks { + + private Networks() { + } + + public static Network create4n() { + Network network = Network.create("4n", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substation1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1.newVoltageLevel() + .setId("VL_1") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(100.0).setAngle(0.); + + Substation substation2 = network.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = substation2.newVoltageLevel() + .setId("VL_2") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(100.0).setAngle(0); + Generator gen2 = vl2.newGenerator() + .setId("G2") + .setBus(bus2.getId()) + .setMinP(0.0) + .setMaxP(150) + .setTargetP(10) + .setTargetV(100.0) + .setVoltageRegulatorOn(true) + .add(); + + gen2.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(20) + .withDirectTransX(20) + .withStepUpTransformerX(0.) + .add(); + + Substation substation3 = network.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = substation3.newVoltageLevel() + .setId("VL_3") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(100.0).setAngle(0.); + vl3.newLoad() + .setId("LOAD_3") + .setBus(bus3.getId()) + .setP0(10.0) + .setQ0(100.) + .add(); + + Substation substation4 = network.newSubstation() + .setId("S4") + .setCountry(Country.FR) + .add(); + VoltageLevel vl4 = substation4.newVoltageLevel() + .setId("VL_4") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus4 = vl4.getBusBreakerView().newBus() + .setId("B4") + .add(); + bus4.setV(100.0).setAngle(0.); + + network.newLine() + .setId("B1_B2") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl2.getId()) + .setBus2(bus2.getId()) + .setConnectableBus2(bus2.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B3") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B4") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B2_B3") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.6) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B3_B4") + .setVoltageLevel1(vl3.getId()) + .setBus1(bus3.getId()) + .setConnectableBus1(bus3.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + return network; + } + + public static Network create4nShunt() { + Network network = Network.create("4n_shunt", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substation1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1.newVoltageLevel() + .setId("VL_1") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(100.0).setAngle(0.); + + Substation substation2 = network.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = substation2.newVoltageLevel() + .setId("VL_2") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(100.0).setAngle(0); + vl2.newGenerator() + .setId("G2") + .setBus(bus2.getId()) + .setMinP(0.0) + .setMaxP(150) + .setTargetP(10) + .setTargetV(100.0) + .setVoltageRegulatorOn(true) + .add(); + + Substation substation3 = network.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = substation3.newVoltageLevel() + .setId("VL_3") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(100.0).setAngle(0.); + vl3.newLoad() + .setId("LOAD_3") + .setBus(bus3.getId()) + .setP0(10.0) + .setQ0(100.) + .add(); + + Substation substation4 = network.newSubstation() + .setId("S4") + .setCountry(Country.FR) + .add(); + VoltageLevel vl4 = substation4.newVoltageLevel() + .setId("VL_4") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus4 = vl4.getBusBreakerView().newBus() + .setId("B4") + .add(); + bus4.setV(100.0).setAngle(0.); + //add a shunt compared to the previous 4n network + vl4.newShuntCompensator() + .setId("SHUNT_4") + .setBus(bus4.getId()) + .setSectionCount(1) + .setVoltageRegulatorOn(false) + .newLinearModel().setMaximumSectionCount(1).setBPerSection(-0.00105).add() + .add(); + + network.newLine() + .setId("B1_B2") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl2.getId()) + .setBus2(bus2.getId()) + .setConnectableBus2(bus2.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B3") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B4") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B2_B3") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.6) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B3_B4") + .setVoltageLevel1(vl3.getId()) + .setBus1(bus3.getId()) + .setConnectableBus1(bus3.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + return network; + } + + public static Network create4nShuntEq() { + Network network = Network.create("4n_Shunt_Eq", "test"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + Substation substation1 = network.newSubstation() + .setId("S1") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1.newVoltageLevel() + .setId("VL_1") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(100.0).setAngle(0.); + + Substation substation2 = network.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = substation2.newVoltageLevel() + .setId("VL_2") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(100.0).setAngle(0); + vl2.newGenerator() + .setId("G2") + .setBus(bus2.getId()) + .setMinP(0.0) + .setMaxP(150) + .setTargetP(10) + .setTargetV(100.0) + .setVoltageRegulatorOn(true) + .add(); + + Substation substation3 = network.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = substation3.newVoltageLevel() + .setId("VL_3") + .setNominalV(100.0) + .setLowVoltageLimit(0) + .setHighVoltageLimit(200) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(100.0).setAngle(0.); + vl3.newLoad() + .setId("LOAD_3") + .setBus(bus3.getId()) + .setP0(10.0) + .setQ0(100.) + .add(); + + //no node 4 in 4n equivalent shunt resulting from the suppression of node 4 + + network.newLine() + .setId("B1_B2") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl2.getId()) + .setBus2(bus2.getId()) + .setConnectableBus2(bus2.getId()) + .setR(0.0) + .setX(1 / 0.5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B1_B3") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + network.newLine() + .setId("B2_B3") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(1 / 0.6) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + //no line connected to node 4 + + ///////////////////// + //add equivalent part + ///////////////////// + // Reduction hypothesis : add line x = 0.0450525 (lf pu) between B1 and B3 + network.newLine() + .setId("B1_B3_eq") + .setVoltageLevel1(vl1.getId()) + .setBus1(bus1.getId()) + .setConnectableBus1(bus1.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(0.0) + .setX(4.50525) //pu in the lf gives a / 100 factor + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + //reduction hypothesis got from 4n shunt = Equivalent shunt g=0.0 b=-0.04661228566671838 at bus num=1 + //reduction hypothesis got from 4n shunt = Equivalent shunt g=0.0 b=-0.05826535708339975 at bus num=3 + vl3.newShuntCompensator() + .setId("SHUNT_3_EQ") + .setBus(bus3.getId()) + .setSectionCount(1) + .setVoltageRegulatorOn(false) + .newLinearModel().setMaximumSectionCount(1).setBPerSection(-0.00058265).add() //lf pu = / 100 + .add(); + vl1.newShuntCompensator() + .setId("SHUNT_1_EQ") + .setBus(bus1.getId()) + .setSectionCount(1) + .setVoltageRegulatorOn(false) + .newLinearModel().setMaximumSectionCount(1).setBPerSection(-0.00046612).add() //lf pu = / 100 + .add(); + + return network; + } + +} diff --git a/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/ReferenceNetwork.java b/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/ReferenceNetwork.java new file mode 100644 index 00000000..03080052 --- /dev/null +++ b/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/ReferenceNetwork.java @@ -0,0 +1,1181 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.extensions.GeneratorShortCircuitAdder; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import org.apache.commons.math3.util.Pair; +import org.joda.time.DateTime; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Jean-Baptiste Heyberger + */ +public final class ReferenceNetwork { + + private ReferenceNetwork() { + } + + public static Pair createShortCircuitReference() { + Network network = Network.create("ShortCircuitReference", "reference"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + + // This is an 8 bus grid used as a reference in many paper to get the equivalent Thevenin impedance for a balanced short circuit + // Sbase = 15 MVA + double sbase = 15.; + // + // Bus1 Vnom = 115 kV + double bus1Vnom = 115.; + // + // Utility at bus 1: Sutility=1500 MVA, X/R = 15, Xpu = Sbase/Sutility which gives Zpu = 0.0007+j0.01 + // Z=VnomBus1²/Sbase * Zpu = 0.617167 + j8.81667 ohms which gives yeq= 0.007901-j0.112868 and then Peq = 104.488 MW and Qeq = 1492.69 MVAR + double xdUtility = 0.01 * bus1Vnom * bus1Vnom / sbase; //8.81667; + double rdUtility = 0.0007 * bus1Vnom * bus1Vnom / sbase; //0.617167; + // + // Bus2 Vnom = 13.8 kV + double bus2Vnom = 13.8; + // + // Transformer T1 (bus1 - bus2) : ST1 = 15 MVA and X/R = 20 and XT1% = 7% + // |Zpu| = XT1%/100 * (Sbase/ST1) which gives : Zpu = 0.0035 + j0.0699 which gives Z = Zpu * Zbase2 = 0.044436+j0.88745 ohms + double xT1 = 0.0699 * bus2Vnom * bus2Vnom / sbase; //0.88745; + double rT1 = 0.0035 * bus2Vnom * bus2Vnom / sbase; //0.044436; + // + // Motor M1 cable (bus2 - bus3) : Zpu = 0.0008 + j0.0003 which and Z = 0.00977+j0.003809 ohms + double xl23 = 0.0003 * bus2Vnom * bus2Vnom / sbase; //0.003809; + double rl23 = 0.0008 * bus2Vnom * bus2Vnom / sbase; //0.00977; + // + // Motor M1 at node bus3: synchronous machine, SM1=4000 HP, Xd'%=15% and X/R=28.9 + // Xd'pu = Xd'%/100*Sbase/(SM1*0.8) then Zpu = 0.0251+j0.703 then Z = 0.31867+j8.92529 + double xM1 = 0.703 * bus2Vnom * bus2Vnom / sbase; //8.92529; + double rM1 = 0.0251 * bus2Vnom * bus2Vnom / sbase; //0.31867; + // + // Cable bus2 - bus 4: Z = 0.0182+j0.01077 then Zpu = 0.001451+j0.000848 + double xl24 = 0.000848 * bus2Vnom * bus2Vnom / sbase; //0.01077; + double rl24 = 0.001451 * bus2Vnom * bus2Vnom / sbase; //0.0182; + // + // bus5 Vnom=2.4 kV + double bus5Vnom = 2.4; + // + // Transformer T2 (bus4 - bus5): ST2 = 3.75 MVA , X/R = 11 and XT2% = 5.5% + // |Zpu| = XT2%/100 * (Sbase/ST2) which gives : Zpu = 0.0199+j0.2191 then Z = Zpu * Zbase5 = 0.007642+j0.08134 + double xT2 = 0.2191 * bus5Vnom * bus5Vnom / sbase; //0.08134; + double rT2 = 0.0199 * bus5Vnom * bus5Vnom / sbase; //0.007642; + // + // Motor M2 at node bus5: induction machine, SM1=500 HP, Xd'%=16.7% and X/R=19.3 + // Xd'pu = Xd'%/100*Sbase/(SM2*0.95) then Zpu = 0.3331+j6.3284 then Z = 0.12791+j2.43011 + double xM2 = 6.3284 * bus5Vnom * bus5Vnom / sbase; //2.43011; + double rM2 = 0.3331 * bus5Vnom * bus5Vnom / sbase; //0.12791; + // + // Motor M3 at node bus5: induction machine, SM1=2000 HP, Xd'%=16.7% and X/R=30 + // Xd'pu = Xd'%/100*Sbase/(SM3*0.9) then Zpu = 0.0449+j1.3917 then Z = 0.017242+j0.534413 + double xM3 = 1.3917 * bus5Vnom * bus5Vnom / sbase; //0.534413; + double rM3 = 0.0449 * bus5Vnom * bus5Vnom / sbase; //0.017242; + // + // Cable bus2 - bus6: Zpu = 0.0049+j0.0007 and Z = 0.06228+j0.00944 + double xl26 = 0.0007 * bus2Vnom * bus2Vnom / sbase; //0.00944; + double rl26 = 0.0049 * bus2Vnom * bus2Vnom / sbase; //0.06228; + // + // bus7 Vnom=0.277 kV + double bus7Vnom = 0.277; + // + // Transformer T3 (bus6 - bus7): ST3 = 1.5 MVA, X/R = 6.5 and XT3% = 5.75% + // |Zpu| = XT3%/100 * (Sbase/ST3) which gives : Zpu = 0.0874+j0.5683 then Z = Zpu * Zbase7 = 0.000447+j0.002907 + double xT3 = 0.5683 * bus7Vnom * bus7Vnom / sbase; //0.002907; + double rT3 = 0.0874 * bus7Vnom * bus7Vnom / sbase; //0.000447; + // + // Motor M4 at bus7: Zpu = 0.446924+j*2.3148 and Z = 0.002286+j0.011841 + double xM4SubTrans = 2.3148 * bus7Vnom * bus7Vnom / sbase; //0.011841; + double rM4SubTrans = 0.446924 * bus7Vnom * bus7Vnom / sbase; //0.002286; + double xM4Trans = 9.3985 * bus7Vnom * bus7Vnom / sbase; + double rM4Trans = 1.3614 * bus7Vnom * bus7Vnom / sbase; + + // + // Cable bus7 - bus8: Zpu = 0.8691+j0.6966 and Z = 0.01335+j0.0107 + double xl78 = 0.6966 * bus7Vnom * bus7Vnom / sbase; //0.0107; + double rl78 = 0.8691 * bus7Vnom * bus7Vnom / sbase; //0.01335; + + Substation substation12 = network.newSubstation() + .setId("S12") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation12.newVoltageLevel() + .setId("VL_1") + .setNominalV(bus1Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus1Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(bus1Vnom).setAngle(0.); + + VoltageLevel vl2 = substation12.newVoltageLevel() + .setId("VL_2") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(bus2Vnom).setAngle(0); + + Substation substation3 = network.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = substation3.newVoltageLevel() + .setId("VL_3") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(bus2Vnom).setAngle(0); + + Substation substation45 = network.newSubstation() + .setId("S45") + .setCountry(Country.FR) + .add(); + VoltageLevel vl4 = substation45.newVoltageLevel() + .setId("VL_4") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus4 = vl4.getBusBreakerView().newBus() + .setId("B4") + .add(); + bus4.setV(bus2Vnom).setAngle(0); + + VoltageLevel vl5 = substation45.newVoltageLevel() + .setId("VL_5") + .setNominalV(bus5Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus5Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus5 = vl5.getBusBreakerView().newBus() + .setId("B5") + .add(); + bus5.setV(bus5Vnom).setAngle(0); + + Substation substation67 = network.newSubstation() + .setId("S6") + .setCountry(Country.FR) + .add(); + VoltageLevel vl6 = substation67.newVoltageLevel() + .setId("VL_6") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus6 = vl6.getBusBreakerView().newBus() + .setId("B6") + .add(); + bus6.setV(bus2Vnom).setAngle(0); + + VoltageLevel vl7 = substation67.newVoltageLevel() + .setId("VL_7") + .setNominalV(bus7Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus7Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus7 = vl7.getBusBreakerView().newBus() + .setId("B7") + .add(); + bus7.setV(bus7Vnom).setAngle(0); + + Substation substation8 = network.newSubstation() + .setId("S8") + .setCountry(Country.FR) + .add(); + VoltageLevel vl8 = substation8.newVoltageLevel() + .setId("VL_8") + .setNominalV(bus7Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus7Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus8 = vl8.getBusBreakerView().newBus() + .setId("B8") + .add(); + bus8.setV(bus7Vnom).setAngle(0); + + Generator utility = vl1.newGenerator() + .setId("Utility") + .setBus(bus1.getId()) + .setMinP(0.0) + .setMaxP(100.) + .setTargetP(0.) + .setTargetV(bus1Vnom) + .setVoltageRegulatorOn(true) + .add(); + + utility.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xdUtility) + .withDirectTransX(xdUtility) //put in per unit Sbase = 100. to be compatible with the per unit of the rest of the grid + .withStepUpTransformerX(0.) + .add(); + + Generator m1 = vl3.newGenerator() + .setId("M1") + .setBus(bus3.getId()) + .setMinP(-100.0) + .setMaxP(0.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(bus2Vnom) + .setVoltageRegulatorOn(false) + .add(); + + m1.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xM1) + .withDirectTransX(xM1 * 1.5) + .withStepUpTransformerX(0.) + .add(); + + Generator m2 = vl5.newGenerator() + .setId("M2") + .setBus(bus5.getId()) + .setMinP(-100.0) + .setMaxP(0.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(bus5Vnom) + .setVoltageRegulatorOn(false) + .add(); + + m2.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xM2) + .withDirectTransX(xM2 / 0.4) + .withStepUpTransformerX(0.) + .add(); + + Generator m3 = vl5.newGenerator() + .setId("M3") + .setBus(bus5.getId()) + .setMinP(-100.0) + .setMaxP(0.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(bus5Vnom) + .setVoltageRegulatorOn(false) + .add(); + + m3.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xM3) + .withDirectTransX(xM3 * 1.5) + .withStepUpTransformerX(0.) + .add(); + + Generator m4 = vl7.newGenerator() + .setId("M4") + .setBus(bus7.getId()) + .setMinP(-100.0) + .setMaxP(0.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(bus7Vnom) + .setVoltageRegulatorOn(false) + .add(); + + m4.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xM4SubTrans) + .withDirectTransX(xM4Trans) + .withStepUpTransformerX(0.) + .add(); + + network.newLine() + .setId("B2_B3") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(rl23) + .setX(xl23) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("B2_B4") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(rl24) + .setX(xl24) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("B2_B6") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl6.getId()) + .setBus2(bus6.getId()) + .setConnectableBus2(bus6.getId()) + .setR(rl26) + .setX(xl26) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("B7_B8") + .setVoltageLevel1(vl7.getId()) + .setBus1(bus7.getId()) + .setConnectableBus1(bus7.getId()) + .setVoltageLevel2(vl8.getId()) + .setBus2(bus8.getId()) + .setConnectableBus2(bus8.getId()) + .setR(rl78) + .setX(xl78) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation12.newTwoWindingsTransformer().setId("T1")).setVoltageLevel1(vl1.getId())).setBus1(bus1.getId())).setConnectableBus1(bus1.getId())).setRatedU1(bus1Vnom).setVoltageLevel2(vl2.getId())).setBus2(bus2.getId())).setConnectableBus2(bus2.getId())).setRatedU2(bus2Vnom).setR(rT1).setX(xT1).setG(0.0D).setB(0.0D).add(); + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation45.newTwoWindingsTransformer().setId("T2")).setVoltageLevel1(vl4.getId())).setBus1(bus4.getId())).setConnectableBus1(bus4.getId())).setRatedU1(bus2Vnom).setVoltageLevel2(vl5.getId())).setBus2(bus5.getId())).setConnectableBus2(bus5.getId())).setRatedU2(bus5Vnom).setR(rT2).setX(xT2).setG(0.0D).setB(0.0D).add(); + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation67.newTwoWindingsTransformer().setId("T3")).setVoltageLevel1(vl6.getId())).setBus1(bus6.getId())).setConnectableBus1(bus6.getId())).setRatedU1(bus2Vnom).setVoltageLevel2(vl7.getId())).setBus2(bus7.getId())).setConnectableBus2(bus7.getId())).setRatedU2(bus7Vnom).setR(rT3).setX(xT3).setG(0.0D).setB(0.0D).add(); + + //map to store the real part of the transient impedance + Map machineToTransRd = new HashMap<>(); + machineToTransRd.put(utility.getId(), rdUtility); + machineToTransRd.put(m1.getId(), rM1 * 1.5); + machineToTransRd.put(m2.getId(), rM2 / 0.4); + machineToTransRd.put(m3.getId(), rM3 * 1.5); + machineToTransRd.put(m4.getId(), rM4Trans); + + Map machineToSubTransRd = new HashMap<>(); + machineToSubTransRd.put(utility.getId(), rdUtility); + machineToSubTransRd.put(m1.getId(), rM1); + machineToSubTransRd.put(m2.getId(), rM2); + machineToSubTransRd.put(m3.getId(), rM3); + machineToSubTransRd.put(m4.getId(), rM4SubTrans); + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(machineToTransRd, machineToSubTransRd); + + Pair result = new Pair<>(network, additionalDataInfo); + return result; + } + + public static Pair createShortCircuitIec31() { + Network network = Network.create("ShortCircuit_IEC_3.1", "IEC_3.1"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + + double bus1Vnom = 20.; // 20 kV + double bus2Vnom = 0.4; // 400 V + + double ratedV2 = 0.41; // 410 V + + double kT1 = 0.975; + double xT1 = 0.010312 * kT1; + double rT1 = 0.002753 * kT1; + double coeffRoT1 = 1.; + double coeffXoT1 = 0.95; + + double kT2 = 0.975; + double xT2 = 0.016100 * kT2; + double rT2 = 0.004833 * kT2; + double coeffRoT2 = 1.; + double coeffXoT2 = 0.95; + + double xL2 = 0.000136; + double rL2 = 0.000416; + double coeffRoL2 = 4.23; + double coeffXoL2 = 1.21; + + double xL1 = 0.000395; + double rL1 = 0.000385; + double coeffRoL1 = 3.7; + double coeffXoL1 = 1.81; + + double xL3 = 0.001740; + double rL3 = 0.005420; + double coeffRoL3 = 3.; + double coeffXoL3 = 4.46; + + double xL4 = 0.01485; + double rL4 = 0.01850; + double coeffRoL4 = 2.; + double coeffXoL4 = 3.; + + // we model the feeder through a load: TODO : check if relevant for all IEC examples + double pEquivalentFeeder = 31.286; //Impedance to be applied at feeder : Zfeeder = 0.126115 +j1.26353 ohms + double qEquivalentFeeder = 313.451; //using formula P(MW) = Re(Z) * |V|² / |Z|² and Q(MVA) = Im(Z) * |V|² / |Z|² + + Substation substation123 = network.newSubstation() + .setId("S123") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation123.newVoltageLevel() + .setId("VL_1") + .setNominalV(bus1Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus1Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(bus1Vnom).setAngle(0.); + + VoltageLevel vl2 = substation123.newVoltageLevel() + .setId("VL_2") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(bus2Vnom).setAngle(0.); + + VoltageLevel vl3 = substation123.newVoltageLevel() + .setId("VL_3") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(bus2Vnom).setAngle(0.); + + Substation substation4 = network.newSubstation() + .setId("S4") + .setCountry(Country.FR) + .add(); + VoltageLevel vl4 = substation4.newVoltageLevel() + .setId("VL_4") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus4 = vl4.getBusBreakerView().newBus() + .setId("B4") + .add(); + bus4.setV(bus2Vnom).setAngle(0.); + + Substation substation5 = network.newSubstation() + .setId("S5") + .setCountry(Country.FR) + .add(); + VoltageLevel vl5 = substation5.newVoltageLevel() + .setId("VL_5") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus5 = vl5.getBusBreakerView().newBus() + .setId("B5") + .add(); + bus5.setV(bus2Vnom).setAngle(0.); + + Substation substation6 = network.newSubstation() + .setId("S6") + .setCountry(Country.FR) + .add(); + VoltageLevel vl6 = substation6.newVoltageLevel() + .setId("VL_6") + .setNominalV(bus2Vnom) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * bus2Vnom) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus6 = vl6.getBusBreakerView().newBus() + .setId("B6") + .add(); + bus6.setV(bus2Vnom).setAngle(0.); + + vl1.newLoad() + .setId("LOAD_FEEDER") // we try to model the feeder through a load that will be transformed into an impedance + .setBus(bus1.getId()) + .setP0(pEquivalentFeeder) + .setQ0(qEquivalentFeeder) + .add(); + + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation123.newTwoWindingsTransformer().setId("T1")).setVoltageLevel1(vl1.getId())).setBus1(bus1.getId())).setConnectableBus1(bus1.getId())).setRatedU1(bus1Vnom).setVoltageLevel2(vl3.getId())).setBus2(bus3.getId())).setConnectableBus2(bus3.getId())).setRatedU2(ratedV2).setR(rT1).setX(xT1).setG(0.0D).setB(0.0D).setRatedS(630.).add(); + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation123.newTwoWindingsTransformer().setId("T2")).setVoltageLevel1(vl1.getId())).setBus1(bus1.getId())).setConnectableBus1(bus1.getId())).setRatedU1(bus1Vnom).setVoltageLevel2(vl2.getId())).setBus2(bus2.getId())).setConnectableBus2(bus2.getId())).setRatedU2(ratedV2).setR(rT2).setX(xT2).setG(0.0D).setB(0.0D).setRatedS(400.).add(); + + network.newLine() + .setId("L2_B2_B4") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(rL2) + .setX(xL2) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L1_B3_B4") + .setVoltageLevel1(vl3.getId()) + .setBus1(bus3.getId()) + .setConnectableBus1(bus3.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(rL1) + .setX(xL1) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L3_B4_B5") + .setVoltageLevel1(vl4.getId()) + .setBus1(bus4.getId()) + .setConnectableBus1(bus4.getId()) + .setVoltageLevel2(vl5.getId()) + .setBus2(bus5.getId()) + .setConnectableBus2(bus5.getId()) + .setR(rL3) + .setX(xL3) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L4_B5_B6") + .setVoltageLevel1(vl5.getId()) + .setBus1(bus5.getId()) + .setConnectableBus1(bus5.getId()) + .setVoltageLevel2(vl6.getId()) + .setBus2(bus6.getId()) + .setConnectableBus2(bus6.getId()) + .setR(rL4) + .setX(xL4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + //additional data + Map machineToTransRd = new HashMap<>(); //map to store the real part of the transient impedance + Map machineToSubTransRd = new HashMap<>(); + Map machineToGround = new HashMap<>(); + + Map leg1type = new HashMap<>(); + Map leg2type = new HashMap<>(); + leg1type.put("T1", AdditionalDataInfo.LegType.DELTA); + leg1type.put("T2", AdditionalDataInfo.LegType.DELTA); + leg2type.put("T1", AdditionalDataInfo.LegType.Y_GROUNDED); + leg2type.put("T2", AdditionalDataInfo.LegType.Y_GROUNDED); + + Map lineIdToCoeffRo = new HashMap<>(); + Map lineIdToCoeffXo = new HashMap<>(); + Map tfo2wIdToCoeffRo = new HashMap<>(); + Map tfo2wIdToCoeffXo = new HashMap<>(); + lineIdToCoeffRo.put("L2_B2_B4", coeffRoL2); + lineIdToCoeffRo.put("L1_B3_B4", coeffRoL1); + lineIdToCoeffRo.put("L3_B4_B5", coeffRoL3); + lineIdToCoeffRo.put("L4_B5_B6", coeffRoL4); + + lineIdToCoeffXo.put("L2_B2_B4", coeffXoL2); + lineIdToCoeffXo.put("L1_B3_B4", coeffXoL1); + lineIdToCoeffXo.put("L3_B4_B5", coeffXoL3); + lineIdToCoeffXo.put("L4_B5_B6", coeffXoL4); + + tfo2wIdToCoeffRo.put("T1", coeffRoT1); + tfo2wIdToCoeffRo.put("T2", coeffRoT2); + tfo2wIdToCoeffXo.put("T1", coeffXoT1); + tfo2wIdToCoeffXo.put("T2", coeffXoT2); + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(machineToTransRd, machineToSubTransRd, machineToGround, + leg1type, leg2type, lineIdToCoeffRo, lineIdToCoeffXo, tfo2wIdToCoeffRo, tfo2wIdToCoeffXo); + + Pair result = new Pair<>(network, additionalDataInfo); + return result; + } + + public static Pair createShortCircuitIec31testNetwork() { + Network network = Network.create("ShortCircuit_IEC_3.1", "IEC_3.1"); + network.setCaseDate(DateTime.parse("2018-03-05T13:30:30.486+01:00")); + + double vEhv = 380.; // Vnom at bus 1 is 380 kV + double vHv = 110.; // high voltage = 110 kV + double vMv = 30.; // medium voltage is 30 kV + double vLv = 10.; // low voltage is 10 kV + + double xG1 = 26.336676; + double rG1 = 0.498795; + double coeffRoG1 = 0.439059 / rG1; // grounded = true + double xo1ground = 66.; + double coeffXoG1 = (13.340874 + xo1ground) / xG1; + + double xG2 = 35.340713; + double rG2 = 1.203944; + + double rG3 = 0.01779; + double xG3 = 1.089623; + + double xM1 = 3.414968; + double rM1 = 0.341497; + + double xM2 = 4.121368; + double rM2 = 0.412137; + + double xL1 = 7.8; + double rL1 = 2.4; + double coeffRoL1 = 6.4 / rL1; + double coeffXoL1 = 25.2 / xL1; + + double xL2 = 3.9; + double rL2 = 1.2; + double coeffRoL2 = 3.2 / rL2; + double coeffXoL2 = 12.6 / xL2; + + double xL3 = 0.975; // TODO : check if this 2 times or only once + double rL3 = 0.3; + double coeffRoL3 = 1.3 / rL3; + double coeffXoL3 = 4.65 / xL3; + + double xL4 = 3.88; + double rL4 = 0.96; + double coeffRoL4 = 2.2 / rL4; + double coeffXoL4 = 11. / xL4; + + double xL5 = 5.79; + double rL5 = 1.8; + double coeffRoL5 = 3.3 / rL5; + double coeffXoL5 = 16.5 / xL5; + + double xL6 = 0.086; + double rL6 = 0.082; + double coeffRoL6 = 1.0; // not used + double coeffXoL6 = 1.0; // not used + + double rho52 = 115 * 115 / (10.5 * 10.5); // Unlike presented in the doc, rT5 and xT5 are expressed on the HV side, we need to divide by rho5² + double rT5 = 2.046454 / rho52; + double xT5 = 49.072241 / rho52; + + double rT6 = rT5; + double xT6 = xT5; + + double rhoB2 = 1. / (120. * 120.); + double rT3a = 0.045714 * rhoB2; + double xT3a = 8.0969989 * rhoB2; + double rT3b = 0.053563 * rhoB2; + double xT3b = -0.079062 * rhoB2; + double rT3c = 0.408560 * rhoB2; + double xT3c = 20.292035 * rhoB2; + + double coeffRoT4 = 0.107281 / (rT3b + rT3c) * rhoB2; + double coeffXoT4 = 18.195035 / (xT3b + xT3c) * rhoB2; + + Substation substation1289 = network.newSubstation() + .setId("S1289") + .setCountry(Country.FR) + .add(); + VoltageLevel vl1 = substation1289.newVoltageLevel() + .setId("VL_1") + .setNominalV(vEhv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vEhv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus1 = vl1.getBusBreakerView().newBus() + .setId("B1") + .add(); + bus1.setV(vEhv).setAngle(0.); + + VoltageLevel vl2 = substation1289.newVoltageLevel() + .setId("VL_2") + .setNominalV(vHv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vHv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus2 = vl2.getBusBreakerView().newBus() + .setId("B2") + .add(); + bus2.setV(vHv).setAngle(0.); + + VoltageLevel vl8 = substation1289.newVoltageLevel() + .setId("VL_8") + .setNominalV(vMv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vMv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus8 = vl8.getBusBreakerView().newBus() + .setId("B8") + .add(); + bus8.setV(vMv).setAngle(0.); + + VoltageLevel vl9 = substation1289.newVoltageLevel() + .setId("VL_9") + .setNominalV(vMv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vMv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus9 = vl9.getBusBreakerView().newBus() + .setId("B9") + .add(); + bus9.setV(vMv).setAngle(0.); + + Substation substation3 = network.newSubstation() + .setId("S3") + .setCountry(Country.FR) + .add(); + VoltageLevel vl3 = substation3.newVoltageLevel() + .setId("VL_3") + .setNominalV(vHv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vHv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus3 = vl3.getBusBreakerView().newBus() + .setId("B3") + .add(); + bus3.setV(vHv).setAngle(0.); + + Substation substation4 = network.newSubstation() + .setId("S4") + .setCountry(Country.FR) + .add(); + VoltageLevel vl4 = substation4.newVoltageLevel() + .setId("VL_4") + .setNominalV(vHv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vHv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus4 = vl4.getBusBreakerView().newBus() + .setId("B4") + .add(); + bus4.setV(vHv).setAngle(0.); + + Substation substation56 = network.newSubstation() + .setId("S56") + .setCountry(Country.FR) + .add(); + VoltageLevel vl5 = substation56.newVoltageLevel() + .setId("VL_5") + .setNominalV(vHv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vHv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus5 = vl5.getBusBreakerView().newBus() + .setId("B5") + .add(); + bus5.setV(vHv).setAngle(0.); + + VoltageLevel vl6 = substation56.newVoltageLevel() + .setId("VL_6") + .setNominalV(vLv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vLv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus6 = vl6.getBusBreakerView().newBus() + .setId("B6") + .add(); + bus6.setV(vLv).setAngle(0.); + + Substation substation7 = network.newSubstation() + .setId("S7") + .setCountry(Country.FR) + .add(); + VoltageLevel vl7 = substation7.newVoltageLevel() + .setId("VL_7") + .setNominalV(vLv) + .setLowVoltageLimit(0) + .setHighVoltageLimit(2 * vLv) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + Bus bus7 = vl7.getBusBreakerView().newBus() + .setId("B7") + .add(); + bus7.setV(vLv).setAngle(0.); + + // Feeders : + // Same as previous IEC example : we model the feeder through a load: TODO : check if relevant for all IEC examples + double pEquivalentFeeder1 = 2262.42; //Impedance to be applied at feeder : Zfeeder = 0.631933 +j6.319335 ohms + double qEquivalentFeeder1 = 22624.3; //using formula P(MW) = Re(Z) * |V|² / |Z|² and Q(MVA) = Im(Z) * |V|² / |Z|² + + double pEquivalentFeeder2 = 275.753; + double qEquivalentFeeder2 = 2757.53; + double coeffF2Ro = 6.6; + double coeffF2Xo = 3.3; + + vl1.newLoad() + .setId("LOAD_FEEDER1") // we try to model the feeder through a load that will be transformed into an impedance + .setBus(bus1.getId()) + .setP0(pEquivalentFeeder1) + .setQ0(qEquivalentFeeder1) + .add(); + + // This feeder is changed into a machine because it is not yet possible to model a grounded feeder with a load + /*vl5.newLoad() + .setId("LOAD_FEEDER2") // we try to model the feeder through a load that will be transformed into an impedance + .setBus(bus5.getId()) + .setP0(pEquivalentFeeder2) + .setQ0(qEquivalentFeeder2) + .add();*/ + + Generator feeder2 = vl5.newGenerator() //alternative modelling + .setId("Q2") + .setBus(bus5.getId()) + .setMinP(0.0) + .setMaxP(100.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(vHv) + .setVoltageRegulatorOn(false) + .add(); + + double xFeeder2 = 4.344543; + double rFeeder2 = 0.434454; + feeder2.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xFeeder2) // TODO : add table to store coeffs for homopolar values + .withDirectTransX(xFeeder2) + .withStepUpTransformerX(0.) + .add(); + + // Generating units : + Generator g1 = vl4.newGenerator() + .setId("G1") + .setBus(bus4.getId()) + .setMinP(0.0) + .setMaxP(100.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(vHv) + .setVoltageRegulatorOn(false) + .add(); + + g1.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xG1) // TODO : add table to store coeffs for homopolar values + .withDirectTransX(xG1) + .withStepUpTransformerX(0.) + .add(); + + Generator g2 = vl3.newGenerator() + .setId("G2") + .setBus(bus3.getId()) + .setMinP(0.0) + .setMaxP(100.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(vHv) + .setVoltageRegulatorOn(false) + .add(); + + g2.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xG2) // TODO : add table to store coeffs for homopolar values + .withDirectTransX(xG2) + .withStepUpTransformerX(0.) + .add(); + + Generator g3 = vl6.newGenerator() + .setId("G3") + .setBus(bus6.getId()) + .setMinP(0.0) + .setMaxP(100.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(vLv) + .setVoltageRegulatorOn(false) + .add(); + + g3.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xG3) // TODO : add table to store coeffs for homopolar values + .withDirectTransX(xG3) + .withStepUpTransformerX(0.) + .add(); + + // Machines : + // They are considered as non-grounded generating units + Generator m1 = vl7.newGenerator() + .setId("M1") + .setBus(bus7.getId()) + .setMinP(-100.0) + .setMaxP(0.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(vLv) + .setVoltageRegulatorOn(false) + .add(); + + m1.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xM1) // TODO : add table to store coeffs for homopolar values + .withDirectTransX(xM1) + .withStepUpTransformerX(0.) + .add(); + + Generator m2 = vl7.newGenerator() + .setId("M2") + .setBus(bus7.getId()) + .setMinP(-100.0) + .setMaxP(0.) + .setTargetP(0.) + .setTargetQ(0.) + .setTargetV(vLv) + .setVoltageRegulatorOn(false) + .add(); + + m2.newExtension(GeneratorShortCircuitAdder.class) + .withDirectSubtransX(xM2) // TODO : add table to store coeffs for homopolar values + .withDirectTransX(xM2) + .withStepUpTransformerX(0.) + .add(); + + network.newLine() + .setId("L1_B2_B3") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(rL1) + .setX(xL1) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L2_B3_B4") + .setVoltageLevel1(vl3.getId()) + .setBus1(bus3.getId()) + .setConnectableBus1(bus3.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(rL2) + .setX(xL2) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L3_B2_B5") + .setVoltageLevel1(vl2.getId()) + .setBus1(bus2.getId()) + .setConnectableBus1(bus2.getId()) + .setVoltageLevel2(vl5.getId()) + .setBus2(bus5.getId()) + .setConnectableBus2(bus5.getId()) + .setR(rL3) + .setX(xL3) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L4_B5_B3") + .setVoltageLevel1(vl5.getId()) + .setBus1(bus5.getId()) + .setConnectableBus1(bus5.getId()) + .setVoltageLevel2(vl3.getId()) + .setBus2(bus3.getId()) + .setConnectableBus2(bus3.getId()) + .setR(rL4) + .setX(xL4) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L5_B5_B4") + .setVoltageLevel1(vl5.getId()) + .setBus1(bus5.getId()) + .setConnectableBus1(bus5.getId()) + .setVoltageLevel2(vl4.getId()) + .setBus2(bus4.getId()) + .setConnectableBus2(bus4.getId()) + .setR(rL5) + .setX(xL5) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + network.newLine() + .setId("L6_B6_B7") + .setVoltageLevel1(vl6.getId()) + .setBus1(bus6.getId()) + .setConnectableBus1(bus6.getId()) + .setVoltageLevel2(vl7.getId()) + .setBus2(bus7.getId()) + .setConnectableBus2(bus7.getId()) + .setR(rL6) + .setX(xL6) + .setG1(0.0) + .setB1(0.0) + .setG2(0.0) + .setB2(0.0) + .add(); + + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation56.newTwoWindingsTransformer().setId("T5")).setVoltageLevel1(vl5.getId())).setBus1(bus5.getId())).setConnectableBus1(bus5.getId())).setRatedU1(115.).setVoltageLevel2(vl6.getId())).setBus2(bus6.getId())).setConnectableBus2(bus6.getId())).setRatedU2(10.5).setR(rT5).setX(xT5).setG(0.0D).setB(0.0D).setRatedS(31.5).add(); + ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) ((TwoWindingsTransformerAdder) substation56.newTwoWindingsTransformer().setId("T6")).setVoltageLevel1(vl5.getId())).setBus1(bus5.getId())).setConnectableBus1(bus5.getId())).setRatedU1(115.).setVoltageLevel2(vl6.getId())).setBus2(bus6.getId())).setConnectableBus2(bus6.getId())).setRatedU2(10.5).setR(rT6).setX(xT6).setG(0.0D).setB(0.0D).setRatedS(31.5).add(); + + ThreeWindingsTransformer twt3 = ((ThreeWindingsTransformerAdder) substation1289.newThreeWindingsTransformer().setId("T3")).setRatedU0(1.0D).newLeg1().setR(rT3a).setX(xT3a).setG(0.).setB(0.).setRatedU(400.0D).setVoltageLevel(vl1.getId()).setBus(bus1.getId()).add().newLeg2().setR(rT3b).setX(xT3b).setG(0.0D).setB(0.0D).setRatedU(120.0D).setVoltageLevel(vl2.getId()).setBus(bus2.getId()).add().newLeg3().setR(rT3c).setX(xT3c).setG(0.0D).setB(0.0D).setRatedU(30.).setVoltageLevel(vl8.getId()).setBus(bus8.getId()).add().add(); + ThreeWindingsTransformer twt4 = ((ThreeWindingsTransformerAdder) substation1289.newThreeWindingsTransformer().setId("T4")).setRatedU0(1.0D).newLeg1().setR(rT3a).setX(xT3a).setG(0.).setB(0.).setRatedU(400.0D).setVoltageLevel(vl1.getId()).setBus(bus1.getId()).add().newLeg2().setR(rT3b).setX(xT3b).setG(0.0D).setB(0.0D).setRatedU(120.0D).setVoltageLevel(vl2.getId()).setBus(bus2.getId()).add().newLeg3().setR(rT3c).setX(xT3c).setG(0.0D).setB(0.0D).setRatedU(30.).setVoltageLevel(vl9.getId()).setBus(bus9.getId()).add().add(); + //twt.getLeg2().newRatioTapChanger().beginStep().setRho(0.9D).setR(0.9801D).setX(0.09801D).setG(0.08264462809917356D).setB(0.008264462809917356D).endStep().beginStep().setRho(1.0D).setR(1.089D).setX(0.1089D).setG(0.09182736455463728D).setB(0.009182736455463728D).endStep().beginStep().setRho(1.1D).setR(1.1979D).setX(0.11979D).setG(0.10101010101010101D).setB(0.0101010101010101D).endStep().setTapPosition(2).setLoadTapChangingCapabilities(true).setRegulating(true).setTargetV(33.0D).setTargetDeadband(0.0D).setRegulationTerminal(load33.getTerminal()).add(); + //twt.getLeg3().newRatioTapChanger().beginStep().setRho(0.9D).setR(0.1089D).setX(0.01089D).setG(0.8264462809917356D).setB(0.08264462809917356D).endStep().beginStep().setRho(1.0D).setR(0.121D).setX(0.0121D).setG(0.8264462809917356D).setB(0.08264462809917356D).endStep().beginStep().setRho(1.1D).setR(0.1331D).setX(0.01331D).setG(0.9090909090909092D).setB(0.09090909090909093D).endStep().setTapPosition(0).setLoadTapChangingCapabilities(true).setRegulating(false).setTargetV(11.0D).setRegulationTerminal(load11.getTerminal()).add(); + + //additional data + // Machines : + Map machineToTransRd = new HashMap<>(); //map to store the real part of the transient impedance + Map machineToSubTransRd = new HashMap<>(); + Map machineToGround = new HashMap<>(); + machineToTransRd.put("G1", rG1); + machineToTransRd.put("G2", rG2); + machineToTransRd.put("G3", rG3); + machineToTransRd.put("M1", rM1); + machineToTransRd.put("M2", rM2); + machineToTransRd.put("Q2", rFeeder2); + + machineToGround.put("G1", true); + machineToGround.put("Q2", true); + + Map machineCoeffRo = new HashMap<>(); //map to store the real part of the transient impedance + Map machineCoeffXo = new HashMap<>(); + machineCoeffRo.put("G1", coeffRoG1); + machineCoeffXo.put("G1", coeffXoG1); + machineCoeffRo.put("Q2", coeffF2Ro); + machineCoeffXo.put("Q2", coeffF2Xo); + + // transformers : + Map leg1type = new HashMap<>(); + Map leg2type = new HashMap<>(); + Map leg3type = new HashMap<>(); // needed only for three windings transformers + + leg1type.put("T5", AdditionalDataInfo.LegType.Y); + leg1type.put("T6", AdditionalDataInfo.LegType.Y); + leg2type.put("T5", AdditionalDataInfo.LegType.Y); + leg2type.put("T6", AdditionalDataInfo.LegType.Y_GROUNDED); + + leg1type.put("T3", AdditionalDataInfo.LegType.Y_GROUNDED); + leg1type.put("T4", AdditionalDataInfo.LegType.Y); + leg2type.put("T3", AdditionalDataInfo.LegType.Y); + leg2type.put("T4", AdditionalDataInfo.LegType.Y_GROUNDED); + + leg3type.put("T3", AdditionalDataInfo.LegType.DELTA); + leg3type.put("T4", AdditionalDataInfo.LegType.DELTA); + + Map tfo2wIdToCoeffRo = new HashMap<>(); + Map tfo2wIdToCoeffXo = new HashMap<>(); + tfo2wIdToCoeffRo.put("T5", 1.); // TODO : remove if not used + tfo2wIdToCoeffRo.put("T6", 1.); + tfo2wIdToCoeffXo.put("T5", 1.); + tfo2wIdToCoeffXo.put("T6", 1.); + + Map tfo3wIdToLeg1CoeffRo = new HashMap<>(); + Map tfo3wIdToLeg1CoeffXo = new HashMap<>(); + + Map tfo3wIdToLeg2CoeffRo = new HashMap<>(); + Map tfo3wIdToLeg2CoeffXo = new HashMap<>(); + tfo3wIdToLeg2CoeffRo.put("T4", coeffRoT4); + tfo3wIdToLeg2CoeffXo.put("T4", coeffXoT4); + + Map tfo3wIdToLeg3CoeffRo = new HashMap<>(); + Map tfo3wIdToLeg3CoeffXo = new HashMap<>(); + tfo3wIdToLeg3CoeffRo.put("T4", coeffRoT4); + tfo3wIdToLeg3CoeffXo.put("T4", coeffXoT4); + + Map tfo2wIdToFreeFluxes = new HashMap<>(); // free fluxes mean that magnetizing impedance Zm is infinite, by default, fluxes are forced and Zm exists + Map tfo3wIdLeg1ToFreeFluxes = new HashMap<>(); + Map tfo3wIdLeg2ToFreeFluxes = new HashMap<>(); + Map tfo3wIdLeg3ToFreeFluxes = new HashMap<>(); + tfo3wIdLeg1ToFreeFluxes.put("T4", true); + tfo3wIdLeg2ToFreeFluxes.put("T4", true); + tfo3wIdLeg3ToFreeFluxes.put("T4", true); + + // Lines : + Map lineIdToCoeffRo = new HashMap<>(); + Map lineIdToCoeffXo = new HashMap<>(); + + lineIdToCoeffRo.put("L1_B2_B3", coeffRoL1); + lineIdToCoeffRo.put("L2_B3_B4", coeffRoL2); + lineIdToCoeffRo.put("L3_B2_B5", coeffRoL3); + lineIdToCoeffRo.put("L4_B5_B3", coeffRoL4); + lineIdToCoeffRo.put("L5_B5_B4", coeffRoL5); + lineIdToCoeffRo.put("L6_B6_B7", coeffRoL6); + + lineIdToCoeffXo.put("L1_B2_B3", coeffXoL1); + lineIdToCoeffXo.put("L2_B3_B4", coeffXoL2); + lineIdToCoeffXo.put("L3_B2_B5", coeffXoL3); + lineIdToCoeffXo.put("L4_B5_B3", coeffXoL4); + lineIdToCoeffXo.put("L5_B5_B4", coeffXoL5); + lineIdToCoeffXo.put("L6_B6_B7", coeffXoL6); + + // Feeders : + Map feederIdToCoeffRo = new HashMap<>(); // we suppose that if no info is provided, the feeder is not grounded + Map feederIdToCoeffXo = new HashMap<>(); + //feederIdToCoeffRo.put("LOAD_FEEDER2", coeffF2Ro); + //feederIdToCoeffXo.put("LOAD_FEEDER2", coeffF2Xo); + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(machineToTransRd, machineToSubTransRd, machineToGround, + leg1type, leg2type, lineIdToCoeffRo, lineIdToCoeffXo, tfo2wIdToCoeffRo, tfo2wIdToCoeffXo, + feederIdToCoeffRo, feederIdToCoeffXo, machineCoeffRo, machineCoeffXo, leg3type, + tfo3wIdToLeg1CoeffRo, tfo3wIdToLeg1CoeffXo, + tfo3wIdToLeg2CoeffRo, tfo3wIdToLeg2CoeffXo, + tfo3wIdToLeg3CoeffRo, tfo3wIdToLeg3CoeffXo, + tfo2wIdToFreeFluxes, tfo3wIdLeg1ToFreeFluxes, tfo3wIdLeg2ToFreeFluxes, tfo3wIdLeg3ToFreeFluxes); + + Pair result = new Pair<>(network, additionalDataInfo); + return result; + + } +} diff --git a/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/TheveninTest.java b/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/TheveninTest.java new file mode 100644 index 00000000..45f3f7d9 --- /dev/null +++ b/simulator/util/src/test/java/com/powsybl/incubator/simulator/util/TheveninTest.java @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2022, Jean-Baptiste Heyberger & Geoffroy Jamgotchian + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.incubator.simulator.util; + +import com.powsybl.commons.reporter.Reporter; +import com.powsybl.iidm.network.Network; +import com.powsybl.incubator.simulator.util.extensions.AdditionalDataInfo; +import com.powsybl.loadflow.LoadFlow; +import com.powsybl.loadflow.LoadFlowParameters; +import com.powsybl.loadflow.LoadFlowResult; +import com.powsybl.math.matrix.DenseMatrixFactory; +import com.powsybl.math.matrix.MatrixFactory; +import com.powsybl.openloadflow.OpenLoadFlowParameters; +import com.powsybl.openloadflow.OpenLoadFlowProvider; +import com.powsybl.openloadflow.ac.outerloop.AcLoadFlowParameters; +import com.powsybl.openloadflow.graph.EvenShiloachGraphDecrementalConnectivityFactory; +import org.apache.commons.math3.util.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.usefultoys.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Jean-Baptiste Heyberger + */ +class TheveninTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(TheveninTest.class); + + private LoadFlowParameters parameters; + + private MatrixFactory matrixFactory; + + private LoadFlow.Runner loadFlowRunner; + + @BeforeEach + void setUp() { + parameters = new LoadFlowParameters() + .setTwtSplitShuntAdmittance(true) + .setVoltageInitMode(LoadFlowParameters.VoltageInitMode.PREVIOUS_VALUES); + matrixFactory = new DenseMatrixFactory(); + loadFlowRunner = new LoadFlow.Runner(new OpenLoadFlowProvider(matrixFactory)); + } + + @Test + void computeZthTest() { + Network network = Networks.create4n(); + LoadFlowResult resultnt4 = loadFlowRunner.run(network, parameters); + + List faultsList = new ArrayList<>(); + CalculationLocation f1 = new CalculationLocation("B4", true); + CalculationLocation f2 = new CalculationLocation("B2", true); + faultsList.add(f1); + faultsList.add(f2); + + AdditionalDataInfo additionalDataInfo = new AdditionalDataInfo(); + + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, + parameters, OpenLoadFlowParameters.get(parameters), matrixFactory, new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + + TheveninEquivalentParameters.TheveninVoltageProfileType avp = TheveninEquivalentParameters.TheveninVoltageProfileType.CALCULATED; + TheveninEquivalentParameters.TheveninPeriodType periodType = TheveninEquivalentParameters.TheveninPeriodType.THEVENIN_TRANSIENT; + TheveninEquivalentParameters thParameters = new TheveninEquivalentParameters(acLoadFlowParameters, matrixFactory, faultsList, true, avp, periodType, false, additionalDataInfo); // check how to give a Network bus in input + TheveninEquivalent thEq = new TheveninEquivalent(network, thParameters); + + thEq.run(); + + assertEquals(0.0027661335620416884, thEq.getImpedanceLinearResolution().results.get(1).getRthz11(), 0.000001); + assertEquals(0.16629396899928067, thEq.getImpedanceLinearResolution().results.get(1).getXthz12(), 0.000001); + assertEquals(0.0030247992008329934, thEq.getImpedanceLinearResolution().results.get(0).getRthz11(), 0.000001); + assertEquals(0.1833452236067607, thEq.getImpedanceLinearResolution().results.get(0).getXthz12(), 0.000001); + //assertEquals(0.16522876711663945, thEq.results.get(0).getDvr1().get(0), 0.000001); + + } + + @Test + void referenceTransientTest() { + Pair result = ReferenceNetwork.createShortCircuitReference(); + + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + List faultsList = new ArrayList<>(); + CalculationLocation f1 = new CalculationLocation("B2", true); + CalculationLocation f2 = new CalculationLocation("B5", true); + CalculationLocation f3 = new CalculationLocation("B7", true); + CalculationLocation f4 = new CalculationLocation("B8", true); + faultsList.add(f1); + faultsList.add(f2); + faultsList.add(f3); + faultsList.add(f4); + + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, + parameters, OpenLoadFlowParameters.get(parameters), matrixFactory, new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + + TheveninEquivalentParameters.TheveninVoltageProfileType avp = TheveninEquivalentParameters.TheveninVoltageProfileType.NOMINAL; + TheveninEquivalentParameters.TheveninPeriodType periodType = TheveninEquivalentParameters.TheveninPeriodType.THEVENIN_TRANSIENT; + TheveninEquivalentParameters thParameters = new TheveninEquivalentParameters(acLoadFlowParameters, matrixFactory, faultsList, true, avp, periodType, false, additionalDataInfo); // check how to give a Network bus in input + TheveninEquivalent thEq = new TheveninEquivalent(network, thParameters); + + thEq.run(); + + // results here are with Sbase = 100 MVA we convert them into Sbase = 15 MVA to be in line with the reference doc result : + assertEquals(0.003683374391319212, thEq.getImpedanceLinearResolution().results.get(0).getRthz11() * 15. / 100., 0.000001); //F1 : doc result = Zth(Sbase15) ~ 0.0036+j0.0712 + assertEquals(0.07118802892811232, thEq.getImpedanceLinearResolution().results.get(0).getXthz12() * 15. / 100., 0.000001); + assertEquals(0.019949496420349225, thEq.getImpedanceLinearResolution().results.get(1).getRthz11() * 15. / 100., 0.000001); //F2 : doc result = Zth(Sbase15) ~ 0.0199+j0.2534 + assertEquals(0.2534161781273357, thEq.getImpedanceLinearResolution().results.get(1).getXthz12() * 15. / 100., 0.000001); + + } + + @Test + void referenceSubTransientTest() { + Pair result = ReferenceNetwork.createShortCircuitReference(); + + Network network = result.getKey(); + + AdditionalDataInfo additionalDataInfo = result.getValue(); + + List faultsList = new ArrayList<>(); + CalculationLocation f1 = new CalculationLocation("B2", true); + CalculationLocation f2 = new CalculationLocation("B5", true); + CalculationLocation f3 = new CalculationLocation("B7", true); + CalculationLocation f4 = new CalculationLocation("B8", true); + faultsList.add(f1); + faultsList.add(f2); + faultsList.add(f3); + faultsList.add(f4); + + AcLoadFlowParameters acLoadFlowParameters = OpenLoadFlowParameters.createAcParameters(network, + parameters, OpenLoadFlowParameters.get(parameters), matrixFactory, new EvenShiloachGraphDecrementalConnectivityFactory<>(), Reporter.NO_OP); + + TheveninEquivalentParameters.TheveninVoltageProfileType avp = TheveninEquivalentParameters.TheveninVoltageProfileType.NOMINAL; + TheveninEquivalentParameters.TheveninPeriodType periodType = TheveninEquivalentParameters.TheveninPeriodType.THEVENIN_SUB_TRANSIENT; + TheveninEquivalentParameters thParameters = new TheveninEquivalentParameters(acLoadFlowParameters, matrixFactory, faultsList, true, avp, periodType, false, additionalDataInfo); // check how to give a Network bus in input + TheveninEquivalent thEq = new TheveninEquivalent(network, thParameters); + + thEq.run(); + + // results here are with Sbase = 100 MVA we convert them into Sbase = 15 MVA to be in line with the reference doc result : + assertEquals(0.0035803351059196286, thEq.getImpedanceLinearResolution().results.get(0).getRthz11() * 15. / 100., 0.000001); //F1 : doc result = Zth(Sbase15) ~ 0.0035+j0.0666 + assertEquals(0.0666102621341282, thEq.getImpedanceLinearResolution().results.get(0).getXthz12() * 15. / 100., 0.000001); + assertEquals(0.01766454025768954, thEq.getImpedanceLinearResolution().results.get(1).getRthz11() * 15. / 100., 0.000001); //F2 : doc result = Zth(Sbase15) ~ 0.0175+j0.2313 + assertEquals(0.2313127317660599, thEq.getImpedanceLinearResolution().results.get(1).getXthz12() * 15. / 100., 0.000001); + assertEquals(0.07967413109647312, thEq.getImpedanceLinearResolution().results.get(2).getRthz11() * 15. / 100., 0.000001); //F2 : doc result = Zth(Sbase15) ~ 0.0796+j0.5 + assertEquals(0.4997813218278794, thEq.getImpedanceLinearResolution().results.get(2).getXthz12() * 15. / 100., 0.000001); + + } + +}