From 3b12b4a66953ead5cd7c3ae3c8448bd81928b707 Mon Sep 17 00:00:00 2001 From: rkravis Date: Wed, 29 Apr 2026 12:00:08 -0700 Subject: [PATCH 1/6] added: gfmi_e analysis scripts feat: gfli with outer loop Vdc and Q control --- .../2-bus-src-gfli_e/inputs/buses.csv | 3 + .../2-bus-src-gfli_e/inputs/gfli_e.csv | 2 + .../inputs/infinite_sources.csv | 2 + .../2-bus-src-gfli_e/inputs/lines.csv | 2 + .../2-bus-src-gfli_e/inputs/loads.csv | 3 + .../2-bus-src-gfli_e/inputs/timepoints.csv | 2 + .../active_power_balance_by_bus.csv | 3 + .../outputs/ac_power_flow/bus_voltage.csv | 3 + .../outputs/ac_power_flow/costs_summary.csv | 2 + .../ac_power_flow/generator_dispatch.csv | 3 + .../outputs/ac_power_flow/line_flows.csv | 2 + .../outputs/ac_power_flow/load_shedding.csv | 3 + .../reactive_power_balance_by_bus.csv | 3 + .../outputs/ac_power_flow/solver_status.csv | 5 + .../component_connection_matrices/F.csv | 18 + .../component_connection_matrices/G.csv | 18 + .../component_connection_matrices/H.csv | 11 + .../component_connection_matrices/L.csv | 11 + .../2-bus-src-gfli_e/run.py | 37 + .../2-bus-src-gfmi_e/emt_analysis.py | 202 +++++ .../2-bus-src-gfmi_e/emt_heatmaps.png | Bin 0 -> 93330 bytes .../2-bus-src-gfmi_e/inputs/gfmi_e.csv | 4 +- .../active_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/bus_voltage.csv | 4 +- .../outputs/ac_power_flow/costs_summary.csv | 2 +- .../ac_power_flow/generator_dispatch.csv | 4 +- .../outputs/ac_power_flow/line_flows.csv | 2 +- .../outputs/ac_power_flow/load_shedding.csv | 4 +- .../reactive_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/solver_status.csv | 2 +- .../component_connection_matrices/F.csv | 18 +- .../component_connection_matrices/G.csv | 16 +- .../component_connection_matrices/H.csv | 14 +- .../component_connection_matrices/L.csv | 12 +- .../outputs/small_signal_model/A.csv | 42 +- .../outputs/small_signal_model/B.csv | 18 +- .../outputs/small_signal_model/C.csv | 22 +- .../outputs/small_signal_model/D.csv | 12 +- .../outputs/small_signal_model/u.csv | 14 +- .../outputs/small_signal_model/x.csv | 52 +- .../outputs/small_signal_model/y.csv | 20 +- .../2-bus-src-gfmi_e/run.py | 124 ++- .../2-bus-src-gfmi_e/run_compare_ssm_emt.py | 453 +++++++++++ .../2-bus-src-gfmi_e/ssm_analysis.py | 129 +++ .../2-bus-src-gfmi_e/ssm_heatmaps.png | Bin 0 -> 55762 bytes .../2-bus_src-gfm/emt_analysis.py | 201 +++++ .../2-bus_src-gfm/emt_heatmaps.png | Bin 0 -> 93334 bytes .../2-bus_src-gfm/inputs/buses.csv | 4 +- .../2-bus_src-gfm/inputs/gfmi_c.csv | 2 +- .../active_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/bus_voltage.csv | 4 +- .../outputs/ac_power_flow/costs_summary.csv | 2 +- .../ac_power_flow/generator_dispatch.csv | 4 +- .../outputs/ac_power_flow/line_flows.csv | 2 +- .../outputs/ac_power_flow/load_shedding.csv | 4 +- .../reactive_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/solver_status.csv | 2 +- .../component_connection_matrices/F.csv | 18 +- .../component_connection_matrices/G.csv | 16 +- .../component_connection_matrices/H.csv | 14 +- .../component_connection_matrices/L.csv | 12 +- .../outputs/small_signal_model/A.csv | 36 +- .../outputs/small_signal_model/B.csv | 16 +- .../outputs/small_signal_model/C.csv | 22 +- .../outputs/small_signal_model/D.csv | 12 +- .../outputs/small_signal_model/u.csv | 10 +- .../outputs/small_signal_model/x.csv | 34 +- .../outputs/small_signal_model/y.csv | 20 +- .../small_signal_and_emt/2-bus_src-gfm/run.py | 4 +- .../2-bus_src-gfm/ssm_analysis.py | 157 ++++ .../2-bus_src-gfm/ssm_heatmaps.png | Bin 0 -> 55762 bytes .../2-bus_src_gfli_a/inputs/buses.csv | 3 + .../2-bus_src_gfli_a/inputs/gfli_a.csv | 2 + .../inputs/infinite_sources.csv | 2 + .../2-bus_src_gfli_a/inputs/lines.csv | 2 + .../2-bus_src_gfli_a/inputs/loads.csv | 3 + .../2-bus_src_gfli_a/inputs/timepoints.csv | 2 + .../2-bus_src_gfli_a/run.py | 34 + .../small_signal_and_emt/gfmi_e_v_gfmi_c.py | 275 +++++++ sting/generator/gfli_e.py | 740 ++++++++++++++++++ sting/generator/gfmi_e.py | 98 ++- 81 files changed, 2756 insertions(+), 320 deletions(-) create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv create mode 100755 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv create mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/run.py create mode 100644 examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_analysis.py create mode 100644 examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_heatmaps.png create mode 100644 examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py create mode 100644 examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_analysis.py create mode 100644 examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_heatmaps.png create mode 100644 examples/small_signal_and_emt/2-bus_src-gfm/emt_analysis.py create mode 100644 examples/small_signal_and_emt/2-bus_src-gfm/emt_heatmaps.png create mode 100644 examples/small_signal_and_emt/2-bus_src-gfm/ssm_analysis.py create mode 100644 examples/small_signal_and_emt/2-bus_src-gfm/ssm_heatmaps.png create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/buses.csv create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/gfli_a.csv create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/infinite_sources.csv create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/lines.csv create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/loads.csv create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/timepoints.csv create mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/run.py create mode 100644 examples/small_signal_and_emt/gfmi_e_v_gfmi_c.py create mode 100644 sting/generator/gfli_e.py diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv new file mode 100644 index 0000000..3817d3d --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv @@ -0,0 +1,3 @@ +name,base_power_MVA,base_voltage_kV,base_frequency_Hz,minimum_voltage_pu,maximum_voltage_pu +lima,1.00E+02,2.30E+02,60,0.95,1.05 +santiago,1.00E+02,2.30E+02,60,0.95,1.05 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv new file mode 100755 index 0000000..491615b --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv @@ -0,0 +1,2 @@ +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,lf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_l1_pu,txr_r2_pu,txr_l2_pu,beta,kp_pll,ki_pll,kp_cc,ki_cc,vdc_ref,i_load_ref,c_dc,kp_oc,ki_oc +solar,santiago,10,10,0,0,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0,1,10,1,10,2,0,20,1,10 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv new file mode 100644 index 0000000..9d95931 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv @@ -0,0 +1,2 @@ +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,r_pu,x_pu +gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.5 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv new file mode 100644 index 0000000..74a86b2 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv @@ -0,0 +1,2 @@ +name,from_bus,to_bus,base_power_MVA,base_voltage_kV,base_frequency_Hz,r_pu,x_pu,g_pu,b_pu +tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.01,0.5,0.05,0.066666667 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv new file mode 100644 index 0000000..79d806e --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv @@ -0,0 +1,3 @@ +bus,timepoint,load_MW,load_MVAR +lima,t_1,0,0 +chile,t_1,0,0 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv new file mode 100644 index 0000000..ad319a5 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv @@ -0,0 +1,2 @@ +name +timepoint_1 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv new file mode 100644 index 0000000..bc24fde --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -0,0 +1,3 @@ +bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW +lima,timepoint_1,-0.058375056098752195,-9.974940964399896e-9,0.0,-0.05837506532163607 +santiago,timepoint_1,10.0,-9.974940964460538e-9,0.0,9.999999989460255 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv new file mode 100644 index 0000000..fa0fc34 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv @@ -0,0 +1,3 @@ +id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg +0,lima,timepoint_1,0.9795934248147871,0.0 +1,santiago,timepoint_1,1.013581804636969,1.3637928689337457 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv new file mode 100644 index 0000000..940fe42 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv @@ -0,0 +1,2 @@ +component,cost +total_cost_USD,-0.00003989976383138582 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv new file mode 100644 index 0000000..80a132f --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv @@ -0,0 +1,3 @@ +id,type,generator,timepoint,active_power_MW,reactive_power_MVAR +0,infinite_sources,gen1,timepoint_1,-0.058375056098752195,-12.902934347861217 +0,gfli_e,solar,timepoint_1,10.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv new file mode 100644 index 0000000..2ffcc27 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv @@ -0,0 +1,2 @@ +line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR +tx_1,lima,santiago,inf,-0.05837506532163335,-12.902934358094463,9.999999989460255,-9.95750355277473e-9,9.94162492413862,-12.902934368051966 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv new file mode 100644 index 0000000..529532a --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv @@ -0,0 +1,3 @@ +bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR +lima,timepoint_1,-9.974940964399896e-9,-9.97494096436451e-9 +santiago,timepoint_1,-9.974940964460538e-9,-9.974940938160877e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv new file mode 100644 index 0000000..961177c --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -0,0 +1,3 @@ +bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR +lima,timepoint_1,-12.902934347861217,-9.97494096436451e-9,0.0,-12.90293435809444 +santiago,timepoint_1,0.0,-9.974940938160877e-9,0.0,-9.95750355277473e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv new file mode 100644 index 0000000..1402c84 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv @@ -0,0 +1,5 @@ +attribute,value +solver_name,ipopt +solver_status,ok +termination_condition,optimal +time_spent_seconds,0.10544800758361816 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv new file mode 100644 index 0000000..85259ac --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv @@ -0,0 +1,18 @@ +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_e_0', 'i_bus_D')","('gfli_e_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'v_ref_d')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'vdc_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'q_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('gfli_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv new file mode 100644 index 0000000..1beb4b5 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv @@ -0,0 +1,18 @@ +Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'vdc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'q_ref')" +"('infinite_sources_0', 'v_ref_d')",1.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_ref_q')",0.0,1.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'vdc_ref')",0.0,0.0,1.0,0.0,0.0 +"('gfli_e_0', 'i_load_ref')",0.0,0.0,0.0,1.0,0.0 +"('gfli_e_0', 'q_ref')",0.0,0.0,0.0,0.0,1.0 +"('gfli_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv new file mode 100644 index 0000000..d2f1694 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv @@ -0,0 +1,11 @@ +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_e_0', 'i_bus_D')","('gfli_e_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv new file mode 100644 index 0000000..e6014c3 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv @@ -0,0 +1,11 @@ +Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'vdc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'q_ref')" +"('infinite_sources_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py new file mode 100644 index 0000000..8dcb018 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py @@ -0,0 +1,37 @@ + +# Import Python standard and third-party packages +from pathlib import Path + +# Import sting package +from sting import main +from sting.system.core import System + +# Step-change input to applied to the system +def step1(t): + return 0.1 if t >= 0.5 else 0.0 + +def step2(t): + return 0.0 + +inputs = { + 'infinite_sources_0': { + 'v_ref_d': step2 + }, + 'gfli_e_0': { + 'i_load_ref': step1 + } + } + +t_max = 2.0 # Simulation length (in seconds) + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model +sys = System.from_csv(case_directory=case_dir) + +# Construct system and small-signal model +_, ssm = main.run_ssm(case_directory=case_dir) +ssm.simulate_ssm(t_max=t_max, inputs=inputs) + +print('\nok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_analysis.py b/examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_analysis.py new file mode 100644 index 0000000..d7b8887 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_analysis.py @@ -0,0 +1,202 @@ +""" + +Analysis of EMT response of GFMI_E v INF bus system to a grid disturbance under different P/Q and different Pref/Pload relationships + +In progress - April 3 (Ruth) + +""" + +# Import Python standard and third-party packages +from pathlib import Path +import numpy as np +import seaborn as sns +import matplotlib.pyplot as plt + +# Import sting package +from sting.system.core import System +from sting.system.operations import SystemModifier +from sting.modules.power_flow.core import ACPowerFlow +from sting.modules.simulation_emt.core import SimulationEMT +from sting.modules.small_signal_modeling.core import SmallSignalModel + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model +def step1(t): + return 0.3 if t >= 0.5 else 0.0 + +def step2(t): + return 0.0 + +def step3(t): + return 0.1 if t > 1.0 else 0.0 + +inputs = {'infinite_sources_0': {'v_ref_d': step1}, + 'gfmi_e_0': {'p_ref': step2, + 'q_ref': step2, + 'v_ref': step2, + 'v_dc_ref': step2, + 'v_s': step2, + 'i_load_ref': step2}} + +t_max = 2.0 + +# Load system from CSV files +sys = System.from_csv(case_directory=case_dir) + +n = 3 +Prange = np.linspace(100,0,n) +Qrange = np.linspace(-50,50,n) + +Sbase = 100 # MVA +vdc_ref = 1.05 # pu + +tps = np.linspace(0, t_max, 500) +dt = tps[1] - tps[0] + +def run_sim(sys, P, Q, Sbase, vdc_ref, factor): + sys.gfmi_e[0].minimum_active_power_MW = -P*factor + sys.gfmi_e[0].maximum_active_power_MW = -P*factor + sys.gfmi_e[0].minimum_reactive_power_MVAR = Q + sys.gfmi_e[0].maximum_reactive_power_MVAR = Q + sys.gfmi_e[0].i_load_ref = P/Sbase/vdc_ref + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + sys_modifier.combine_shunts() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + + emt_sc = SimulationEMT(system=sys) + emt_sc.sim(t_max, inputs) + return emt_sc + + +# Case 1: Pref = Pload +results_rocof = np.zeros([Prange.shape[0],Qrange.shape[0]]) +results_nadir = np.zeros([Prange.shape[0],Qrange.shape[0]]) + +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + emt_sc = run_sim(sys, P, Q, Sbase, vdc_ref, 1.0) + + # Measure some performance metrics - frequency ROCOF and nadir and steady state error ? + gfm = emt_sc.system.gfmi_e[0].variables_emt.x.value + w_pc = gfm[1,:] + # nadir + nadir = np.min(w_pc) + results_nadir[i,j] = nadir + # rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) + df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof + df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) + rocof = np.max(df_dt_ma) + + results_rocof[i,j] = rocof + j += 1 + i += 1 + +# Plot results +fig, axes = plt.subplots(nrows=2,ncols=3, figsize=(30,15)) + +sns.heatmap(results_nadir, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0,1], cbar=True, cbar_kws = {'label': 'nadir'}) +axes[0,1].set_xlabel("Q_sh") +axes[0,1].set_ylabel("P_load") +axes[0,1].set_title("Pref = Pload") + +sns.heatmap(results_rocof, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1,1], cbar=True, cbar_kws = {'label': 'rocof'}) +axes[1,1].set_xlabel("Q_sh") +axes[1,1].set_ylabel("P_load") +axes[1,1].set_title("Pref = Pload") + + +# Case 2: Pref < Pload (Pref=0.8*Pload) + +results_rocof = np.zeros([Prange.shape[0],Qrange.shape[0]]) +results_nadir = np.zeros([Prange.shape[0],Qrange.shape[0]]) + +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + emt_sc = run_sim(sys, P, Q, Sbase, vdc_ref, 0.8) + + # Measure some performance metrics - frequency ROCOF and nadir and steady state error ? + gfm = emt_sc.system.gfmi_e[0].variables_emt.x.value + w_pc = gfm[1,:] + # nadir + nadir = np.min(w_pc) + results_nadir[i,j] = nadir + # rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) + df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof + df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) + rocof = np.max(df_dt_ma) + + results_rocof[i,j] = rocof + j += 1 + i += 1 + +# Plot results + +sns.heatmap(results_nadir, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0,0], cbar=True, cbar_kws = {'label': 'nadir'}) +axes[0,0].set_xlabel("Q_sh") +axes[0,0].set_ylabel("P_load") +axes[0,0].set_title("Pref = 0.8*Pload") + +sns.heatmap(results_rocof, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1,0], cbar=True, cbar_kws = {'label': 'rocof'}) +axes[1,0].set_xlabel("Q_sh") +axes[1,0].set_ylabel("P_load") +axes[1,0].set_title("Pref = 0.8*Pload") + + +# Case 3: Pref > Pload (Pref=1.2*Pload) + +results_rocof = np.zeros([Prange.shape[0],Qrange.shape[0]]) +results_nadir = np.zeros([Prange.shape[0],Qrange.shape[0]]) + +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + emt_sc = run_sim(sys, P, Q, Sbase, vdc_ref, 1.2) + + # Measure some performance metrics - frequency ROCOF and nadir and steady state error ? + gfm = emt_sc.system.gfmi_e[0].variables_emt.x.value + w_pc = gfm[1,:] + # nadir + nadir = np.min(w_pc) + results_nadir[i,j] = nadir + # rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) + df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof + df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) + rocof = np.max(df_dt_ma) + + results_rocof[i,j] = rocof + j += 1 + i += 1 + +# Plot results + +sns.heatmap(results_nadir, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0,2], cbar=True, cbar_kws = {'label': 'nadir'}) +axes[0,2].set_xlabel("Q_sh") +axes[0,2].set_ylabel("P_load") +axes[0,2].set_title("Pref = 1.2*Pload") + +sns.heatmap(results_rocof, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1,2], cbar=True, cbar_kws = {'label': 'rocof'}) +axes[1,2].set_xlabel("Q_sh") +axes[1,2].set_ylabel("P_load") +axes[1,2].set_title("Pref = 1.2*Pload") + +plt.show() +plt.savefig(str(case_dir)+"/emt_heatmaps.png") + +print('ok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_heatmaps.png b/examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_heatmaps.png new file mode 100644 index 0000000000000000000000000000000000000000..3855df09961b4e8ce86d81b5efd902c28ac597d7 GIT binary patch literal 93330 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


X}yq`~7H1j3(W~w|9&QBpY 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 literal 0 HcmV?d00001 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv index 8ae66d8..5e0b55d 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv @@ -1,2 +1,2 @@ -name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,h_s,kd_pu,droop_q_pu,tau_pc_s,kp_vc_pu,ki_vc_puHz,kp_v_dc,ki_v_dc,kp_i_L,ki_i_L,l_dc,c_dc,v_dc_ref,v_s,Ti_L,Tv_dc,Ti_dc,Kff_idc,Kff_iload,Ti_load,Tload,i_load_ref -solar,santiago,-10,-10,0,0,1.00E+02,4.80E-01,60,0,0.01,0.1,10000,0.1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0.5,70,0.01,0.001,1,10,1.2,20,1,10,1,20,1.0,0.5,0.01,0.01,0.01,1,1,0.01,0.001,0.1 \ No newline at end of file +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,h_s,kd_pu,droop_q_pu,tau_pc_s,kp_vc_pu,ki_vc_puHz,kp_v_dc,ki_v_dc,kp_i_L,ki_i_L,l_dc,c_dc,v_dc_ref,v_s,Ti_L,Tv_dc,Ti_dc,Kff_idc,Kff_iload,Ti_load,Tload,i_load_ref,Pbat_max_pu,SOC_max_pu,SOC_init_pu +solar,santiago,-50,-50,-10,10,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0.5,70,0.01,0.001,1,10,1.2,20,1,10,0.1,20,1.05,0.5,0.01,0.01,0.01,1,1,0.01,0.001,0.5,1.5,0.25,0.125 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/active_power_balance_by_bus.csv index c0a07a8..43499bd 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,timepoint_1,20.024029596964457,-9.99090909090909e-9,0.0,20.024029586973448 -santiago,timepoint_1,-10.0,-9.990909090909092e-9,0.0,-10.000000009990726 +lima,timepoint_1,60.31569213226618,-9.974940964403198e-9,0.0,60.31569212229119 +santiago,timepoint_1,-50.0,-9.974940964403193e-9,0.0,-50.00000000997488 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/bus_voltage.csv index 753a447..d17c3a2 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/bus_voltage.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/bus_voltage.csv @@ -1,3 +1,3 @@ id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg -0,lima,timepoint_1,0.9850307325446462,0.0 -1,santiago,timepoint_1,1.0144691438583457,-4.385666517431731 +0,lima,timepoint_1,1.0039676883731015,0.0 +1,santiago,timepoint_1,0.9966223958255749,-15.986449720491722 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/costs_summary.csv index 6122372..22bb914 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/costs_summary.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/costs_summary.csv @@ -1,2 +1,2 @@ component,cost -total_cost_USD,-0.00003996363636363637 +total_cost_USD,-0.00003989976385761279 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/generator_dispatch.csv index c8329f2..22fa1bb 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/generator_dispatch.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/generator_dispatch.csv @@ -1,3 +1,3 @@ id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,timepoint_1,20.024029596964457,-11.986372758049026 -0,gfmi_e,solar,timepoint_1,-10.0,0.0 +0,infinite_sources,gen1,timepoint_1,60.31569213226618,1.388783529105015 +0,gfmi_e,solar,timepoint_1,-50.0,0.7526101226466527 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/line_flows.csv index efe99e5..6eac7b5 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/line_flows.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/line_flows.csv @@ -1,2 +1,2 @@ line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,20.02402958697347,-11.986372768040068,-10.000000009990744,-9.99095410228382e-9,10.024029576982725,-11.986372778031022 +tx_1,lima,santiago,inf,60.31569212229122,1.3887835191300466,-50.00000000997491,0.7526101126717135,10.315692112316306,2.14139363180176 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/load_shedding.csv index 8c5a706..2061806 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/load_shedding.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/load_shedding.csv @@ -1,3 +1,3 @@ bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,timepoint_1,-9.99090909090909e-9,-9.99090909090909e-9 -santiago,timepoint_1,-9.990909090909092e-9,-9.990909090909097e-9 +lima,timepoint_1,-9.974940964403198e-9,-9.9749409644032e-9 +santiago,timepoint_1,-9.974940964403193e-9,-9.97494096440319e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv index 726b458..1f256f2 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,timepoint_1,-11.986372758049026,-9.99090909090909e-9,0.0,-11.986372768040068 -santiago,timepoint_1,0.0,-9.990909090909097e-9,0.0,-9.990909050803768e-9 +lima,timepoint_1,1.388783529105015,-9.9749409644032e-9,0.0,1.3887835191300466 +santiago,timepoint_1,0.7526101226466527,-9.97494096440319e-9,0.0,0.7526101126717357 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv index 6b473ea..9d22778 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.9590749740600586 +time_spent_seconds,0.11365103721618652 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/F.csv index e41178d..435c050 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/F.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/F.csv @@ -1,4 +1,4 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_e_0', 'i_bus_D')","('gfmi_e_0', 'i_bus_Q')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_e_0', 'i_bus_D')","('gfmi_e_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" "('infinite_sources_0', 'v_ref_d')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 @@ -11,11 +11,11 @@ Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","( "('gfmi_e_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 "('gfmi_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('pa_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 -"('pa_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 -"('pa_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('pa_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 -"('se_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/G.csv index 72d52df..9d27ee0 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/G.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/G.csv @@ -11,11 +11,11 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('gfmi_e_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 "('gfmi_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/H.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/H.csv index ba4ffa4..1358a61 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/H.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/H.csv @@ -1,11 +1,11 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_e_0', 'i_bus_D')","('gfmi_e_0', 'i_bus_Q')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_e_0', 'i_bus_D')","('gfmi_e_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" "('infinite_sources_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/L.csv index aa2b8d8..5b2598d 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/L.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/component_connection_matrices/L.csv @@ -3,9 +3,9 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv index dd0ac99..84befb8 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv @@ -1,29 +1,29 @@ -Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_e_0', 'angle_pc')","('gfmi_e_0', 'w_pc')","('gfmi_e_0', 'p_pc')","('gfmi_e_0', 'q_pc')","('gfmi_e_0', 'pi_vc')","('gfmi_e_0', 'i_vsc_d')","('gfmi_e_0', 'i_vsc_q')","('gfmi_e_0', 'i_bus_d')","('gfmi_e_0', 'i_bus_q')","('gfmi_e_0', 'v_lcl_sh_d')","('gfmi_e_0', 'v_lcl_sh_q')","('gfmi_e_0', 'i_l_f')","('gfmi_e_0', 'v_dc_f')","('gfmi_e_0', 'i_dc_f')","('gfmi_e_0', 'i_load_f')","('gfmi_e_0', 'x_1')","('gfmi_e_0', 'x_2')","('gfmi_e_0', 'i_L')","('gfmi_e_0', 'v_dc')","('gfmi_e_0', 'i_load')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'i_bus_d')",-7.5398223686155035,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-749.3755584175443,-83.21950461965382,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_q')",-376.99111843077515,-7.5398223686155035,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,83.21950461965382,-749.3755584175443,0.0,0.0,0.0,0.0 +Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_e_0', 'angle_pc')","('gfmi_e_0', 'w_pc')","('gfmi_e_0', 'p_pc')","('gfmi_e_0', 'q_pc')","('gfmi_e_0', 'pi_vc')","('gfmi_e_0', 'i_vsc_d')","('gfmi_e_0', 'i_vsc_q')","('gfmi_e_0', 'i_bus_d')","('gfmi_e_0', 'i_bus_q')","('gfmi_e_0', 'v_lcl_sh_d')","('gfmi_e_0', 'v_lcl_sh_q')","('gfmi_e_0', 'i_l_f')","('gfmi_e_0', 'v_dc_f')","('gfmi_e_0', 'i_dc_f')","('gfmi_e_0', 'i_load_f')","('gfmi_e_0', 'x_1')","('gfmi_e_0', 'x_2')","('gfmi_e_0', 'i_L')","('gfmi_e_0', 'v_dc')","('gfmi_e_0', 'i_load')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'i_bus_d')",-7.5398223686155035,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-723.1203801581415,-213.50908482472548,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_q')",-376.99111843077515,-7.5398223686155035,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,213.50908482472548,-723.1203801581415,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'angle_pc')",0.0,0.0,0.0,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'w_pc')",0.0,0.0,0.0,-70.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'p_pc')",0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,0.0,1011.6644575859594,8.949041881888602,-98.5341583153472,-2.792568273869858,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'q_pc')",0.0,0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,8.949041881888602,-1011.6644575859594,2.792568273869858,-98.5341583153472,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'pi_vc')",0.0,0.0,0.0,0.0,0.0,-0.1,0.0,0.0,0.0,0.0,0.0,-9.999608776788051,-0.08845513655734374,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_vsc_d')",0.0,0.0,0.0,37.086415468269735,0.0,-37.69911184307752,3769.9111843077517,-37.69911184307751,376.99111843077515,0.0,0.0,-7539.674880939275,-33.34680086169996,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_vsc_q')",0.0,0.0,0.0,37.44573462619731,0.0,0.0,0.0,-376.99111843077515,-37.69911184307751,0.0,0.0,0.0,-3769.9111843077517,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_bus_d')",0.0,0.0,-54.17296510215203,-1.052773436860497,0.0,0.0,0.0,0.0,0.0,-56.54866776461627,376.99111843077515,1884.9555921538758,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1874.5984341253277,197.32788745394876,0.0,0.0 -"('gfmi_e_0', 'i_bus_q')",0.0,0.0,1911.4617786551519,37.1465025469378,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-56.54866776461627,0.0,1884.9555921538758,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-197.32788745394876,-1874.5984341253277,0.0,0.0 -"('gfmi_e_0', 'v_lcl_sh_d')",0.0,0.0,0.0,3.3737093079370326,0.0,0.0,0.0,3769.9111843077517,0.0,-3769.9111843077517,0.0,-0.37699111843077515,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'v_lcl_sh_q')",0.0,0.0,0.0,-381.3885153419943,0.0,0.0,0.0,0.0,3769.9111843077517,0.0,-3769.9111843077517,-376.99111843077515,-0.37699111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'p_pc')",0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,0.0,987.9260565411992,23.78030884341824,-496.76780316078685,-70.54199784602056,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'q_pc')",0.0,0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,23.78030884341824,-987.9260565411992,70.54199784602056,-496.76780316078685,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'pi_vc')",0.0,0.0,0.0,0.0,0.0,-0.1,0.0,0.0,0.0,0.0,0.0,-9.997104207562575,-0.24063969567519944,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_vsc_d')",0.0,0.0,0.0,346.7421388408102,0.0,-37.69911184307752,3769.9111843077517,-75.39822368615502,376.99111843077515,0.0,0.0,-7538.730680585775,-90.71902801143482,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_vsc_q')",0.0,0.0,0.0,158.9980800432565,0.0,0.0,0.0,-376.99111843077515,-75.39822368615502,0.0,0.0,0.0,-3769.9111843077517,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_bus_d')",0.0,0.0,-236.09093185117854,-26.593706664312624,0.0,0.0,0.0,0.0,0.0,-56.54866776461627,376.99111843077515,1884.9555921538758,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1732.449388861673,742.7494190000986,0.0,0.0 +"('gfmi_e_0', 'i_bus_q')",0.0,0.0,1863.6945957046444,187.27704971398418,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-56.54866776461627,0.0,1884.9555921538758,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-742.7494190000986,-1732.449388861673,0.0,0.0 +"('gfmi_e_0', 'v_lcl_sh_d')",0.0,0.0,0.0,8.964965227509495,0.0,0.0,0.0,376.99111843077515,0.0,-376.99111843077515,0.0,-37.69911184307752,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'v_lcl_sh_q')",0.0,0.0,0.0,-372.4393489823719,0.0,0.0,0.0,0.0,376.99111843077515,0.0,-376.99111843077515,-376.99111843077515,-37.69911184307752,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_l_f')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-100.0,0.0,0.0,0.0,0.0,0.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'v_dc_f')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-100.0,0.0,0.0,0.0,0.0,0.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_dc_f')",0.0,0.0,0.0,0.0,0.0,0.09932789605777746,-9.932789605777746,100.08337013864903,1.7018489238183393e-16,0.0,0.0,9.932401011992427,0.0878606260974435,0.0,0.0,-100.0,0.0,0.0,0.0,0.0,9.9410705862438,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_dc_f')",0.0,0.0,0.0,0.0,0.0,0.40167191080960485,-40.167191080960485,84.52521309107098,1.0406627303014625e-14,0.0,0.0,40.155559496144,0.9665820637849917,0.0,0.0,-100.0,0.0,0.0,0.0,0.0,33.9514038538795,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_load_f')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-100.0,0.0,0.0,0.0,0.0,100.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'x_1')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-20.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'x_2')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-10.0,-12.0,10.0,10.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_L')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-452.38934211693015,376.99111843077515,376.99111843077515,376.99111843077515,376.99111843077515,0.0,-188.49555921538757,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.0,0.0,-0.018722867313098655,1.8722867313098654,-18.86527082244527,-3.207909645952434e-17,0.0,0.0,-1.8722134831069943,-0.01656133784925169,0.022215865600420028,0.02665903872050403,-0.022215865600420028,-0.022215865600420028,-0.022215865600420028,-0.022215865600420028,9.42477796076938,-1.873847659353666,-18.84955592153876,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_L')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-3958.406743523139,-4750.0880922277665,3958.406743523139,3958.406743523139,3958.406743523139,3958.406743523139,0.0,-1795.1958020513098,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.0,0.0,-0.07571337144916976,7.571337144916975,-15.932627309401225,-1.9616030330278627e-15,0.0,0.0,-7.569144642832431,-0.18219642664071545,5.680719791051915,6.816863749262298,-5.680719791051915,-5.680719791051915,-5.680719791051915,-5.680719791051915,8.97597901025655,-6.399688855584481,-18.84955592153876,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_load')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",5620.316660029999,-624.1462815266722,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,0.0,0.0,-5654.8667481872935,0.0 -"('pa_rc_0', 'v_bus_Q')",624.1462815266722,5620.316660029999,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,0.0,0.0,-5654.8667481872935 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,-42.625779353978785,0.0,0.0,0.0,0.0,0.0,0.0,5623.795274257007,591.983659401928,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,5654.8667481872935,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,-555.7890886726366,0.0,0.0,0.0,0.0,0.0,0.0,-591.983659401928,5623.795274257007,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,5654.8667481872935 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,0.0,-7.5398223686155035,376.99111843077515 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,-376.99111843077515,-7.5398223686155035 +"('shunt_parallel_rc_0', 'v_bus_D')",5423.402824069048,-1601.3181281788504,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,0.0,0.0,-5654.8667481872935,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",1601.3181281788504,5423.402824069048,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,0.0,0.0,-5654.8667481872935 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,-740.2906646531766,0.0,0.0,0.0,0.0,0.0,0.0,5197.348140598278,2228.2482458590544,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,5654.8667481872935,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,-2739.060301026596,0.0,0.0,0.0,0.0,0.0,0.0,-2228.2482458590544,5197.348140598278,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,5654.8667481872935 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,0.0,-7.5398223686155035,376.99111843077515 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,-376.99111843077515,-7.5398223686155035 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv index 88c69cc..3553091 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv @@ -14,16 +14,16 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('gfmi_e_0', 'v_lcl_sh_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_l_f')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'v_dc_f')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_dc_f')",0.0,0.0,0.0,-0.09932789605777746,-9.932789605777746,0.0,0.0,0.0 +"('gfmi_e_0', 'i_dc_f')",0.0,0.0,0.0,-0.4016719108096049,-40.167191080960485,0.0,0.0,0.0 "('gfmi_e_0', 'i_load_f')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'x_1')",0.0,0.0,0.0,0.0,0.0,20.0,0.0,0.0 "('gfmi_e_0', 'x_2')",0.0,0.0,0.0,0.0,0.0,12.0,0.0,0.0 -"('gfmi_e_0', 'i_L')",0.0,0.0,0.0,0.0,0.0,452.38934211693015,376.99111843077515,0.0 -"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.018722867313098655,1.8722867313098654,-0.02665903872050403,0.0,0.0 +"('gfmi_e_0', 'i_L')",0.0,0.0,0.0,0.0,0.0,4750.0880922277665,3769.9111843077517,0.0 +"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.07571337144916976,7.571337144916975,-6.816863749262298,0.0,0.0 "('gfmi_e_0', 'i_load')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1000.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/C.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/C.csv index b1e47fb..7f0ea0e 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/C.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/C.csv @@ -1,11 +1,11 @@ -Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_e_0', 'angle_pc')","('gfmi_e_0', 'w_pc')","('gfmi_e_0', 'p_pc')","('gfmi_e_0', 'q_pc')","('gfmi_e_0', 'pi_vc')","('gfmi_e_0', 'i_vsc_d')","('gfmi_e_0', 'i_vsc_q')","('gfmi_e_0', 'i_bus_d')","('gfmi_e_0', 'i_bus_q')","('gfmi_e_0', 'v_lcl_sh_d')","('gfmi_e_0', 'v_lcl_sh_q')","('gfmi_e_0', 'i_l_f')","('gfmi_e_0', 'v_dc_f')","('gfmi_e_0', 'i_dc_f')","('gfmi_e_0', 'i_load_f')","('gfmi_e_0', 'x_1')","('gfmi_e_0', 'x_2')","('gfmi_e_0', 'i_L')","('gfmi_e_0', 'v_dc')","('gfmi_e_0', 'i_load')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'i_bus_D')",0.9938902029533464,-0.11037329601563937,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_Q')",0.11037329601563937,0.9938902029533464,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_bus_D')",0.0,0.0,-0.007537892801389666,0.0,0.0,0.0,0.0,0.0,0.0,0.9945053570112422,0.10468569566059048,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'i_bus_Q')",0.0,0.0,-0.09828509024563639,0.0,0.0,0.0,0.0,0.0,0.0,-0.10468569566059048,0.9945053570112422,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 +Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_e_0', 'angle_pc')","('gfmi_e_0', 'w_pc')","('gfmi_e_0', 'p_pc')","('gfmi_e_0', 'q_pc')","('gfmi_e_0', 'pi_vc')","('gfmi_e_0', 'i_vsc_d')","('gfmi_e_0', 'i_vsc_q')","('gfmi_e_0', 'i_bus_d')","('gfmi_e_0', 'i_bus_q')","('gfmi_e_0', 'v_lcl_sh_d')","('gfmi_e_0', 'v_lcl_sh_q')","('gfmi_e_0', 'i_l_f')","('gfmi_e_0', 'v_dc_f')","('gfmi_e_0', 'i_dc_f')","('gfmi_e_0', 'i_load_f')","('gfmi_e_0', 'x_1')","('gfmi_e_0', 'x_2')","('gfmi_e_0', 'i_L')","('gfmi_e_0', 'v_dc')","('gfmi_e_0', 'i_load')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'i_bus_D')",0.9590681912721563,-0.2831752187073487,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_Q')",0.2831752187073487,0.9590681912721563,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_bus_D')",0.0,0.0,-0.1309121324371581,0.0,0.0,0.0,0.0,0.0,0.0,0.9190929463128948,0.3940408050416634,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'i_bus_Q')",0.0,0.0,-0.484372209460925,0.0,0.0,0.0,0.0,0.0,0.0,-0.3940408050416634,0.9190929463128948,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/D.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/D.csv index aa2b8d8..5b2598d 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/D.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/D.csv @@ -3,9 +3,9 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv index a8d0368..6d1790e 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv @@ -1,9 +1,9 @@ name,component,type,init -v_ref_d,infinite_sources_0,device,0.9319147420205057 -v_ref_q,infinite_sources_0,device,2.4364261741631428e-17 -p_ref,gfmi_e_0,device,-0.09970849663622566 -q_ref,gfmi_e_0,device,0.0019433557584956256 -v_ref,gfmi_e_0,device,1.0117040377962803 -v_dc_ref,gfmi_e_0,device,1.0 +v_ref_d,infinite_sources_0,device,1.0602915467925695 +v_ref_q,infinite_sources_0,device,-6.184065849485258e-17 +p_ref,gfmi_e_0,device,-0.4924473672884809 +q_ref,gfmi_e_0,device,0.05787698596992697 +v_ref,gfmi_e_0,device,0.9882122222891866 +v_dc_ref,gfmi_e_0,device,1.05 v_s,gfmi_e_0,device,0.5 -i_load_ref,gfmi_e_0,device,0.1 +i_load_ref,gfmi_e_0,device,0.5 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv index 8fe7525..787fd4f 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv @@ -1,29 +1,29 @@ name,component,type,init -i_bus_d,infinite_sources_0,"",0.21547208231583534 -i_bus_q,infinite_sources_0,"",0.09850474697346907 +i_bus_d,infinite_sources_0,"",0.5722653561902081 +i_bus_q,infinite_sources_0,"",-0.18339083649397844 angle_pc,gfmi_e_0,"",0.0 w_pc,gfmi_e_0,"",1.0 -p_pc,gfmi_e_0,"",-0.09970849663622566 -q_pc,gfmi_e_0,"",0.0019433557584956256 -pi_vc,gfmi_e_0,"",1.0008337013864903 -i_vsc_d,gfmi_e_0,"",-0.09932789605777746 -i_vsc_q,gfmi_e_0,"",0.09837477238891429 -i_bus_d,gfmi_e_0,"",-0.0985341583153472 -i_bus_q,gfmi_e_0,"",-0.0027925682738698583 -v_lcl_sh_d,gfmi_e_0,"",1.0116644575859595 -v_lcl_sh_q,gfmi_e_0,"",0.008949041881888602 -i_l_f,gfmi_e_0,"",0.001178588275124015 -v_dc_f,gfmi_e_0,"",1.0 -i_dc_f,gfmi_e_0,"",-0.099410705862438 -i_load_f,gfmi_e_0,"",0.1 -x_1,gfmi_e_0,"",0.0005892941375620075 -x_2,gfmi_e_0,"",0.5 -i_L,gfmi_e_0,"",0.001178588275124015 -v_dc,gfmi_e_0,"",1.0 -i_load,gfmi_e_0,"",0.1 -v_bus_D,pa_rc_0,"",0.9850307325446462 -v_bus_Q,pa_rc_0,"",0.0 -v_bus_D,pa_rc_1,"",1.0114986905096575 -v_bus_Q,pa_rc_1,"",-0.07757604616073832 -i_br_D,se_rl_0,"",0.15403176129835686 -i_br_Q,se_rl_0,"",0.05601655115598958 +p_pc,gfmi_e_0,"",-0.4924473672884809 +q_pc,gfmi_e_0,"",0.05787698596992697 +pi_vc,gfmi_e_0,"",0.8875147374562453 +i_vsc_d,gfmi_e_0,"",-0.42175550635008513 +i_vsc_q,gfmi_e_0,"",0.9197620895795204 +i_bus_d,gfmi_e_0,"",-0.49676780316078684 +i_bus_q,gfmi_e_0,"",-0.07054199784602057 +v_lcl_sh_d,gfmi_e_0,"",0.9879260565411991 +v_lcl_sh_q,gfmi_e_0,"",0.02378030884341824 +i_l_f,gfmi_e_0,"",0.30137154502195707 +v_dc_f,gfmi_e_0,"",1.05 +i_dc_f,gfmi_e_0,"",-0.35648974046573473 +i_load_f,gfmi_e_0,"",0.5 +x_1,gfmi_e_0,"",0.15786128548769174 +x_2,gfmi_e_0,"",0.5238095238095238 +i_L,gfmi_e_0,"",0.30137154502195707 +v_dc,gfmi_e_0,"",1.05 +i_load,gfmi_e_0,"",0.5 +v_bus_D,shunt_parallel_rc_0,"",1.0039676883731015 +v_bus_Q,shunt_parallel_rc_0,"",0.0 +v_bus_D,shunt_parallel_rc_1,"",0.9580798748577896 +v_bus_Q,shunt_parallel_rc_1,"",-0.2744797866029319 +i_br_D,branch_series_rl_0,"",0.5505748558041546 +i_br_Q,branch_series_rl_0,"",-0.08076412991454072 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/y.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/y.csv index 30a92aa..cf58c36 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/y.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/y.csv @@ -1,11 +1,11 @@ name,component,type,init -i_bus_D,infinite_sources_0,"",0.2032832980270174 -i_bus_Q,infinite_sources_0,"",0.12168526688588112 -i_bus_D,gfmi_e_0,"",-0.09828509024563638 -i_bus_Q,gfmi_e_0,"",0.007537892801389665 -v_bus_D,pa_rc_0,"",0.9850307325446462 -v_bus_Q,pa_rc_0,"",0.0 -v_bus_D,pa_rc_1,"",1.0114986905096575 -v_bus_Q,pa_rc_1,"",-0.07757604616073832 -i_br_D,se_rl_0,"",0.15403176129835686 -i_br_Q,se_rl_0,"",0.05601655115598958 +i_bus_D,infinite_sources_0,"",0.6007732403221651 +i_bus_Q,infinite_sources_0,"",-0.013832950454366671 +i_bus_D,gfmi_e_0,"",-0.484372209460925 +i_bus_Q,gfmi_e_0,"",0.1309121324371581 +v_bus_D,shunt_parallel_rc_0,"",1.0039676883731015 +v_bus_Q,shunt_parallel_rc_0,"",0.0 +v_bus_D,shunt_parallel_rc_1,"",0.9580798748577896 +v_bus_Q,shunt_parallel_rc_1,"",-0.2744797866029319 +i_br_D,branch_series_rl_0,"",0.5505748558041546 +i_br_Q,branch_series_rl_0,"",-0.08076412991454072 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py index d862599..2908494 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py @@ -1,70 +1,124 @@ + + # In progress - March 5, 2026 - Ruth # Import Python standard and third-party packages from pathlib import Path +from scipy import signal +import numpy as np # Import sting package -from sting.system.core import System -from sting.system.operations import SystemModifier -from sting.modules.power_flow.core import ACPowerFlow -from sting.modules.simulation_emt.core import SimulationEMT -from sting.modules.small_signal_modeling.core import SmallSignalModel +from sting import main +# from sting.system.core import System +# from sting.system.operations import SystemModifier +# from sting.modules.power_flow.core import ACPowerFlow +# from sting.modules.simulation_emt.core import SimulationEMT +# from sting.modules.small_signal_modeling.core import SmallSignalModel # Specify path of the case study directory case_dir = Path(__file__).resolve().parent # Construct system and small-signal model def step1(t): - return 0.1 if t >= 0.5 else 0.0 + return 0.5 if t >= 0.5 else 0.0 def step2(t): return 0.0 def step3(t): - return 0.1 if t > 1.0 else 0.0 + return 0.25 if t > 1.0 else 0.0 + +def step3_neg(t): + return -0.25 if t > 1.0 else 0.0 + +def square_oscillation(t): + osc = 0.1*signal.square(2 * np.pi * 14 * t) + return osc # Specify inputs to excite - any constant input does not need to be specified # NB: input is a perturbation from the nominal value -inputs = {'infinite_sources_0': {'v_ref_d': step1}, - 'gfmi_e_0': {'p_ref': step2, +inputs = {'infinite_sources_0': {'v_ref_d': step2}, + 'gfmi_e_0': {'p_ref': step3_neg, 'q_ref': step2, 'v_ref': step2, 'v_dc_ref': step2, 'v_s': step2, - 'i_load_ref': step2}} + 'i_load_ref': step3}} t_max = 4.0 -# Load system from CSV files -sys = System.from_csv(case_directory=case_dir) +# Construct system and small-signal model +sys, ssm = main.run_ssm(case_directory=case_dir) +ssm.simulate_ssm(t_max=t_max, inputs=inputs) +# Run EMT simulation +main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) + + +# # Load system from CSV files +# sys = System.from_csv(case_directory=case_dir) + +# # Match power reference with load +# P_load = -100 # MW +# sys.gfmi_e[0].minimum_active_power_MW = -P_load +# sys.gfmi_e[0].maximum_active_power_MW = -P_load +# sys.gfmi_e[0].minimum_reactive_power_MVAR = 0 +# sys.gfmi_e[0].maximum_reactive_power_MVAR = 0 + +# # i_load_ref*v_dc_ref = i_load_ref*1 = p_load = i_load_ref +# Sbase = 100 # MVA +# sys.gfmi_e[0].i_load_ref = P_load/Sbase +# sys.gfmi_e[0].i_load_ref = 0 -# Match power reference with load -P_load = 10 # MW -sys.gfmi_e[0].minimum_active_power_MW = -P_load -sys.gfmi_e[0].maximum_active_power_MW = -P_load -sys.gfmi_e[0].minimum_reactive_power_MVAR = 0 -sys.gfmi_e[0].maximum_reactive_power_MVAR = 0 +# # sys.gfmi_e[0].SOC_init_pu = 0.0 +# # sys.gfmi_e[0].Pbat_max_pu = 100.0 +# # sys.gfmi_e[0].v_dc_ref = 1.00 -# i_load_ref*v_dc_ref = i_load_ref*1 = p_load = i_load_ref -Sbase = 100 # MVA -sys.gfmi_e[0].i_load_ref = P_load/Sbase -# Run power flow -pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) -pf.solve() +# # Run power flow +# pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) +# pf.solve() -# Break down lines into branches and shunts for small-signal modeling -sys_modifier = SystemModifier(system=sys) -sys_modifier.decompose_lines() -sys_modifier.combine_shunts() +# # Break down lines into branches and shunts for small-signal modeling +# sys_modifier = SystemModifier(system=sys) +# sys_modifier.decompose_lines() +# sys_modifier.combine_shunts() + +# # Construct small-signal model +# ssm = SmallSignalModel(system=sys) +# ssm.construct_system_ssm() + +# ssm.simulate_ssm(t_max=t_max, inputs=inputs) + +# emt_sc = SimulationEMT(system=sys) +# emt_sc.sim(t_max, inputs) + +# # calculate SIL + +# # characteristic impedance +# import numpy as np +# l = sys.lines[0] +# Vbase = l.base_voltage_kV*1e3 +# Sbase = l.base_power_MVA*1e6 +# wbase = 2*np.pi*60 +# Zbase = Vbase**2/Sbase +# Ybase = 1/Zbase +# l = sys.lines[0] +# x = l.x_pu*Zbase +# r = l.r_pu*Zbase +# g = l.g_pu*Ybase +# b = l.b_pu*Ybase +# Z0 = np.sqrt((r + x*1j)/(g + b*1j)) + +# # for 230kV line, Z0 should be 360-390 Ohms... + +# Z0_ideal = np.sqrt(x/b) +# SIL_ideal_MW = (Vbase**2/Z0_ideal)/1e6 + +# # Average transmission line parameter values +# # HV (69-230kV) - L=1.3mH/km, c=8.75nF/km -# Construct small-signal model -ssm = SmallSignalModel(system=sys) -ssm.construct_system_ssm() -ssm.simulate_ssm(t_max=t_max, inputs=inputs) -emt_sc = SimulationEMT(system=sys) -emt_sc.sim(t_max, inputs) -print('ok') \ No newline at end of file +print('ok') +# %% diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py new file mode 100644 index 0000000..1785da0 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py @@ -0,0 +1,453 @@ +""" +In progress (March 6, 2026 - Ruth) + +Simulates a a GFMIe connected to an infinite source via a transmission line. + +1. Computes and simulates the small-signal model using STING. + +2. Runs EMT using sting. + +3. Compares corresponding SSM and EMT states in plots. + +""" + +# Import Python standard and third-party packages +import os +import numpy as np +import plotly.graph_objects as go +from plotly.subplots import make_subplots +from pathlib import Path +from typing import Callable + +# Import sting package +from sting import main +from sting.utils.transformations import dq02abc, abc2dq0 +from sting.modules.small_signal_modeling.core import SmallSignalModel +from sting.system.core import System +from sting.system.component import Component +from sting.system.operations import SystemModifier +from sting.modules.power_flow.core import ACPowerFlow +from sting.modules.simulation_emt.core import SimulationEMT + + +# Define additional functions that return required data in order to compare SSM and EMT + +def simulate_ssm_ruth(ssm: SmallSignalModel, t_max: float, inputs: dict[str, dict[str, Callable[[float], float]]] = None, settings={'dense_output': True, 'method': 'Radau', 'max_step': 0.001}): + """Simulate the small-signal model under a given input profile.""" + + x0 = np.zeros_like(ssm.model.x.init) + tps, solution = ssm.model.simulate(t_max=t_max, inputs=inputs, x0=x0, settings=settings, output_directory=ssm.output_directory, plot=False) + + # Add the initial conditions back to the solution (for plotting purposes) + for i in range(len(ssm.model.x.init)): + solution[i] = solution[i] + ssm.model.x.init[i] + + components_to_plot = np.unique(ssm.model.x.component) # Get the components in the same order as solution vector + i = 0 # Initialize counter + + name_list = [] + component_list = [] + # Make a html file for each component. Each file plots the states corresponding to each component. + for component in components_to_plot: + number_of_states = sum(ssm.model.x.component == component) + nrows = int(np.ceil(number_of_states / 2)) + ncols = 2 if number_of_states > 1 else 1 + #fig = make_subplots(rows=nrows, cols=ncols) + for j in range(number_of_states): + row = j // ncols + 1 + col = j % ncols + 1 + #fig.add_trace(go.Scatter(x=tps, y=solution[i]), row=row, col=col) + #fig.update_xaxes(title_text='Time [s]', row=row, col=col) + #fig.update_yaxes(title_text=ssm.model.x.name[i], row=row, col=col) + name_list.append(ssm.model.x.name[i]) # added + component_list.append(component) + i += 1 + + + #fig.update_layout(title_text = component, title_x=0.5, showlegend = False) + #fig.write_html(os.path.join(ssm.output_directory, f"{component}.html")) + + return tps, solution, name_list, component_list + +def run_emt_ruth(sys, t_max, inputs, case_directory=os.getcwd(), model_settings=None, solver_settings=None): + """ + Routine to simulate the EMT dynamics of the system from a case study directory. + """ + + # Load system from CSV files + sys = System.from_csv(case_directory=case_directory) + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=model_settings, solver_settings=solver_settings) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + + emt_sc = SimulationEMT(system=sys) + emt_sc.sim(t_max, inputs) + + return sys, emt_sc + + +def reconstruct_input_timeseries(fcn_name, tps, init_cond): + output = np.zeros_like(tps) + i = 0 + for t in tps: + output[i] = fcn_name(t) + i += 1 + return output + init_cond # add back initial condition + + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model +def step1(t): + return 0.1 if t >= 0.5 else 0.0 + +def no_step(t): + return 0.0 + +def step3(t): + return 0.1 if t > 1.0 else 0.0 + +def noisy_i_load_ref(t): + avg = 0.1*np.sin(2*np.pi*1*t)/2 # 1 Hz + fast = 0.05*np.sin(2*np.pi*20*t)/2 # 20 Hz + return avg + fast + +def p_ref(t): + return -0.1*np.sin(2*np.pi*1*t) if t < 1 else 0 #1 Hz oscillation + +def slow_small_oscillation(t): + return 0.1*np.sin(2*np.pi*1*t) if t < 1 else 0 #1 Hz oscillation + +# Specify inputs to excite - any constant input does not need to be specified +# NB: input is a perturbation from the nominal value +inputs = {'infinite_sources_0': {'v_ref_d': no_step}, + 'gfmi_e_0': {'p_ref': step1, + 'q_ref': no_step, + 'v_ref': no_step, + 'v_dc_ref': no_step, + 'v_s': no_step, + 'i_load_ref': slow_small_oscillation}} + +t_max = 4.0 + +# Load system from CSV files +sys = System.from_csv(case_directory=case_dir) + +# Run power flow +pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) +pf.solve() + +# Build and perturb small-signal model +sys, ssm = main.run_ssm(case_directory=case_dir) +tps, sol, ssm_state_names, component_name = simulate_ssm_ruth(ssm, t_max=t_max, inputs=inputs) + +# Retrieve state traces +i_bus_d_inf = sol[0,:] # infinite source +i_bus_q_inf = sol[1,:] # infinite source +angle_pc = sol[2,:] +w_pc = sol[3,:] +p_pc = sol[4,:] +q_pc = sol[5,:] +pi_vc = sol[6,:] +i_vsc_d = sol[7,:] +i_vsc_q = sol[8,:] +i_bus_d = sol[9,:] # gfmie +i_bus_q = sol[10,:] # gfmie +v_sh_d = sol[11,:] +v_sh_q = sol[12,:] +iLf = sol[13,:] +vdcf = sol[14,:] +idcf = sol[15,:] +iloadf = sol[16,:] +x1 = sol[17,:] +x2 = sol[18,:] +iL = sol[19,:] +vdc = sol[20,:] +i_load = sol[21,:] +v_bus_D_rc0 = sol[22,:] # RC0 +v_bus_Q_rc0 = sol[23,:] # RC0 +v_bus_D_rc1 = sol[24,:] #RC1 +v_bus_Q_rc1 = sol[25,:] #RC1 +i_br_D_br0 = sol[26,:] #BR0 +i_br_Q_br0 = sol[27,:] #BR0 + +# Run EMT + +emt_sc = SimulationEMT(system=sys) +emt_sc.sim(t_max, inputs) + +emt_state_names = emt_sc.variables.x.name +# Define timepoints that will be used to evaluate the solution of the ODEs +tps = np.linspace(0, t_max, 500) +n_tps = len(tps) + +# Extract solutions directly from each component +gfm = emt_sc.system.gfmi_e[0].variables_emt.x.value +gfm_names = emt_sc.system.gfmi_e[0].variables_emt.x.name + +angle_pc_emt = gfm[0,:] +w_pc_emt = gfm[1,:] +p_pc_emt = gfm[2,:] +q_pc_emt = gfm[3,:] +gamma_emt = gfm[4,:] +i_vsc_a = gfm[5,:] +i_vsc_b = gfm[6,:] +i_vsc_c = gfm[7,:] +v_sh_a = gfm[8,:] +v_sh_b = gfm[9,:] +v_sh_c = gfm[10,:] +i_bus_a = gfm[11,:] +i_bus_b = gfm[12,:] +i_bus_c = gfm[13,:] +iLf_emt = gfm[14,:] +vdcf_emt = gfm[15,:] +idcf_emt = gfm[16,:] +iloadf_emt = gfm[17,:] +x1_emt = gfm[18,:] +x2_emt = gfm[19,:] +iL_emt = gfm[20,:] +vdc_emt = gfm[21,:] +i_load_emt = gfm[22,:] + +inf_src = emt_sc.system.infinite_sources[0].variables_emt.x.value + +# convert abc to dq for plotting comparison +angle_pc_emt_init = sys.gfmi_e[0].emt_init.angle_ref * np.pi / 180 + +i_bus_d_emt, i_bus_q_emt, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, angle_pc_emt)]) #? what angle to use here + +i_vsc_d_emt, i_vsc_q_emt, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_vsc_a, i_vsc_b, i_vsc_c, angle_pc_emt)]) + +v_sh_d_emt, v_sh_q_emt, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, angle_pc_emt)]) + +fig = make_subplots( + rows=14, cols=2, + horizontal_spacing=0.15, + vertical_spacing=0.05, +) + +r, c = 1,1 # define row, column for convenience +# plot inf source voltages? +fig.add_trace(go.Scatter(x=tps, y=angle_pc_emt, name="angle_pc (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=angle_pc, name="angle_pc (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='angle_pc', row=r, col=c) + +r, c = 1,2 +fig.add_trace(go.Scatter(x=tps, y=angle_pc_emt, name="angle_pc (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=angle_pc, name="angle_pc (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='angle_pc', row=r, col=c) + + +r, c = 2,1 +fig.add_trace(go.Scatter(x=tps, y=gamma_emt, name="gamma (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=pi_vc, name="gamma (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='gamma', row=r, col=c) + +r, c = 2, 2 +fig.add_trace(go.Scatter(x=tps, y=w_pc_emt, name="w_pc (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=w_pc, name="w_pc (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) + +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='w_pc', row=r, col=c) + +r, c = 3,1 +fig.add_trace(go.Scatter(x=tps, y=p_pc_emt, name="p_pc (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=p_pc, name="p_pc (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) + +pref = reconstruct_input_timeseries(inputs['gfmi_e_0']['p_ref'], tps, sys.gfmi_e[0].emt_init.p_ref) +fig.add_trace(go.Scatter(x=tps, y=pref, name="p_ref", mode='lines', line=dict(color='green', dash='dot'), legendgroup=str(r)), row=r, col=c) + +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p_pc', row=r, col=c) + +r, c = 3,2 +fig.add_trace(go.Scatter(x=tps, y=q_pc_emt, name="q_pc (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=q_pc, name="q_pc (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +qref = reconstruct_input_timeseries(inputs['gfmi_e_0']['q_ref'], tps, sys.gfmi_e[0].emt_init.q_ref) +fig.add_trace(go.Scatter(x=tps, y=qref, name="q_ref", mode='lines', line=dict(color='green', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='q_pc', row=r, col=c) + +r, c = 4,1 +fig.add_trace(go.Scatter(x=tps, y=i_vsc_d_emt, name="i_vsc_d (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, name="i_vsc_d (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_vsc_d', row=r, col=c) + +r, c = 4,2 +fig.add_trace(go.Scatter(x=tps, y=i_vsc_q_emt, name="i_vsc_q (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_vsc_q, name="i_vsc_q (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_vsc_q', row=r, col=c) + + +r, c = 5,1 +fig.add_trace(go.Scatter(x=tps, y=v_sh_d_emt, name="v_sh_d (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=v_sh_d, name="v_sh_d (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='v_sh_d', row=r, col=c) + + +r, c = 5,2 +fig.add_trace(go.Scatter(x=tps, y=v_sh_q_emt, name="v_sh_q (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=v_sh_q, name="v_sh_q (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='v_sh_q', row=r, col=c) + +r, c = 6,1 +fig.add_trace(go.Scatter(x=tps, y=i_bus_d_emt, name="i_bus_d (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_bus_d, name="i_bus_d (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_bus_d', row=r, col=c) + +r, c = 6,2 +fig.add_trace(go.Scatter(x=tps, y=i_bus_q_emt, name="i_bus_q (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_bus_q, name="i_bus_q (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_bus_q', row=r, col=c) + +r, c = 7,1 +fig.add_trace(go.Scatter(x=tps, y=iLf_emt, name="iLf (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=iLf, name="iLf (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='iLf', row=r, col=c) + + +r, c = 7,2 +fig.add_trace(go.Scatter(x=tps, y=vdcf_emt, name="vdcf (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=vdcf, name="vdcf (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='vdcf', row=r, col=c) + +r, c = 8,1 +fig.add_trace(go.Scatter(x=tps, y=idcf_emt, name="idcf (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=idcf, name="idcf (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='idcf', row=r, col=c) + +r, c = 8,2 +fig.add_trace(go.Scatter(x=tps, y=iloadf_emt, name="iloadf (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=iloadf, name="iloadf (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='iloadf', row=r, col=c) + +r, c = 9,1 +fig.add_trace(go.Scatter(x=tps, y=x1_emt, name="x1 (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=x1, name="x1 (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='x1', row=r, col=c) + +r, c = 9,2 +fig.add_trace(go.Scatter(x=tps, y=x2_emt, name="x2 (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=x2, name="x2 (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='x2', row=r, col=c) + + +r, c = 10,1 +fig.add_trace(go.Scatter(x=tps, y=iL_emt, name="iL (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=iL, name="iL (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='iL', row=r, col=c) + +r, c = 10,2 +fig.add_trace(go.Scatter(x=tps, y=vdc_emt, name="vdc (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=vdc, name="vdc (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +vdc_ref = reconstruct_input_timeseries(inputs['gfmi_e_0']['v_dc_ref'], tps, sys.gfmi_e[0].emt_init.v_dc) +fig.add_trace(go.Scatter(x=tps, y=vdc_ref, name="vdc_ref", mode='lines', line=dict(color='green', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='vdc', row=r, col=c) + +r, c = 11,1 +fig.add_trace(go.Scatter(x=tps, y=i_load_emt, name="iload (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_load, name="iload (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +iload_ref = reconstruct_input_timeseries(inputs['gfmi_e_0']['i_load_ref'], tps, sys.gfmi_e[0].emt_init.i_load) +fig.add_trace(go.Scatter(x=tps, y=iload_ref, name="i_load_ref", mode='lines', line=dict(color='green', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='iload', row=r, col=c) + +r, c = 11, 2 +pload_emt = i_load_emt*vdc_emt +pload = i_load*vdc + +fig.add_trace(go.Scatter(x=tps, y=pload_emt, name="pload (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=pload, name="pload (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='pload (calculated)', row=r, col=c) + +r, c = 12, 1 + +pbattery_emt = iL_emt*sys.gfmi_e[0].v_s # needs fixing if we perturb v_s +pbattery = iL*sys.gfmi_e[0].v_s # needs fixing if we perturb v_s + +fig.add_trace(go.Scatter(x=tps, y=pbattery_emt, name="p_battery (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=pbattery, name="p_battery (ssm)", mode='lines', line=dict(color='blue', dash='dot'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p_battery (calculated)', row=r, col=c) + +r, c = 13, 1 + +# power comparisons +g = sys.gfmi_e[0] +v_vsc_d_emt = v_sh_d_emt + np.multiply(g.rf1_pu, i_vsc_d_emt) - np.multiply(g.xf1_pu, i_vsc_q_emt) +v_vsc_q_emt = v_sh_q_emt + np.multiply(g.rf1_pu, i_vsc_q_emt) + np.multiply(g.xf1_pu, i_vsc_d_emt) + +p_ac_emt = np.multiply(i_vsc_d_emt,v_vsc_d_emt) + np.multiply(i_vsc_q_emt,v_vsc_q_emt) +idc_emt = p_ac_emt/vdc_emt + +duty_cycle_emt = g.kp_i_L*(g.kp_v_dc*(g.v_dc_ref - vdcf_emt) + x1_emt - iLf_emt + g.Kff_idc*idcf_emt + g.Kff_iload*iloadf_emt) + x2_emt + +i_cap = -idc_emt - i_load + (1-duty_cycle_emt)*iL_emt + +p_capacitor_emt = vdc_emt*i_cap +p_bat_inductor = (sys.gfmi_e[0].v_s - (1-duty_cycle_emt)*vdc_emt)*iL_emt + +fig.add_trace(go.Scatter(x=tps, y=pbattery_emt, name="p_battery (emt)", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=np.multiply(pload_emt, -1.0), name="p_load (emt)", mode='lines', line=dict(color='blue', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=p_ac_emt, name="p_ac (emt)", mode='lines', line=dict(color='green', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p (calculated)', row=r, col=c) + +power_sum = pload_emt - np.multiply(p_ac_emt, -1.0) + p_capacitor_emt +r, c = 13,2 +fig.add_trace(go.Scatter(x=tps, y=p_capacitor_emt, name="p_capacitor", mode='lines', line=dict(color='pink', dash='dash'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p capacitor', row=r, col=c) + +r, c = 14,1 +fig.add_trace(go.Scatter(x=tps, y=p_bat_inductor, name="battery inductor power", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p inductor', row=r, col=c) + +r, c = 14, 1 + +# p_sh_emt = np.multiply(i_bus_d_emt,v_sh_d_emt) + np.multiply(i_bus_q_emt,v_sh_q_emt) +# #ptracking = -0.1 -0.1*np.sin(2*np.pi*0.1*tps) + +# fig.add_trace(go.Scatter(x=tps, y=p_pc_emt, name="p_pc", mode='lines', line=dict(color='red', dash='solid'), legendgroup=str(r)), row=r, col=c) +# #fig.add_trace(go.Scatter(x=tps, y=p_sh_emt, name="p_actual", mode='lines', line=dict(color='blue', dash='dash'), legendgroup=str(r)), row=r, col=c) +# #fig.add_trace(go.Scatter(x=tps, y=ptracking, name="p_ref", mode='lines', line=dict(color='green', dash='dot'), legendgroup=str(r)), row=r, col=c) + +# fig.update_xaxes(title_text='Time [s]', row=r, col=c) +# fig.update_yaxes(title_text='p (calculated)', row=r, col=c) + +fig.update_layout(height=1200*4, + width=800*2, + legend_tracegroupgap=400, + margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) +fig.show() +fig.write_html("examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_emt_comparison.html") +print('ok') diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_analysis.py b/examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_analysis.py new file mode 100644 index 0000000..c6442b3 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_analysis.py @@ -0,0 +1,129 @@ +""" + +Small signal analysis of GFMIE v INF source under variation in Pload, Pref, and Qref. + +""" + +# Import Python standard and third-party packages +from pathlib import Path +import numpy as np +import seaborn as sns +import matplotlib.pyplot as plt + +# Import sting package +from sting.system.core import System +from sting.system.operations import SystemModifier +from sting.modules.power_flow.core import ACPowerFlow +from sting.modules.small_signal_modeling.core import SmallSignalModel + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model + +inputs = {} +t_max = 2.0 + +# Load system from CSV files +sys = System.from_csv(case_directory=case_dir) + +# Define range of P and Q to vary over +n = 11 +Prange = np.linspace(100,0,n) +Qrange = np.linspace(-50,50,n) + +Sbase = 100 # MVA +vdc_ref = 1.05 # pu + +def run_sim(sys, P, Q, Sbase, vdc_ref, factor): + sys.gfmi_e[0].minimum_active_power_MW = -P*factor + sys.gfmi_e[0].maximum_active_power_MW = -P*factor + sys.gfmi_e[0].minimum_reactive_power_MVAR = Q + sys.gfmi_e[0].maximum_reactive_power_MVAR = Q + sys.gfmi_e[0].i_load_ref = P/Sbase/vdc_ref + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + sys_modifier.combine_shunts() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + return ssm + +# Case 1: Pref = Pload +results = np.zeros([n,n]) +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + ssm = run_sim(sys, P, Q, Sbase, vdc_ref, 1.0) + + max_eig = np.max(np.linalg.eigvals(ssm.model.A).real) + + results[i,j] = max_eig + j += 1 + i += 1 + +# Plot results +fig, axes = plt.subplots(nrows=1,ncols=3, figsize=(30,10)) + +# Colorbar - use the same range for each +vmin = -6 +vmax = 0 +# Plot small-signal stability heatmap +sns.heatmap(results, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1], vmin=vmin, vmax=vmax, cbar=False, cbar_kws = {'label': 'maximum eig. real part'}) +axes[1].set_xlabel("Q_sh (MVAR)") +axes[1].set_ylabel("P_load (MW)") +axes[1].set_title("Pref = Pload") + +## Case 2: Pref = 0.5*Pload +results = np.zeros([n,n]) +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + ssm = run_sim(sys, P, Q, Sbase, vdc_ref, 0.5) + + max_eig = np.max(np.linalg.eigvals(ssm.model.A).real) + + results[i,j] = max_eig + j += 1 + i += 1 + + +sns.heatmap(results, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0], vmin=vmin, vmax=vmax, cbar=False, cbar_kws = {'label': 'maximum eig. real part'}) +axes[0].set_xlabel("Q_sh (MVAR)") +axes[0].set_ylabel("P_load (MW)") +axes[0].set_title("Pref = 0.5*Pload") + +## Case 3: Pref = 1.5*Pload +results = np.zeros([n,n]) +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + ssm = run_sim(sys, P, Q, Sbase, vdc_ref, 1.5) + + max_eig = np.max(np.linalg.eigvals(ssm.model.A).real) + + results[i,j] = max_eig + j += 1 + i += 1 + +sns.heatmap(results, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5,ax=axes[2], vmin=vmin, vmax=vmax, cbar=True, cbar_kws = {'label': 'maximum eig. real part'}) +axes[2].set_xlabel("Q_sh (MVAR)") +axes[2].set_ylabel("P_load (MW)") +axes[2].set_title("Pref = 1.5*Pload") +fig.suptitle("Effect of Pref v Pload across P & Q space") + +plt.show() +plt.savefig(str(case_dir)+"/ssm_heatmaps.png") + +print('ok') + \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_heatmaps.png b/examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_heatmaps.png new file mode 100644 index 0000000000000000000000000000000000000000..1df24922a5e364cd399881744a18f535990efc52 GIT binary patch literal 55762 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 h;Q#L@+4bj= 0.5 else 0.0 + +def step2(t): + return 0.0 + +def step3(t): + return 0.1 if t > 1.0 else 0.0 + +inputs = {'infinite_sources_0': {'v_ref_d': step1}, + 'gfmi_c_0': {'p_ref': step2, + 'q_ref': step2, + 'v_ref': step2, + 'v_dc_ref': step2, + 'v_s': step2, + 'i_load_ref': step2}} + +t_max = 2.0 + +# Load system from CSV files +sys = System.from_csv(case_directory=case_dir) + +n = 3 +Prange = np.linspace(100,0,n) +Qrange = np.linspace(-50,50,n) + +Sbase = 100 # MVA +vdc_ref = 1.05 # pu + +tps = np.linspace(0, t_max, 500) +dt = tps[1] - tps[0] + +def run_sim(sys, P, Q, Sbase, vdc_ref, factor): + sys.gfmi_c[0].minimum_active_power_MW = -P*factor + sys.gfmi_c[0].maximum_active_power_MW = -P*factor + sys.gfmi_c[0].minimum_reactive_power_MVAR = Q + sys.gfmi_c[0].maximum_reactive_power_MVAR = Q + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + sys_modifier.combine_shunts() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + + emt_sc = SimulationEMT(system=sys) + emt_sc.sim(t_max, inputs) + return emt_sc + + +# Case 1: Pref = Pload +results_rocof = np.zeros([Prange.shape[0],Qrange.shape[0]]) +results_nadir = np.zeros([Prange.shape[0],Qrange.shape[0]]) + +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + emt_sc = run_sim(sys, P, Q, Sbase, vdc_ref, 1.0) + + # Measure some performance metrics - frequency ROCOF and nadir and steady state error ? + gfm = emt_sc.system.gfmi_c[0].variables_emt.x.value + w_pc = gfm[1,:] + # nadir + nadir = np.min(w_pc) + results_nadir[i,j] = nadir + # rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) + df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof + df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) + rocof = np.max(df_dt_ma) + + results_rocof[i,j] = rocof + j += 1 + i += 1 + +# Plot results +fig, axes = plt.subplots(nrows=2,ncols=3, figsize=(30,15)) + +sns.heatmap(results_nadir, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0,1], cbar=True, cbar_kws = {'label': 'nadir'}) +axes[0,1].set_xlabel("Q_sh") +axes[0,1].set_ylabel("P_load") +axes[0,1].set_title("Pref = Pload") + +sns.heatmap(results_rocof, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1,1], cbar=True, cbar_kws = {'label': 'rocof'}) +axes[1,1].set_xlabel("Q_sh") +axes[1,1].set_ylabel("P_load") +axes[1,1].set_title("Pref = Pload") + + +# Case 2: Pref < Pload (Pref=0.8*Pload) + +results_rocof = np.zeros([Prange.shape[0],Qrange.shape[0]]) +results_nadir = np.zeros([Prange.shape[0],Qrange.shape[0]]) + +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + emt_sc = run_sim(sys, P, Q, Sbase, vdc_ref, 0.8) + + # Measure some performance metrics - frequency ROCOF and nadir and steady state error ? + gfm = emt_sc.system.gfmi_c[0].variables_emt.x.value + w_pc = gfm[1,:] + # nadir + nadir = np.min(w_pc) + results_nadir[i,j] = nadir + # rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) + df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof + df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) + rocof = np.max(df_dt_ma) + + results_rocof[i,j] = rocof + j += 1 + i += 1 + +# Plot results + +sns.heatmap(results_nadir, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0,0], cbar=True, cbar_kws = {'label': 'nadir'}) +axes[0,0].set_xlabel("Q_sh") +axes[0,0].set_ylabel("P_load") +axes[0,0].set_title("Pref = 0.8*Pload") + +sns.heatmap(results_rocof, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1,0], cbar=True, cbar_kws = {'label': 'rocof'}) +axes[1,0].set_xlabel("Q_sh") +axes[1,0].set_ylabel("P_load") +axes[1,0].set_title("Pref = 0.8*Pload") + + +# Case 3: Pref > Pload (Pref=1.2*Pload) + +results_rocof = np.zeros([Prange.shape[0],Qrange.shape[0]]) +results_nadir = np.zeros([Prange.shape[0],Qrange.shape[0]]) + +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + emt_sc = run_sim(sys, P, Q, Sbase, vdc_ref, 1.2) + + # Measure some performance metrics - frequency ROCOF and nadir and steady state error ? + gfm = emt_sc.system.gfmi_c[0].variables_emt.x.value + w_pc = gfm[1,:] + # nadir + nadir = np.min(w_pc) + results_nadir[i,j] = nadir + # rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) + df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof + df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) + rocof = np.max(df_dt_ma) + + results_rocof[i,j] = rocof + j += 1 + i += 1 + +# Plot results + +sns.heatmap(results_nadir, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0,2], cbar=True, cbar_kws = {'label': 'nadir'}) +axes[0,2].set_xlabel("Q_sh") +axes[0,2].set_ylabel("P_load") +axes[0,2].set_title("Pref = 1.2*Pload") + +sns.heatmap(results_rocof, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1,2], cbar=True, cbar_kws = {'label': 'rocof'}) +axes[1,2].set_xlabel("Q_sh") +axes[1,2].set_ylabel("P_load") +axes[1,2].set_title("Pref = 1.2*Pload") + +plt.show() +plt.savefig(str(case_dir)+"/emt_heatmaps.png") + +print('ok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/emt_heatmaps.png b/examples/small_signal_and_emt/2-bus_src-gfm/emt_heatmaps.png new file mode 100644 index 0000000000000000000000000000000000000000..72c694a9167ed5587a5e97a70e301eaaeea8c24d GIT binary patch literal 93334 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


X}yq`~7H1j3(W~w|9&QBpY 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
9R3FU^LX6fTTunvGQbS=rrM3qJMw&7fL zSx>QmJ?!mJR4fKjXm5Lw$<&7#d`N5?a8;HA7B>7o2YkRSHm>sW@|5^u__6+qoy^SjKTeGtmbq%x&yMc}zA%r56W7UX8)D zUSNd<7D_JuQlIDp{XG!9%DBjk58kW5r7@R}P)+wHCrZX4rnmTEn$Z#%L}t&LVN17L zo~#;#J|T>&3XGo>m|50SzX)<0AX@*#dcC8nuk3-N(_}0@DFZGd#9O5;)d*nWgzsUu zifw?G$n)d=ql2IF8&BVMj`ufB(Kt(Ix;h^%TLJHN^0!TGbidvA0g!4qL$3P|`ew-j zDlPT3UXfQXnb-~YLDwKyT}+WboL6aDY&bATkwQYz^$9k%o0>spze-iE-Tjc1Owel$ zSEF1(X*r7wQJpA6n+jZORb}b@)eye+z9MTfSh%ZO2cyDvZ+7At;o&Y*OiPjJD^oLk zR#}E2WT5l_-t_up3)?uUp;cx8UYMK(YM0TS9?bf}Q7CUb1?3KlLIP2v^8+jYp zTi8PT@YqWq_lBr7;1nx;*119vXhOSnB><>|jnhryY9j^JS4^{hw8mYyn+#DH_h+0q zUe4sq-w2JedvU=H&tW;W%1+%OyUVq+6;U{hf&l%Rh7UE+IIj=9|Yb) z87}3+O=l2GSYW4wONCM4PaSh~l!`(V2yj7oMJfET@ysufoE}Z)VxU%}`Sx%g!r|25 zc0gDzJ(k`iI}Nak1nG}bTB7a}D8fkWX`oF}(rrjEXM59#hs4+kr6b^hCJ!y80#YS$ zx-LHS99vsOZIfcj4;fRZmqT%Ug1B7`0B<4bWPqsczhzE=s4Ky_&{;~UMF~s<57}hj zbQTa0kn>X9k;dnCe59#wQIP$x;jk4MH#!3!Ni;!tefwy zLHg4EP#RW$mLr%y`*X%2TW7nSBoI_M&wWGbU}M`suT6M03M#8`^@;c7#V+FYkPi|b zolx}&T;Gkr?haHSSZpKG6uhl%DQ-gdskbQGThA5;Qe(KxnBmJ8cElauk*kbFISw!A zp>`^Wo4%aBiu2S5IPe>Dg-gi`OBzHC3ZbTqM_`hGq$$sx;oj?89n^_8Q2ZQYDRQ2q z(1|HL^EQqSeceORN0wnYKxtt6Q0I^}#~HY-@1wZboKXa-D&&&iZ5NpEVS76Ay+L9K zt#*$kw_MWVG6~0o9wfXP!=b)k2dH4g_Eg^!tUT^N&P8kV^I-?hgJR*?x0U@+`a{|v zI5vtjhtysfk1ddksZjD(Y7!l5rf!)KiGOME%Cb5a;Ze7BSWFm2_EE z;t|?|ZtuTpIH_a=A=@b`Rw#P&2{FGo}Rt8pe^2m~4zNq8$0CTwv6qIs(?l5Pd-hlQw`6A^7!9 z34Tg{9C2m4M$Qo z9;G?ZIgI1U6kK^88w{sjlTm_;E_tH?rnw`y1(yy?jRXJhea2n^BJvJ9Nkoj82gL(! z=AXXch-FjK>E6(TxF_Q?3USw;`~hQ5ix!q+SB}Y3w1tW{b@;2iy00lrLz%L(d9o=( zu0U%Gr-uU-oqvjB4*z`|^Wu=RE(J@|T0v@94x9x_c8;u%PFVw{vCfvThpD_HlJEt3 z2-*?z6|rgRQ-^9KkgX$V2cC&Mt%8L(qgS9mu+a)RhBr^aKBQDJoYU`2E5t{E7qw%M zwSE>zoDGPB=U<_Q5-x#%oXoyZdF%$hN*>+Ua5Zf=l3B5C&oD7B+Mt_dL&I*iO?}Y9Q|Ky@*>l4vb5))t#(d1qQZJr-TtQKdCP<>pRBy zq66Oj=!ODumX9Gnp~@ygu#o~Kqmk1LXyE+qFQrjue+#d?a_=X^5r9lUZpW`OI8zPJ zNKQQ&^~KXCuR@r-XNc|voLcczRk$r^le;wp7MK}<96a7|L?a`1I+vO&-;j7Po|Hdv)lbRlN|v0p8PfJe?skMdY`kF~aO-AEZB}?LEy!S6?Ph*!`MF)dw^gXWfCJi?4> zO8LmkV)GmD4kfHXbZx(I1kY}iXiO}fy#&56eV**%9az1kp^|ukg%@lqzrUOfZCG+j z7K&i$qe)GeRVaHE5tL9W{BG*ys{~B9h<#*Z{o#EwJRnv40oE0~oB=6izbRm_#3s@I zJsdCeMr4NoejsD9zx+G*5_q%f`hUjfrhy-#ME@O_4UFX%jKP`7Uqe6h2?KQ;UH89$ z5iz#9vjrA`EsdY^(U!l&4R5y4ob10nb>`BUdabMvDg_1J#z*)b=Sk`?#dZ2~xcP4k zK8eKVi$<5xphF`lf~XKbVPethtw7}kGhZ*kZ&t*o z=w1)ZtKNq^_l|xN+hdh93AY#twQm^d3>?QX)g!}?yywizrAx2B);HDYBIX?YpzY?^N@gVD3>_f5+n z@tJ$LmiaC>Yx=v1kV7vc$X_5nX@ zAoCq`*bi!AF@B_cO5g}P2$7)L3(4r~2{6#tl}2wxDvn0Xbgtpw1v=M8q0q4tg)X@4Fbs#!79L^fnAa^#*%N7AJxlSfe%eNS(a9#h$5Y8;R z6#*^=`;NZ5EOQOy8OB_hTD}XbSCPpN`9fuE3LREs0#cfy+=b zCs&a8Pd-@G7rvId@vs>#z&A_}q?t{CH0w-B_v}Fufaqtp%l$rz0afYk5g-o?i#gFK zJ#sTcZuIq`t{`z!N9ha|*FVA8l`=fDV>`yY?%FI@iEQaC5#n=s`{j;_eK#LIbMFaP z&u^7gj5qDLhr>WUYB+mNfjEajpyr^KmGUgXqhr=Kub&(&0`kL=$QST}oO^rEps7bw zBz%TOb6n*zA_B6?+oOU&2heZDt%9k!veOG0z^=FixEj#lxahiU3o~Yl19I)IA8nRk zH!{bGK06%y18^XI30G(2}`8}zOGmQ03;!z4%y+oY`6OW#7&c8 z*VUo_PG}v5*F@?YIt^^3MdL z!+m)rMKzJuM?q&r=G^nxk*C*fJB~_2QAW?N=GG}NLh2P0P`6ReDJnqu+_SCaLPg8{ z6SC@SXlpS^>p)Ziuxz)Wmg(|VS@69K9XHn6bmr-;Jv4Xr@RsjAZ2{FhC z^h!c~6mD*i9m4TLr9yT3m4fL~mvY2}6lA7L-8TbN>bhF%lM4vt5@G8wBsSD-vpa~f z_%d6Hy@qJW;j`CV2OOE@au@^fK5FOFCg>W7PHpxjZbc0uw{G_LsEFl>q29f5BD2Nn zPXJV9X%D}eFQX)<;nSkEc+@U9IDh`hl}chygEmJlV{}H20x@%{I950=p+~}qIYVFu zkX2{{h-FtM#_A`Wk=C$5=%E9}OoMbPu)+bv^^i+>a&xk&;&ZxsweF=`zGAZg5aL#Gx!itoBr(NF5x20QWBQ!f_PWPy zv>U<5f_GQgl-Oku%Qzsszyj3Ekn?=0?&{!r8>&}KKd4EfTdN-U301D;g_PX1CCy~0M499h0`MPB^FEXrE zI`6h{7N4ep+T;)IbDwJj^q(%XTE9U!zQ?pRCYWet1(JQI2jzwdYY&3(r= zCTX_g!Wt`NUZvoLpG55h474jJ%p<4*V?y$PeCaHeFDiFTuDggt`&<*?P=%gNmiN^% z^=R8M^x!m_o8)1u8KB1nzvM}l7{;J*Ddv45BW2Bd(H~=8<>69Q6TTZ&UtEcQ+rpWb zcA)}{@bNMDfE41%sY_6zkW&4(Jewdfx~J4i1UCFgp|d+afbQa)b^!PB;mj^ZXbcgB zD6yH?l?;=x`MSfX?)GMi`-P;6K^a}-o`srG6k$lrS>(8c>Ee>`;t$YP@Vl1uyi<>LFj6j1;QBM8@{t}7BQ zB>Jt&@UX?D1%~`XtZ(W4=_t+;PTLWjiPKu(n2pege;&r5OTcu*bb_Dleu*JRVU|vBCvqgzBgsDOo~EBj<^CkQ@udAMmTh9ksz0k$ee~q! zRsmo4O|;ESkvfP=o`h!*F!j~F&U$-S``&glJVtj(H}C2(J%&iE!`qte)_RQZ(Y1_Kga4)1Fxdn5|z zwNeIB8%*k@NvVpXuOVOs!`%L9ZZ8q93EGEcleei1O4-ss)cVUvPKY?oaGiai?Q>@9 z05sGbQ7m6KyCXldwY*3;pF(E+vCr5>6tN)mNT%~)Pf0^R1-8s}xZpT?o2puxTRaEO zmD22M)IffcfX+f%TgxKd?JrO~=XLbtb)9oq->uN!ly>*mex8qtehB+1M+sw=Rl@0Q zam3))N+BpGF3dyja33gxUe5mo`qmt=139+z4Fzew&NNkB;n9{%zSO|$ls$SPU{oH)t6ib9kRwA3YSaoK#<$)7|>ec+0 zU)mSMxK)4{k|21VE5gvnpteTy=0>qRuK&Z;OtuY|FU1$5RwTt4=Xs>0|XORge@^~`#MMPoA1A5H!~A45EK~e-H+HZe@gQRQQ-Z%jF6BLh7g>G zR%~h^{hRcMv2@}HQJ6#}9R3=W*u}sXfBgqHF)+sYe?#J@KDg|AuCj0b#@m|2uSEV5YvNVY`kUmTkdmpP)qi^pQy<9v z@BW4w3*o*%t1{2ldG4s<7XJ0*ZmwFJCD6 zPL>$Ll<-9U_;+7LOzo0KS-A8Mr_SKtYNX>3$1p;F`Fh{({J2yX!P()*3LCy17%v1P zE<(NUEZcL@z68Y{DL_e_4!y&Nmj_uI%e-=&)$r2_)BW6Do}M{4!oR{${TKw#P4gPv zU0O-rTk@=B#kznO?n>>XWui(V7GYT-ms)9hP{o+{P;?48!#7Yf47u17O2{FBNmXkS zY!mFk?g*RR4SSz!M1&@xtIXgp3rj@J9(L^|Ge?Y{u5AU>kCF2)N#9DG?K!)}(N;Zc zP}b+C$4|xP>D*;Z73w>59K;SM!_VfCmxZw3&avuZ8^4{6c8a$$V~kV-U^4l-~`G(HOC=`X17hG#}Fj%&aQ zTz^&p6crRlQ|3*FxA6KLE@9i1re*-Fqhu|S0N}p&OMviKbh<}Cl*|^+WS$c>kR16&AO}8o34OX>o)=Fe=tJ_u?Yxt&*bPUz_>zWc= z?eN&xjpX=>Gvzi$%O8O1%x5W@i9NB0{D#lDj*)bq0`Rue(0YXz&kM>{2}v z2x|?WFyP_rIeKT|Cn9oE94h^Vv;djP9de@B! zg$I%vKE(eX+jMG0N1K&XY~LSmIXaLzka4d`t?sao%Td{6L5*`61qLhL*JO%{PZ*wwbaF6%MOo-7yj8wC5Vd5pe(z;x*PV*Mx z9J|8wX|nA(!Y!m6nH2HLJsf&=zF6C|K+V^Q3fPKd+8i8apg*jAwRN z{^d(#$dT}9>|Xpc`Sf2LGHq<#V%_Y_Or>c%Hc3zM>O;5Ha5aI1sOZ&AfO~5fakH$2 zYTPCVYCHm`ls$z61O>@nER?+1Gt5O{E;OmuWypAGDAmJj%TK^bwZj5bRzCx0MT$-t zdu4hYM@}r-Um+JchV}@MkldU-txc5UO7Zz_+N+$sLl!FbqmA6*KDeUU&(Zdo_y^3{0&|M5NAT;{AP6bWD|JuToR27o_R-Aeq&ZBb7Ld|; z$T`OU^yAMob=_tTklAi~L!jl2EaZ$gQXT~6){?b>_Z%9o@Eykny}5-ZfXO}AsB^eG z#fPCE=4;~mU_+m{f57Q!Vd?+m=nSxSHA61~{!y^;Q>-OUuOsGiWEn$z(U4Zk20RDK zC__rb;SO;OdO#)e%x&!(!5SvImo6}y>T~~Op3|x60_;}$c2vPxQG%sFeEim*oOl>A z%LWts<3KB(qh7IOj@PY_xvnGf?B#xRXWkIcu5=jfn&f?1YEFrY7+=g*_cuI+VO+I~ zEylf=SQuknrF8oa2-Q}sRBe0;-VPcG& zUnpljET96}M}O54hf+(dKce7NCFY|5bbcL06cb8y8&qZe5o3Jrl~`ZwVVuuFqT1yA z@HL?`QN!t$g;&Lvi)?~ya5y;A zaF8KRgNUIEADIKW1pj8D_|5rp-^wo?h6;6VV<19F*@G`DGCdPxLW0Kt8LhXeL9C_V z;FTHOll=re8-PE-&RA{FkWfv;=;5sbmJ{_r+R0*tn0-1+WzaGVQUW}j3gJZw7B$h@ zbIEv?Gm|eGbI?i%+2Wik&LYD_q;0*xNwd_u|WVq0i*0$LNHETVkHu z-fR`0HiE@)R)aBuWjFr053EN2An4wV+}s`_)G&D1KI@Rn9ViBRrj?dK-jKIOLBIcZ zMTFa$UObBbC`}ju!t?Q&{}r@$PB2Nj>Fk4ch;mbik(&$b&yzDI2>NLs#&^Q8kx)Gy7r8l!=ho(3pn>2cfPDPC~eO{i474Ux$hp_}EP8vTM#bj&|I?{<7 zuM9d;@>8UT|8mIgPRP5l74?IANBfhr6w{M~tjaadH2a(&SJc9s(YO0#oM^y0omt%g z*}0k|Mwf<%r|J0gxbc;@gHCv)a2b#+!>B4wAKWB9&q<5a8jOt12Wm0AAV(9jpz0!9 zA@bHEbeU`-MTw$By5iRJxWo2=I&Kzkp%fVO^bG{}bw33y4K`3=g6h8kDMW)}AQ*;? zhJ!P&Cf!r0))-?3FU#*bCsEDKcSkOCGdj@GK6u!ZxvVa(^=Tz&VUit0|JRr?#VD2k zs9I~<#=%hIhKH;2)y5h8LDL5^zFTncd^oR|roQ&1nS2B8;p-%GZvTM(^kU48vSKL@ z7O!#?UPo848>j10W9-No56C9KnkZuTKFdB{J%@H%R(NtR+D;eY)vE}#?XC*Dy* zf}L&ngU!FGJ82&EHP}-L8||S2iLEwG&xT=YBY=E`zE+C6h z_m&#aJY&#H%jd{g%xA2{as7RKT;I%<|8L0THyOk2@C1}E7m9GJ_`#QON$`8nsQ*Wl z-TY6QUQ%IuPcgKI#L*LesKNwE7fyM9e7%S`#OvPmg8-}YQ!;SG(#S=Vc?a`Mm({a> zMKj%GH=i{=vcKq6r!=F-8|+-W`Eu46|L(d9%*klYRiA{j$8&J_$PH86sTPuf+dH?0(NBr{DdvZ}P{V{eHJ(dG%rrCOyxxcvMQfMQ9+0st4THdt+uE;w^#hU7ncu#dlZ}U$ zv~?UTzn?L84QnJh)FAwAkLX)m>b|yAv|--FA->M%PNRN)`S1Od$ZCOxgSb~#X5qGl zO}pZ%QrBYemMSIB|Kz(fY&A(>Wi?>)%nEYnND$FVh%inC(KbGbFT0(Bu1H!zEcR@g z$SyYMby&AG$#&`ux0VE!~n%r{;RFhME!oJ*E3etL!i9L=ck>kidm1E z^Tjx}(?0`w)3|kr*HGghEDx(WLnKdwU!LH=&t%oa__C865Nvd}3Y9bET7Kuq?o(Kg86IF8}b&lEy~!EoDKHGMai_Bkt0%hn(ShTT|FQbJcK z#Zz2`e$4j!QH;FlSY}_|wne@!9=DY8ydQ3*rTxe=p+uovH@nSDzq;b)kmb;IRp$w4 zVuf*I31~G;;27%s3wrI2+BZmZXeVRB)fKq(n`*(umT+d-Yu>j3!@4!JS-V#4C~Bp z%J7q{tFaN)%wiAjE_w0N#;auus*eQzjFgnE8Syy?nob(3CoYl%r^qq?K7YssLzgUn zSTQ4S-=_KSn!Tk04#b1=@SRnlf2LJLJ3T19?=>`S%gC&W=*nTrL0=ORME+m3fgG~D z*`0n-pcp{)zNYq2pdL93ls+C*To>?z#K6kRuSx2Y>E%99SDv}Vy-!D9QvFiMJ4yDv zz?yH!6;pm5=7sHMxoDamDo!+;9_h?jAiLVtkY5i!lx*M(M|j8aThc#cL>nbvD`>Nh zl_!c%ads4oSjcA7D{FL}eeWIan^JWs{f<-mGqi`HT)SK(yYo)@3b6d-rOzcC?C!KQ z-;_v}(l932hi$~QfpzM0pHEj!;8VX7>Hb{S9zy`fsM|ojHojS{ zTban=l_&1kc{*4~L=POBLyNym9G`DL<)OKqh`56E1+wHb7D1O05z92b zFe|B2JyuaqKJ_TE?w~GUH^SaNY>zu3npNI>f#=ph)>*pX@oP4YbF%k?t-e-L2V7FF z0p185`7ufr!_D5Y6t<1%`A**J5(CE+%FVV*L+{7YdDOfB))rq;PdIGWmo(~^j{7cj zPluoM2^{mH7R?HsRxER095&=VjHP@^5QokFuv=eXDL=AypTSZkWn?qng}wnyMSN(L z^`JmKL99tVYrGWX9vKz5sKSAmnM50)D)51)o?xy0 zGmh9iNw5}t=#$Pw|x*M?35(n8IQdQ5A-d=R-c<)yj}rK8448K@LbZ4#h+~ zv3Q|zSfA((PocXI!)7idU8DVZbwA3#_JQ6dlsRS56YE27>!X=w$v!mU=Qp}T3METi zE}W}7OD04Jk@s-`tl@(ibW1!Qjy_DQ;sqy$ka&?VUiQhR`9n%rtKIoxWgPU+lWVp8 z@b-nQp-?PG6bqwkNdXD{1Dx#`;GgXBp!6unsl^#frqDU4ua^O_b|KGkAZCY?$Y;Kt zlZ^g7@26uhTabgRVhg!f!;n+==ESv)|I&W$gGUaTAV0^k(EEz#Xteu4%ihZ?&G!Qo zk|GNnvpNdG<6r7j;ky1BUV}fZMdRGGj+vq{?ueI^@U3KjxPSwp z5rWIBm^SYrSRIE%7cOE+PZ6DCwQ49V2UVGgD&uQWX-&=l+~2Sz86#2xiUA6)fSIT@ zb&r&0L0MmHOsp3x6zpOMdvxARQDaGL( zf_zTcjFCqcc{FNPyY1`+UsRO(UBpHB)#g${0YRJBR&Luw{Vr+wS3(c2sY}cCBwBIe z#71M=Shjfxs3lbw0|F`g{DaSwl6La}ea1cfBJar6Z(6qCp@=`me0?7=Wg7;V+*#B)uggliwXvs#mSSXQT^I@qTy3 zY#b$PyyEu@%SGSdNsS0Q$_*cF39=5X3_16GqpG?(2eCRE$zTHQqc0(u~Nw8 z$8piPqe)lwUM#z-4wz>{b^W+CO%SR_JYwsI>aPSVvIKbUZ6h%tHMwxt3N>!%1i)|HDR8=$Qg!{r5 zrWYOpKU_{-CHltgm^h^?%D`+-Jt^&G=029A@hHK0jEmX@1$L^x}-+foUCTm9YpyH;ZrSYGnqL-#Bx zS4xaTaYHCWZ{E0GO2+UQI=%G4d}aHurajJx5Zyb-<9rP|6mJ(B;>fKT4%smlMm%F; z##J?<9)3b^Fua~`Pj|jmpw4)=7devTkiJNM$OpdWYp515ZXg*24baDEUuFO9NA8eTqB|$dDs6qn$hI%j zpW_@~8L;E53+Ab?TgO|!vy&d65C70_W^0BVO<<~4>hd!cqrE8iwVEMM+I)2Ni5rv@ zMST!3Vs@V|nak*~hw?3?9V%+{f+#J4U<-B^P*IvXKOt7NQw5rkTJav9YPaaV{5f`u zp!p!)^^x3dZ-akqp!xOn^QsCcWL`vCCuVMkC-?{?$!gX!PdA#ZaCdj0<_Hes5*f}- zXiUB>Uu}okUMv(wp~=O@RGpz6B|WJ}yNJN&%BJUi{7=`OYt0RrcR^#nl0C15{M_df zaFN&0Y6|pUIUZN76b)NOirlzo`cuBidiTcISg|~i0ajvj9qC1!4UJKA0TPu|+tgv^ zMULiX7<-PD7Z6F9yh|BRBUpASc+LmkC@Qni0!rlgOK`E7}9p9Xo2 z*1!bj`wJV~>fcaT3yfr(^9}3WL{<8djdxovIE-=&&v3f9icSq90ryLg`e;2s|uRUDw13G`gVZb^=kKy zi#O}ETdck3N)zmUuljdSI=u|C1taes_dSb1Lp>k35J4c9>j&5vFJElOVRk*O?fIk6 zP~O^VsB+2~DQ36#C0d=aF~<<*#6&a(Fe4HfK}EA$A|u9-+4L;>v(Os1jw6YwDH+0% zcgLD5261Sn)t+Z7WfB-n0;0klbo8KZvh*`jV5g~_vh^@m^Yv;De*;akp`R`}G**pU z3Wb9Wp&15MB2C(@SK00N4Ozn$l6iO_`3xB*#;<89O3qf`_6S?J6EeSLR8jZ;V3a1+ zgk>6EU6b`js`E+^V7IC{R*~;)EC#Nr|1(3%ETivGQih-9-F!0DzHCvlonYj)T?Im+ z3+I*k5-{GDe(0p(_0H=0Hj!K|j<5w9U()*P#2l;AR(@{fv)f zO{6zxtP{8s(igUkeU5Dx*5;A9->=`qbFPBwn?Vq}C25RrWufp!$nMNi7VG5Knjv4X zqto)6)74Boqn{g}KWcCy`gxnNuzZEOYUwS@!}ll-jgGht#UX1(gt8v^*)NFU^KGh?}bEr4` zIgY&`2N3SM$sv9mAWm^7E`cG$YzR$cArPiYb2+Z*EuAn*iL@$bmSQ6j>+g2~aWNAM zba@V+j)3j0IM)jTCrqmW+73qN%Ez1s3(ic;9tE*M?tY`%8$j#cDC0cUim8Sn%P4MQ zEit};aQw&3FgEif+P6Kl`a^V}*29ns)pVm#b7-G^w|mgjXI3xmaZkJ>W)o!9AK_{EWyizOf)>H_d?nt{$~`H?aHr%P0}`)P0$vR*)mc7X=OOE~+F!AXn#l zogw-AsX;?ePsDXuj^i3gJ-HzDWKS%K6*6)u*l&m%9VLl-n$s=WZWQZs-{M9;A5;$c zlyX!kikmrVTOH1DYbPQR@~Rg$~35h5XhU$tgv+#izO~YGXUe&Rt?o9cy~+b-9l5ASbnbMNpfh} zr5I}YMJBpMx?s>-er*6kmsSOex6|W#eD(!yyLe%qPTtz2uG)}}H|#C0+6R(bY92c5 zQ(fXg{d+6#V5G90;5cNfxrp6rZ|nDDTi>9A*t)Q|EOKo?Zc*|B1+EIU%N}Lfx(6b) zf48Y0sng<88V3X~MN+i(= zZMxi7Cq|1W?pZzLk#aZ?c_0skQPQu}Fx`xDvv_VGP;JcK#XYQ2e(AKuK<@ooTpLvZ zw^R>RcDra0&jsh4cn8j!dumPPt>5B>dnT$%Jhqh%(!WC)>s>G~&Bf~ZlGr2G&$b+R zBWz`ci?uqmf6r1w6K#VVz#=Gxp%KW;vrOakP)qwH$-3s99zvlNq;{Cmi88xD%_48j z{MDPeAfQnv>o!7H%U*gpjkcO2N8ZGu)}W^^uxzEk;>Tuo$C8uq`G(pF=9OwrhQ^+(o+d%rmWgGG^2f%W6nm8Za&!Ir~W%W>RXC*d_r?7gj4E4%@^b}~~lLGNC9fl>R9jWL#7XTNyWurv1#;$q&2}KHg(W*{Kligx z?Q#bU5=?&6J{YFGot(ovw~N$kwFf=wE+;kE1479)%D{hHqwDqY0I{4*WTn=h@N0LO3OtB^-;!n{L4jQ3%=nIGqy3Q)cEB5pu7l&1 zm5ou3k6r4yc#nW2>0@p#16P!7ZGc|K$zM`>|B@x3X?0E~#z#JRteUJmh- z;ZQ1R=T-Y=2nBrORh`k}if3JD@>t~5eAd8u{_n6&bfMO^YCG6hhb-&~aW~*@3UgWI zx8r)qW>ZbqAn7B4C<5;{jZ*ZC4<+mEFcst^o;3h-33*B&xaqw>spUqn&V-MfwML8E%s_jxxD7)H5tY3tmPt57f5>_Hdk=0+fv zMdP&xh2?b?C}rId3N;;L+Yx9AiL{S?bQ6WA9oo2%CEG(}@!%ODk6Y9SrSuCtfco*p zEnHUL6F8uEluvcWc|n89BkGy!RZlz;N}t8KCS!b`boY(6U60i_K1Vsx_UjY%Pcp6M zxWS!#^~hO^+#9_!8nMl|$TdEx6HVREv=OR$CNOQFDDB$To&v9hl zJ2`D`Ll5n+$ri*1|5VlfcB=P0$fC49jij`<`vm17Z__2? zJ!&eq1GYwKxJ&UzNoc84-N21E?QH4~Qp~9kfGc?HP=E z4Q9V(KO~cmk%kUR%D}Bt+#G5oM24zc24MV9oVm)suGO=XPcG|@rBX?!u+?d9$kEGF zrD~m%E0=x2a%>=Y@;6!4%U8fy(VJZv`QsjtrQ&C{slRnKy{c&(sk=8#|1~PO_~;)a zH%cu&9-_1qW`&jM(x~Dl?E8P$Z&67}4VIFcAb#3?ZG|miWwY1SzT9NBRe{@^207X` z!w^RngRJLK#rsJp9A}-@f8C3In_TP713v=?~r6 z6UKj9S#@oQx@};z&{(%u*0rdps13ymzT%Mv&(K7#V3Ocq+Fnq1SkPKI>vEYaC2J}w2ySvLl~oKEeflrtpTy526o!tJ8uwA{_7*xEVoVWr{($F(s^3R3LL=NXerVsy zGrK`@izy$6Pgz+iS#fT*C}}mxm_S(`T``Qahj&#e4TMypeFclldJrNQI_a_}ehd9i zZpxWPN}7*_aRxCbs6>MHRqf3vjwW&3FPs1`oj|e3#gP!-4&{H8PY)J*#Y-H0Hy1xx zxNK8Md2A-ymK6nkeNlWWOeC0K;m(M$thoxC5p&6+*I?G(1*Dt?OHQW9%%l)bV5Hhc2Xw8f9(=!R!_hQxCCRQQ4xjdT z3dQGQ)D+#b&Cs@RNUmitGh+xPwF@POv<_w_61+-0@Igq$SCTOfh%P(Q2TkcfXTs}P zDyJC!m5NVyDsL5yZOG{-F*G)fj;2`5%E>U}ephcwT3Xt(Hz8d|eHg3g)-rq8Ka?$( z!Tg472Cq|hn~Ch#90`cQQeYQwwkrh4iQ?X{NUQC%p!nA+dYp)x@90!EwfW4qfZnq|_DCh|YBoz$mPlz+N?NBVCCUt~ zw9u{+C23b_-`?ZApGpS9`~Cgi&-=cg&+qwTETiYSpZmV9^E%JtJdWcOoLE*gvI!0M z^?=@qyc32o33;%J{S42FrYhM6(l8U;@i?fWfJXP0CCrXexFrJ|2vwU0{^ zlOlC9dz_)4^eA?v!JayN9E)vs?oJ?8hL9iZ7QOVSGAadXlOWFRP6G|O#?u}XB6U+5 z^Fj*Z!z%$2o+O(*DnwfMQ9O~Sc*62s%#e>++o-onj(g;|Mg~*hgH9th?zwsLI^8EQ z&W?D-n2>V7Wf`CzqNb85I%9>3VwVrLCYDUD?Tk$R89BnNFgC;jaAv5<6nb!iG%^$4XJ9?APp{h2(&k1?RT`^7W9{?u@}7W`OLQut%NHjC zw+y}6r9(eewu1hh5WBz>U~>KNOyr#AM+TN$6M6mYO(tTONzb-E(}a_>$&W`_ff zKXrV`&Yu90D|sC7uqU~#?_E~|P+n|rrCtvyV<~(7Gy;Ut{T1i(INrfnT z^4WV#=h5sni*y??p+?4QqTngfJAH9w;%T+FWIE<;YnIF|nN`sG+x}%ExN`^M?d#AT z+nudn3R@j_#6$eO*^2*SSHFvlWV=w?a^KZqyKl}vekWYvclX|&533 zq7lm)X5+pLi)Fs{&s&JyKKOFn$mj|MckZKKeb2lP&1adn82opxoIw|#4C0^pm!0a7 zI1E`z_S9Mbr_j7p;VVDeiqSwXXI$bcX>J4i10tl-WDZ?)c_s5}I%CZG4`+v?YnHKK z8y(X%G}DunPD3cKGCw=`wSDJv2=?0PRgU+t;~ly_e=IQ9qU3JM{Ho39#+i5g`7`fW zOTpKb@$eI%5m%!>R2Q7*i~+g0`1XxL%%x`q$x#6P_+JKDit0Q*2@BEnG+>fE1~8zf zKGTk9{2-V{H1^lUy%uY(N)p0DE zb8HzqW*;2YmQwdrrw@uLD7R;773X$Dqf@VJ)8C{Re) z9i0qEzT){2y-0l@l2DTDEO33|X@(=krVnerJ49V@EV2mny@zs(cuKobdedQq=)N!F z9^%w)SSyn^uDI5XEj=`43Gx6J%u3Xa7@a$&NqQPaf;4<+qr@&+M6b4QuQ&JGA zF6<`52alP1&Q>6%JtVj-mz#&CUBC<`0Xk^GLt_O#d}L^{3e$L8NHd(!Gfcwr*^jQm zp6%AlL2P*geF1T9ZSJCDiW0L=sig$~#tcR2rgbEGW=x3COq&V#BD9qoGi+VZsIs_X z!a9mUxwvr`? zpjl!OGSSvWR}DA||+-&Gvw5rtcD&KAzGqCmtD z&RDK|xb5q2A=_lU2w`E<1k6|tq{H)0U97nx3*;gsS>8M<0zb7PHrq?r> zT;qUnw@8g-1-Vct)eHRKiwVwVCW=KeYa*iva+!H?s7sem9D+yXG1Y=yM~6Blj^Qr>%lv$h&!a-?EGAS>K0Ce$IabI1Wq67|y3sghwgjDLEZ=uXIR$>A1dJJ#03rpK z&0-d-auJHAsq}VAv)#uyZpJAo;6M)w3*Pk)YSO!qSQKfja$Nx4r*87Fi=OI2pBaOR zgN3msT1a?UO5Qje*NV^wk3O&O%RGXSAS+LW#X5Ujv3-9p)r%|#>*0^Ki7ssfRw5bg zYxgfivGD|HZk;Ap%3@re*l6aFrixLGfkz6;z0z!EQDWzJluu8bLE)RYg=lU081;a| zf7(`-AJ6PGfnB7-qD9GCupSOKtaOI>#tI10j-1ZQ_SC>+6gp3dJ6C}m)fqLI?%pmd z8kd$J24vSdwbDc9R~;lHEIshM#RT24zD72aGL{P-Yj!BpzB!_gJ?>C$dkf`{R0YQQD zDx<9dpko%bZ!Ij((5N7g`?fRh#tQJ(os2KR5*SmUM@!>Kv~b%M$F`oYCdXL7i z1SV0cL&LDvRgcQ)?~3S$QGv?C#$)aALE+(<$sxhP`{7yk!@vOusZd=8GQEx1UlmqlVKBWXnt4@m|rrbU;I@qzYd`(`bF`fEgktcKa!B3Fw7 z7GQ}jcOS!j5tWD12&-hkDUKLX%KpI)I!T70B&62}mPVKzdYT!IC7?i*#Db;c7_$MN zdWXCj2lpQHrTl0jF?4iM+e~cECsTP1oz1fJXIPQ@_F0(gZN>Lu&%2=SyPYz0C!*G~ zmwpRqxAitE-z37D>l+lipCY$+*f9@R$Esv9P=}|St9tpZ=1H163K9Vynd4Fw#G{FX zgIGUgslN*0sK*(5YTkA1sfEm*`Y=q=o_fc$vA~F-a$Eh#llADFo;H&9$&$ymv_;$9 z35h^qBia}O5+6m+C0vo$>!B*yf&*Sx>qczvDE;KE8h)ymU_2+A46DnI1bYnP6<(2a}`!4a)_bi`%$Lvcb@F-f3`7`|5CwJYx#hEIeubz~`V3Qb@FoePU-lm*#B(OnJru;N?AWs*{@0Aip|!Ok&>eHPkAN7Ne<|5f z8`ygjD4#13AXRUPu?`TB42{d`hkb!cWRb@Is7M3sBKv-y}tt@qqt7@Oa&Uw%FZjz+( z+LMPUI>pz|l`X~Us(CuyC}+CK?OXTacfc@UG0~%ekB5tU^Pw!r>+}>$xY_jET$2fs-4aog1HVyWxI95svkknNk>jc=Mn< z#$S3G6?@~%T_og+wUfSXinfRHBe*E94e9=A<`hMES@>YJN!!PbLj~!;vX$`WiXpyFXGqt-mmPFxb${c8k z$wvfO1^c7@Xk#!30BREzrNO7($gv_B+_y|xg3VICJOjBL4LUn@YRaMwSioeaLHi#B z@U*rF@Lr*{+auZ!j&@qZLUS5V@(*ZojcqBc2*ia9SFHV~-0i zMgb!`geXWYflA<*TKHsPGQZj?F43h5vLi%hnqiYM83zcSJiB|%3>L|ZCbZlnXczO^8IBz`vV=$ zf~)4JA@OpW>t?YZP+>DOwd9ik(%^-e{lkTQcrmtL39j#ubrbqpjQgJ-pM&GdPc%Cl zN+3{^JpSFx^BF{=#0EWhMFKnW6sV$-ZBTc-fv)g7m}-EfS(Ms1(zls$^Yb;0|5Ypr zX$&)jS*%ywL*uT3C?>a2&kViy#Ymv1VM?4MS<*vjTjSYJ(>-WnFAb=m6Ir4Pe6%xY z39ZDzTSktP`-w0NwZU?Ph^oYYIgGp|{3?xZ8|M{pXjlT+W(mD6Bm6=}N%I7YbdJ?L z_Kvw0-kSzLe?yr*2;N=IO*JvZm;5fBnq^rYqXq({ZTJU!XWM06_$d1v+-rbSH0Gc_ zu!TKOIspe8B-6-S$T{}IvLp7-{1))|4)izRbenK=tHbLc0vU#0e}oL{P=?+6xI?*G z!B*JqG+FS=c0F;~fLTyHuP(DPE`EALVRtKWoXLya#MGW)#+QQ0}>Gr5#>}4_v&{ z9G^!k(>{2TaSAx?uMkjx>hcerC2o(jUBK~GYSh$}^~rK9CNH66qU?3tA1nJpZnVP~ zPN?DB3Z*ABqnRc+fMvV^5^u6z|KOB=0!i^@9LsH%xYJE1frsMjRc^J9S88DHfRg(WRXWuei zYv%Fk74c(jGr?%_Z2v23e`#euLtZhE>#Pbe*D> z3Q=~=2{t}$-_u+Jpp?7h&~?on+`1S*6qRS<@+$yJy~D%E;wFmt9U``bGvpirhcED2(3?awX28(*9Y=vr5m5#|L}1)t z20AtF?iA#%u8h6%#|^__C%Y=c1DMJT75~SlQiT%}&xL*OeSg^XGyUQH%Wy@$On>g5 zS{^u;mVNEH#Kp8zMl+MB{&Cqdi)>iy^)t;}rm@3mVEp4H?z6y1;^95<&wOWQ*Ze1I zo4Js|2RN8;acyEgmI1Zc3`^?oAIk|4MuaTY;7FcHEJ!_&;b@*RVTcbpiLwd@eccup z5YsNNG=9FqjG8I1r!YV*BSM-sYP^O5kJh0otVs`NcUb)v&8{82&v8N7p^X3T;#cIq zYs#G14hzNp@py(9FPWz9K^Z~V=`i31t-!cdjHIXo^0p+%2xrpBJ(ceh1E}=EWC`_~ zcCaTkc-M|)%e>wEDEQM@2Ldr z5i}5iWGKWEB10IIDcm@~%jJHxdUUC%0=66lR9IE^%s00!la|BLHoBSm2sfM1-I9mA zPYYuQi)Ym$TOrq$^*E);2(}4-?kpudA1|dHoP!5xaNyU_l6W&{(f|$DGC?V=>-C(P zVvI)Q0??z(D=LBH9y!Nz!Q( ztFqih7r_b5J3~=3*6V|1epEG-i1pC_YY64o0uOc}2HvxTcLB@t40~}$v|qV*0RY*| zv47K>3r!NyVjp7Il%e^LicG>%W%jh=Qp*Ks?{gn3rfmJ4A5Pz);#E*uoWcON!|{ez zM0mE}xd>ft2QP8#k2+x$vid`}ni(?>q9~D&C9AH-Z-LTj3AOmpfpMWyxfnyZc8M-3 z-BW{{CByN>^rdQ3VR?K&9XRKn)jgMpi?=MX$26lY%-HVHGD;nMM>5SA{6^%JS%B5P zopVx8{xHuEL!-#KlqQ{%^)0cq>-o{OpdnMFZ<0xP@yoaSA=5G;g)q#PCsRv|yg7(i zoIlG9J4XaVF=?tzIf`Z%a^*qBbb;%*k?^!Q%AttV4ybgc(6pJbl3dXb_S%xC?zp6M zk({j{4ho5tdHzI8g@7k8JA`N!*umFj1F))g+yZylaI^)KqK%}RwJo77Izrn@`_V70 zkStnW(W(d0w)kE**<(m<7N-K3$!r`Jx{iNB=@roesN}A_<~{x?X}jtf$K6whZ(^w0 zLLmjk(e*b(%c-44rhFu=)yX6&DcA99`_B?=eF(y3;6E)Y8o?$d74ZD%@(^P74t)+Wo9`h|j>Q3aqrB#b=RCK@YtPWSX!S4H&zvssl z&(UCd13Aqlo0y|et!+T|93?GAUikP^Pc!WZK~80trWpQgWns!+aNhC(rVnlHbZA^<}rj zeSo@T6@U3L)5f^BEZHP7T)nQ=UEyuk;;)I!yQNpv~xpFK=ub&`k2r=-03- z6*Yl#_#ul@Vzaav*Sw;A$JXXJmLn^R7GSa#LE)ThhZ?`CNa^_HTj7($RDqi$KO^Oa zSuX=f5;kS_7?3lW6;@z*o+bhjmA zSQE)8)ctj{yv5BFFjM<72L7qQmY}mL1(w^>NC7_!`c-j_=df$(^Vr`-GI=Wdd8gnr z9nzWp0rT||#jRD59eBBw0j}5NoN0?EN z{d0aKUPG@zj^Ym40>8p7`0JpROoN(8bHD$GDb(O8^)Za{X&CB%yfn{6)D}QyDy5HdU0kl8j8VzBo_G(`yGJ@kA(9sXdZ=|u%UuFtqA6(Ml({|ouWr+36c{oKzSF$9?1w6 zYyG9H`jS!$-#97>(!pl$r{1mWbnJsQ(Mvl1#mZ2s$vs8_LQ(JJ9gGiyBleCIL~Xn4 zw6pKi2YZaN3S3U)0_Blbqkl|6Mwa`42!DGA6j8$+Hx%OSXi@Fe5={9 zhW0WI1$9FIMT@!<9hl(u#uJnnk>^kgCR6XI1L`1N;QsihWs^>KF2Wjjc+K{{L9Kt* zMfScCg(A4Ut#1J*$fAq+Ls@60MLF6-kCUHP5T+COf)^5>bT^X%=N-)f%j^~_z^E$p z51E5VcZxbsrl#~}AL*8cS6mLf;1en--ZTk45(UupIzj&oO`v!K9yOhJfJ+t3cn*Y44%91#Qm^mnB{t zPpF_4Z)A)y=}Q?Pi`Wng5c|x2LB{&V>vbi|fijzl!Ipz%(;cN^AG5?#}0r zAsN$gaMg)0O5T$6!LGk4zwjoXJvuh)sz@PCk8PTjb{!FZ9A0nmzA#(nBKf-9P8$qfB_I3Xn-?~6UqPBg zF3qj+h;!G~6V_ia$_P98f+A>NgA5Y1sHMHq2Dw2-{FOA~Prz>Zwg=~R1?AVi;(Y1m zEYXXmUK_Al&~J9IX2wWDn@y8%=Bpvm1|=Y+9O|>vguj^}O=-v#!9$DS7)kW%YkHta z37`_73F5S5^hbFk68o%Uuq!l*3fE89P_CWmVp{&Tz<75>9p?Vz?lONNF6)^7U?} zJtw}1PMi|h$}~lQBve%Pr@j=X#8m+7`w4>BM1i*E!qF6fsxRnsUy|M_!96!?emGX0 ztG)M)EOU=xwF0rls9HE$B@4N5(R87lK}z&^Sse{5{e#irTHiTXoP%H*OJOoSDu1l8 zCUVR}9_D5~p{OVvJ{w&H{vgDfkm9RUIEVkQO|m{NC}S8QFIHA$88E5^=BFQnJ8UFM zso}sg@{Kz;>;b*j9c+oy3479eeEwLhe-svv!_9Lq&U;$s14^AvzV^L2&U>a~_3mBT zeB=CN2aWTRp=9Zn2~kRHe_wY+j!)kamCFeLcrQzu8>pk}8Gq#NyVXW$PPLf{9_k0Q zs`S?Qn)#LR(fp$}r%CbbJJZPbHjcrxtd{jG$=B#J27ndN`^rat@$RGd^?|6#-#e@- zHl6rEOsWBi==|C>=TSWMqs-WFfVbsoS+eU1(^N5y_CBRQQyg|Qm-ox?TI{k5B;}X6 zTI<0k7tMY3p*u{DO7)%>7(KW)tK5@Q#dzCS#rjsQU2I*EQC0q67ynX{FD#8XHovd; zzNqIe!E{BRL{X2O-bEOndV|$V<~~#`xR}MM)5L3O+`~F%8@+<=i2Wz+L1Wl zsE(w7Ds{)o?Aj$dwbx-k4LqwV&Spx#Jx~s8z5MU1I-#iQippFO5`}9-DzR>Wi|Dbc z_j>3XZX|LDNps2gZ;XsEX=|7>TTlT2Kn2vpJ83R&6U{&1K?8p`!(bQ?}5-H=HLw!0YxzZ=z53~cMT248))zvC%ldf;jXz%p@za+4(g~G zL>|qbIbWjZ?E@OU(-i4q7rHvL8cP1WP)l$uv9S-3Nghxau%pC?^3MrfPF|eQmHyzP z#NW>o8#_9>q(+1#^-@=^d(Tm+tM5`O;g;cZ5PlPJ!Xu=iRvq!)qA}g7utOb zJKyN&n6N~5l4N%(_rYVmcTrKTSojkbYib$VCFMwng8;mQ zeWIIP8_o0n+dfzcp8&?z6Yd7#op0Y&$@N>QW1hGOXgdpKu2;L10O!wt_H+gwgj?2a zz4Kuboup6zHKt6bhYTekXTB+5=ywbkE1_nP5EUTD{8P?(s zoe2hVQ3br%b8GEZeNB z9gDRYSu{iy8oMbV6WKh({HCa#DJ3%pm(qg~V`rVUD>iN654pnlZ6tURhVgfP_YSiw zen`iXAqIa!m9-xT3{uhx*lgj(-XmFJ7I8ZH8uu1oOEk|Zi{MA62lSyxoO+3RwfhSQ zMh;L6)Zg8_wNme-bdFMiK2cT$BBVuuL(n9-{pAl&?b&WMpaT%M|CY?9I8F9XcV2M9 z#EDWg$F2-IL4G?FD(m*Tlg4YsC0SN48hTS1{vm1IPVU#qZw|fC#A@} z>9VMN!5XAS4=_2E5io;xlDtT^=@il+E=HwYmg+ibX!i^r{2tf;-qL9q&1Qhmu-;1oBc-M>e>rumvprKqF9u{wUw|V&sE=n6ar2^_MOYs9 zQMGD;oY90+GfL&EO_zj|6FgV3%wAg{vsH|<1qA5fPxcKEQ6?6G#x(WzN>b{wE6(e4 zpOAi_L%;8=a^D=UFfW4DL#j0?eRvOLCWyp|l9=AYOBAsHJY13oB}p~joq}pUEqSF; z;rGD2onbhfguJ8*h6(JnjrNKA$kycG3mRE#WCU>X3@rn~nn1iskk?FSB9Ew5;9c<} ze@k2bxPXpjzHgOaX;(98-PO%9#3MnH9pohY!A1Vb)wN?2c~}l3UkQa8$}iryzK%F8 z6n2`hw8VI9I$oZra1;tZ33N@0j7&f@u4Af%eaK7~WVzlxp84^Esk?@wwXQjC^Y3z+ zT~FQiBYk?-;HjK?49+nr@EIwm*j7S-6>^_BwRH(#5qAN=R9ep1sgZdg!TK46mvFJLsOp+Tuw_Ct!th2t4SGEM5YZXC^>7fe6d@2%T9S}yz_3qyT}`U zD|M&cF-ZAhi~I&;C3@1xq|WIU(v9&oNfnh++&3cV3iA88?%-91r2v?b4@&`PoeKC5v0+?=$YdN)M-Re8134X)=Bpnxf#Arp1R zHm(YpaA1DIw57IJeP_{%6H{&8+Iaz0Ymo9lSgZTRr{~q8CY+Zz(~_3yxa_P-->D$6 zfbd?uqNo6~aV(`Z&62SzW1->peK~3K1yUOgUgRP|4JKLX6fiN`Y1|**l1mCfZ5b8h z?i6ebcAh5+K;KtGjZgfc6W#+qPA9cFqvqCXw92v}?n&l67W-5hx|K@5&=PB&lf!1F zz;{BUAb`1F4jN~(swz-c*xo`?r&C9Ql4W+)TgZ#eBNBHu#|kCHW@qeN#`6H^jK?H; zPCp{b7N5aG^)Z_`g0)#mR&A=yI0irY)Q22~L*yz=pq54SU71EdVZ01L=-}uo1UUzb z#gynQhK!`N^jlf_tHGBTV+qI#+W7aL{ioZh>(k{=eAdsWf(L{jCz%6b$<@-`l!|Yxi1r~_eET6h@MDyjJ;_P$z1tFg?|_) zdr*tZo9P27JBcl?*gyP1{vnO6aKE`f$#E5E?PSePkAD?G&_HL!yU|;#Uxlr#g`$Mu zqIm#WCljr+iXe0kGlYZxh)+**2I2qC_v_e6nK9U6!8)1XPEAJ!Xr%H@BKp6U=#y4Z z3NXHWB@dcBK%QmxMD|c&LFkMpIttsGNlihoA9bUjS5yB-X@lu@k73Me9T$w~_)Hgw z$n`f<$fyz>wTI-wSG7>H`&j|gT{1=sSUgJ#%}m2%$IzB4e|^Q+nOuO8cPSpwx?N(r zg`6iCt{1roZLlxH&DW-Y=3oKEixOuk`JNiqwcVew8@1ryT_miI!LM)pE1J}+Q$c2} zG@St8x8dvS1tp+^hE)LCCluf|haXGd@;i=@U}N00Z8yJy+brdgh?Ln9A-DoYADNQ( z03IYd6O5R-s^ScFdJLVPAmgB%=xCQY$bqlvykMn^XCcmAsTapzRw2J=+YeDW+5Vn#`ezR@7&1gvpD1UWxXgR#o}hB zp(S=z`D7pZWDntU=YQvpB<5Inab2?@hY6oM!MBY%vv|S@HZ{QryCjU_ndY|7TA!Q- zMJyUh-auua;Yn)(*^E0^5;F!4M$<{W79~H>w#@J(caI{I69@nh-?|VL66`yfM4N#- zv^iZa;xe8*dvsc~o=2E0@bW3-;-{iVf`s)TBN(;?gx8`#b1}$}BeWDvuA&|~Unw^_ z=uB>(DWn2KlGgVFb-X;`-y7X_8*Vl*Rr-OTJg6H0GLtD%*F8LZ-#xk>u1k@ZUaCCf zs{2K@$`*GfRZu3?<&`t*YZ}1)e*!c;i zP!X@+=N)jC+F4oLq~>G%s9l1;C~9`ueL1KdGo;(e6*S1!ol|uSr=fPJvS#UlI9A%JT zmA!R%#2Idq;}Hp(td7g{W&6|zs0z7Fb}c_OjEDnV zsFt#+^m!SaNsZePcyO3#4#c+3h0pNEbcvi{3L5jzh&TTC16+ERDDYWjM$kq#96@y! zO$}ujs)iuWUbuIx=7l#cKr;QRtD>1)AQ5vD;|J~26-8?FLjn_qA~A<6U9C%6gqx%5 z|BJEBF-06=*}SM1^XOyn;);OoiDvKajy?QS*u=W(CXCOj;mEY2@Sr_9t1mMoD)qRi zd{5FrPn3Rpzpm={&@Y`ns|88^6piT}_#* zO1eA3kVuh=TZTr#?IW02sZ;Ds;?JTO83G@H@*m*ALVC7Y0to3|KZ=A6H zBeMKwI@SS96>qVKi*c)zS)0! zLo6Gv8m|e&9*3I~*cebhOo^|Y=+hyO-Uqp?T+&|n7IVIXxYI@=4yvw>H&=Q&8y3FeM z<_xjn22KjnKc2n0d3{{}@7;b=DZLoRWuetPNRa1Rtoe7w?&r#wk2tfRtF)GWWNh@g ze|heUgk6qt+1$!2ddNzsH_!6uvafe&|8t ztQ`F_%lw&r@GeaoUsV<+D$SA_ZvPmx`-_rJ2}W6VCpr<{bKt{QfX#K1P8`|JG{qI= z@gst42g%>x#idF`;?f6d|2$VK9h@y_AffrJQ+#P3(8@yCPy^Zw$hWlaLEaacsimr_ zS|B0kWZYKTcx+|uvp!5!ScYb?7gt6<+;-03TnQiXoR{4)JcKA69VS-^JU_m~9x5Dd zjgp-wI^a#rOhF*QhUTgm0OMpwo5vCM~JC1VT7|j7(t5# z>{{|wR6i(8JUD(xFtqVXQK;CPsW@%PZY5EBpf^R(V%IN!th~_&7E!$n5r@6LIdabX zK!4G|dz065`Hzi}C_yyphpW0jsKb~)=vvoA#!sNlo}qhDn~j?OcFN#^9U}*&L(bvAE`d7c@JL9p)j5!J+G9&%!lP_+>LfjQXod5YmQ~z2Vi@)=0v9|x4Z|Rmr z2QL^q@4fnzhVM?8WO1ZRbF0fhuykbx*?k(nV|)o5T~FIyFlxog@O5shuv2cKww7A) zrbS-Lh6>jA!m6lI)2b#%T(kc+aYOX$fM41mW{A`2&VsSpLFZQR*6!td0ve8Z@_f<2 z&dBBD)`B|{PW9amVZgHf{vx-M*q2{4(M#=gK;#>>@4q>+Crs_9+8x0! z&7Z7UIzFynqDpSV+z|^z(&AS7OlsjVJyvCbR@n23ZG4Sxi*^P+e3f>A0Hp2v_VHmv ztu0LgjSdH=vpeT40R0?h7&G7r^86yPfASp+ZWma(G>mVN*O8|`rAPv~Hw=}4a@o1F zhkz>$^axo80=&29u%-%+027`SbOI%33J7<5wdR{eTVWa{Gm;&~SuW#S{QQpCkd5i{ zh3;}k?B-T?^fk3o^OeLBq{s)}R)Aj+Vi~Znx z3=?Dd&fX>d6bhGpx4?o=-uT+!Ci>8;EZ_d<>gt+%SusXPNXYQsw&nesYA;M|1pK6@ ztgQS{!=jWK2zrK3M)F?7q*56Clgm>F26Rjz5`}G}O&Jvzxh;Eu_%z?6JRr5edc(F+ zO0jCB9I~ z`w@HN{aX`Hx9vcU@?zsRCHSqt#$D$OUAs5^_0ng>?B{5~p>3gUR2&nQY#XJarlwYV z@O-D%9v?E@v(^N{7CbT5zJv_G@Bcii2^cA40y46BowzH%tJ0IO&CeBO<*IJK=Yl`5 zYy~$JyhHW8Vg35`t?I!0?#Q63R;fk2`B#xFa7obqtat}aMiya*u z{a{hCPlnzCujOmU(sLcZL)gD`;-mC*VyiLAT(t7|iPKqEe0v#8i#!_=iSbA*aT?KE z3>oNYnyLoGX9`W=fHrA9$@kIaR=Q)4-ab>v0@2I~Z@_$L=K;a^yOxS&k|KrOe$GwJEJ&qN$cP5uWpy7Qm1HPTwcvgc2|6@c1XItS zAUn*&Qz)2kzv#~ns(srU%lL__#eG=uuQs|(c%5A^k_yZP6 zzoCc)PWE`!C!6pZ5Z}9CT9{KgZr50qS4oe5HZe*qhdi0n)6@O#-j$wk zy9~K9Kc=62KYD+(YH1T-7>O1ARr*G0NPxbAbbT4Atf(r&}g>Hs46SF9)f-0o&4hYEhoYi=ku1yB^(2I zsz0`aCbbchAM6W!228be36UX^VF#4I{9`B13aYsIEpFr*nF=#*hrc+>5}%lni_VW9 z0)+U6<*HY(*^;3G^1pWN8g2AFd-l*qS5)&kVoAO@{(gRQ$#KRKm`(oAM7i^NQ`MmuXt*_LT zLmNaqo!(SfeA`oTJBX9EZ{ObZ#7bpXK=In+-`!|kk=Apyb_!fxR+V=>M9|Oz8b+RT z^TMsrl=TY=5P*3aRDlyjYA-i_^;4SHOmLCiD|Cunw%Kn~-^|&wxMeYB4 z0ct8Ab3P-EyBXH(#MKC-dQf3o9h3nI!=x)bKqMs%a!@K-g2d)*Z4cu9NlboAaMs0& z7K>R@Aj*1m!sXduVncVDJ%y2AIQ*i_S$#^A43O;3*1Vz13R?DoU^(@h5f%;$-^G{p z3S;(yG4&_>v3;vICMdi533Bg^>)`lSI`roYpXDL?24%pD@6-y zo+fZospE%v;$CGj2ls};kjbkb-aOOb4P#WZ6OHs$XFPCvr>f#@@XHGNaLDm*Ka|4| z8jLO~F)2)-FwP6&$#kR=LH>fJc~@88Ky=`~>xV?}5u9V?iY(y7ys}1O1Z(Pfb>RYm zQ656#U_Cw9k;8tl9sGl-8#r+yFZhNc=%;s3Y?t%*H}O-ihP*}v7BAjZbsHY?TwGiv ztlf>sz0(I(1v_l$9Kau95Z%Pi0ZdQAsMBfWOa|5MB0(XcoKDPkCh{J)tzsX((V^fq z@DtpKYj!vu5cuxNyn*iw4tx3I=6LCw-a*13Jp z9{0?Pmo6m}bq#YBOt8$YvGi&-8k_4`GWWzE_auPs$ttXynvkI9@+@Bknl`%%lT8^< zVzldH-~RP&&N*AJ$E_ITNNyuVSZ~WW4g2f^SJ1xM_KIfCo`Gpq`Xgqxr+6ETJUbu= za1Y;fCw>&`a9!}bu|JOqpe4T>OP)Rxj|J|I3CudO?=7%T0@z`?J67i0L3l#v9rh~y;^Y3oN01RoL+gD%Kx0EKOTH-D_Y~4X=O6O)7 z%E5~kGCv9i&l_ZLJp>d%-u3T^eA1UQZ^L<;8Wt|n%^Pj&4K-W3usF60Uyqj*oBzS; z7h5urx@G^C?85KkJV~}=_R)qC_sj)X+p!ZeYjF%6@E*R8QDQiJw{c`W4$wn2xEuii z%2dRr;cf5hDsCI)v=&$Qwg}5nNppSc2gDZJF`S}vHpd{o`SQ^FzCYyf#qcCKLqp!j z$skd@J?m@)rPgkPeL3cz9^i}(LBhujBuUjA3I-oVzrj3?Ghn1|=w7nzK2&`zX=CN%rKs&19 z30T@k>@W+2R3Fkt8Dz3fop6+FvI-b3Ne!v}AZkqL5MYh>aUh<{=o^A=hOYSDzVzkw zdb0GKTg^ORAh!XT5<0PqR)7LZ5}h+(oW>ZJ?W^sAaGaSp=V2Iririq(4Zq+_wYhZ$ z0#F51m>bu=rAm|7yfmNj+Rv7(8ihgGBtBF?kJQW5{Uifc62Lb%H#44punP<~?*)6A zpWH2x)5;?|+z+KcokP1Zz^v7SBwS4-yJ`TXk4jc1xmwwdx>me=-Y5a-fxJh3UN63p z5^W^0K3%aic*P9W%KYsRxurmJLbTdEGt_L=ZA8_u>pb7NwqVhD=SKeUh~QvHnh9ru z-u?BKO+j|>O^n2La~s8$VKQ9i!Qnya!lam}Mcnh;yJJ6*Zbn|{^bf;Bv8gMKU)caQ z+pU~#k_LCAt0qh)g9Z}A`czkZqjzYq=3N_^YOEY9DwtD@@ku7yG#lWc>|z{#vMy#9v>i$AQIE zs0Zy#AA_y39)+S|Xf^6VY4D=VEyY;2K|x*AE)$p?x1kHM==~z8H1j|>PC;#q4BBtQ z*tP>UP!kS-4Fve-)n3r@M%K>oT6VX9>njVn>k+qa*fN|cCY(!~dMhd_Dz+v#WIrVQ zCL}>5b~f_n(=eF0-VHuz`YN(Yz&);RBYS)lJ0^fx9)cM#8AE_hA(JLUQf0fxSS=|a zJ~2dc;v*9YOAf44wXwra%qp9fbw!XtuABIj67H;}pT%@d2d!XL1EoRQ6v{~hUss+1;C^vEZNzx?cZdyT z9ZH(~y|PqV4Zif&a1hwDrg6c6vN#bPB4){es+W=8{ut2A|5?wkj>#6L823Eb7DP+JQ$gq7rx5A-3z zycd%kh+VEfyEkdNCN=u5Rlt!tJW_bvEfT)uX~7Ctylv6THsT+Ul#4h?(n!2Wb;s92 zD3{Z`5DGqQm71C26WA}A4|mvb1ge2#h?mH?l@J4gWy@ShOi$zB)e6Y(LEjQi%he?N zB8!OcfWJ`ZM@s{VqwA$hDlUKwZdjMm(L@FvIU=PvDAsNXger;7!SD%F@ za)`+JxMwsofs#xVMtNp{Hmlp<<6%H3#G9^%aFutCm5oFgE`(R>5ggTBGte z5mqS4a=Q7XE#HdB=`bIF977C}KnwrBXnsm&kk_2TM{)YPr z&J@6$ydwBkL7|~%Xif`klmRHsFHM9}Q9!Wk&Gc|8=RR{lTZCp4gt<9ypz_czcQQT7!IuZeAZ6+`^*2Y+ zGW?ow_{9X8IoOnN45|BLyE?p9$w8BK6uAtq1A@MvGA6_`|8NjPQ^@k7x{DSyjH&i$ z^khV{S88kFo%E)go)@+Tb0oTp?f0fg+`LnjMg(*`mFJ&jyd1v?TrDb zbU&yKkwCr~A~(?NNWgw-1>_TpX=InjJk?a?h z$?lY_pw*)I5Ued8nA(SZh+>*Ly~FeHmBc z^DQgsE{@+C63EcF^!^p+*-%4)#URu}2VhZkhvmxF1GXhF+NwYU1c?3)E!m=DI2jCN zj%jOaht*!_oV0-VIZTt98E*hkN<OP@}yUR(dNbR;eGEm}C zQcpxfhK!CQl7>W)IeS1iz6lSROr7DCT2Y^zKu6HPYyono3a4bA1PMeTL5A7{C)bBW zwgn-CT*4RHV!nriZLlsYm49fZU5DVU14siSnFO%qkI~))h!H?GkP*vvEqMC1y&ZkG zw)uY?Yf75VQ2&pdCky|lN=(PDV$shXu)wm(W7eb6X?5W${;ea}yme)Xa`kJ(aHP%a zOW&6&AAWHBxms?Pt^GBtw`TmmrY6v83ONx6nv_#G zx>}@n{9Cf8cxR!85)mKRmBgKyIL_9o{|a+Bl}KXAbDI6D|&=!#hGvxgapve*j| z?%2H8%_*4F@HFx2+1QxH>%t9LZ+p+Z{xi#N=%XOtWr!>46$jr@Q%y#v-^SN7 zF6dj-89PGSZl_}nhl7^czWi4Fd590dTI+jN&rxKOI}By72#62K^}X-*x!)>B%q|Pk z7dT+RJ^p9wF)Zh;GHb8y%u)ImN-?&U-nlJmTq9X|rK@2qF$L9MNb5{uZBvuKiDuAY zHqX;IGF2hEPvISNLy*5J|0pK?8sVQbL5%g zI;}_RspB%T3vyQTgD!=!W8@dIyp)|gB^&Q?bFmaC-?w5Comjs1YI}+>sl{Uih+oEy&=E?Ba+fjz)kK*Z=HTyTI#bmm@rS{{l^aYeeO% zXWh37_EQt>s>u95Tdv)-Zsrl|Yt`%*pAgg!AogGnynn)TY#L6qgqyR*|BNdL47~b% zkS^;*YOd&1N%0B4vuRmhQgJ$}$T}+mfyHihTuxpw(>6hH%`5(ZprDlsg2I(ITSu%? zZl2^K>v~slQNL&_W|g}zv%P=;TSu|B` zE6$7TpA1PUi!<*G0KkA^G-4_1;WnH&Yt{D%1`6$Y3*8luRldNG1B1sX0_soR{`vS> z^+)I;ZjHF7XtQ)-OG*@<$C{^Mz1VaLWg)fA-rXkNF zQ|eEMO~nTG2qSg?_!TNL2DM%B?=yahQa);Xc&HB@eqgRWo%mPuOn5bu1PD)SyG%Qc zuR_oNX~k+SeVkv`GJMuc7Zx0fvb$y|LKGEB#KJX}YXgO%QtI8kJHm}|OyA#B?%fKg zu@AxCA~|{@JMf0S-l1HvsdKT8GcIBsW68`Rly}0yv8-!-@EximpuGf$o*9gz`Aris zm)VTGvCJV3Y%fUci|*RPEpDJ3jw&w>`~MX=ryl8txG1FE7%h?l%q}?6e96-a=6dZ>|}bBC$387U`#i6FSPNPMC~az3Td|oZ&LsE3tHWIW^z3*CxsNi8BmV!1~d|W^q1Q}22AiWnnyw+ z>}ayd+?`-+1#-|eMTMu8l5r|>Vn{@(sYV7ZlrFx3@PtOy04=CUyOuUTQ9}!mMYVAu z=`{&PrjDp=Z&yx2)xyRcn&Q{m2A1T0K&gaM1eT3DueGrMAV9&uC;a@Z+>0Iq5iN_w zuFn+}n{L3}?1k!HZ^_d0t~UOqJcGw7ex3EFd_6jaLoeTP!y#S3QG?kK^Dhqk@};z3 z=#4)8qbYTbeGQG}K7G|b(q_oC_i=g>bT+cDP61FWphV%fc%KgA}Xx26zY5*;%6^YtD{)QrWA1_Wa zXre4ddv-6CZBuV^j^S6Ns7Z9kN);%y67zoV3e zJb&ZV8&0NR-NUFsm%oCy&Bh0{D8hox?U(mcd?l6=l|^BOF-Aq_j~CD~PC<<^19`eN z3~&>ZJ%~z1`|_CA5o=Af8Hl_l*YiM5?7lyeH%eqs!er4mQ?e(da%zyjxt=0`k*<^7 z=g(SCHfTipWm~~hb`pZZCGa0foHgyRoHFVHEnwNL(uPR0`_VK*-OY5ka=oyk*gsd} zc%q4k#1>a;U(^D{paFKt%abV^@5C6xC;)}3=a>Q%7QEDhsVlm9-8us|Nws1{nw`q7 zU3SM^AU@G^9)8PdAy2(b+Wh+aePk^LhL5V2ChE3lPIhR;`dE6BGL`u9@Mw0XCe0P& z1WZfpJu=Fh`o_#EGe-4^Da_2;WYat->Yl(Dx&l_gPVljIp^8AP;}|tZc#B+K<941T zH(OM3Q~SFU`+0~0_{{4batWul8C3Id&}7cHccE`Tq6PurE*iWvW}xF)LBce89t?VR zo5ChDd|dvX9igGu<$Cpd5H9WB)#M4G$4mGn`J7#S!iRgBZOdu15ftpurH{dIrmfOg z6jnb19$;oq0HrEFt=Y4Grw^*W`y!xF0xhTQ$MW%}YY~M*T+e)(*FyqU|De=mErcqc ze!t^7ju0B-ah1mUo&mDRZ_UC!ubb}sJ#YJ2rj4^7rd_K$PG4fvdWCi2 JYkvIYe*tdyO`8A! literal 0 HcmV?d00001 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/inputs/buses.csv b/examples/small_signal_and_emt/2-bus_src-gfm/inputs/buses.csv index b50acd6..3817d3d 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/inputs/buses.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/inputs/buses.csv @@ -1,3 +1,3 @@ name,base_power_MVA,base_voltage_kV,base_frequency_Hz,minimum_voltage_pu,maximum_voltage_pu -lima,1.00E+02,2.30E+02,60,1,1 -santiago,1.00E+02,2.30E+02,60,0.95,1.3 \ No newline at end of file +lima,1.00E+02,2.30E+02,60,0.95,1.05 +santiago,1.00E+02,2.30E+02,60,0.95,1.05 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/inputs/gfmi_c.csv b/examples/small_signal_and_emt/2-bus_src-gfm/inputs/gfmi_c.csv index 3c5387f..8f66653 100755 --- a/examples/small_signal_and_emt/2-bus_src-gfm/inputs/gfmi_c.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/inputs/gfmi_c.csv @@ -1,2 +1,2 @@ name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,h_s,kd_pu,droop_q_pu,tau_pc_s,v_dc_pu,kp_vc_pu,ki_vc_puHz -solar,santiago,80,80,50,51,1.00E+02,4.80E-01,60,0,0.02,0.1,1,0.1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0.5,70,0,0.001,1,1,10 \ No newline at end of file +solar,santiago,-50,-50,-10,10,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0.5,70,0.01,0.001,1.05,1,10 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/active_power_balance_by_bus.csv index e798b85..43499bd 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,timepoint_1,-67.08056522433783,-9.97494096311732e-9,0.0,-67.08056523417291 -santiago,timepoint_1,80.0,-9.974940965584796e-9,0.0,79.99999998990339 +lima,timepoint_1,60.31569213226618,-9.974940964403198e-9,0.0,60.31569212229119 +santiago,timepoint_1,-50.0,-9.974940964403193e-9,0.0,-50.00000000997488 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/bus_voltage.csv index aa7960b..d17c3a2 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/bus_voltage.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/bus_voltage.csv @@ -1,3 +1,3 @@ id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg -0,lima,timepoint_1,1.0,0.0 -1,santiago,timepoint_1,1.2093116675812525,17.191975663648932 +0,lima,timepoint_1,1.0039676883731015,0.0 +1,santiago,timepoint_1,0.9966223958255749,-15.986449720491722 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/costs_summary.csv index 004cadd..22bb914 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/costs_summary.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/costs_summary.csv @@ -1,2 +1,2 @@ component,cost -total_cost_USD,-0.0000398997638476992 +total_cost_USD,-0.00003989976385761279 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/generator_dispatch.csv index 3cbe5a5..3ad6183 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/generator_dispatch.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/generator_dispatch.csv @@ -1,3 +1,3 @@ id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,timepoint_1,-67.08056522433783,-36.2809234833063 -0,gfmi_c,solar,timepoint_1,80.0,50.227752474633064 +0,infinite_sources,gen1,timepoint_1,60.31569213226618,1.388783529105015 +0,gfmi_c,solar,timepoint_1,-50.0,0.7526101226466527 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/line_flows.csv index 365c6aa..6eac7b5 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/line_flows.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/line_flows.csv @@ -1,2 +1,2 @@ line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,-67.08056523417287,-36.28092349329843,79.99999998990337,50.22775246504935,12.919434755730506,13.946828971750918 +tx_1,lima,santiago,inf,60.31569212229122,1.3887835191300466,-50.00000000997491,0.7526101126717135,10.315692112316306,2.14139363180176 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/load_shedding.csv index 69b1b33..2061806 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/load_shedding.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/load_shedding.csv @@ -1,3 +1,3 @@ bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,timepoint_1,-9.97494096311732e-9,-9.974940964300128e-9 -santiago,timepoint_1,-9.974940965584796e-9,-9.974940954696962e-9 +lima,timepoint_1,-9.974940964403198e-9,-9.9749409644032e-9 +santiago,timepoint_1,-9.974940964403193e-9,-9.97494096440319e-9 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/reactive_power_balance_by_bus.csv index 6626494..1f256f2 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,timepoint_1,-36.2809234833063,-9.974940964300128e-9,0.0,-36.28092349329843 -santiago,timepoint_1,50.227752474633064,-9.974940954696962e-9,0.0,50.22775246504937 +lima,timepoint_1,1.388783529105015,-9.9749409644032e-9,0.0,1.3887835191300466 +santiago,timepoint_1,0.7526101226466527,-9.97494096440319e-9,0.0,0.7526101126717357 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv index 0f015d9..69f8c08 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.016715049743652344 +time_spent_seconds,0.0771188735961914 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/F.csv index a6b49a3..f3b6a13 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/F.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/F.csv @@ -1,4 +1,4 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_c_0', 'i_bus_D')","('gfmi_c_0', 'i_bus_Q')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_c_0', 'i_bus_D')","('gfmi_c_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" "('infinite_sources_0', 'v_ref_d')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 @@ -8,11 +8,11 @@ Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","( "('gfmi_c_0', 'v_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 "('gfmi_c_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('pa_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 -"('pa_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 -"('pa_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('pa_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 -"('se_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/G.csv index 282ae29..703f4ab 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/G.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/G.csv @@ -8,11 +8,11 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('gfmi_c_0', 'v_ref')",0.0,0.0,0.0,0.0,1.0 "('gfmi_c_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/H.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/H.csv index 05c50bf..8d8fa5a 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/H.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/H.csv @@ -1,11 +1,11 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_c_0', 'i_bus_D')","('gfmi_c_0', 'i_bus_Q')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfmi_c_0', 'i_bus_D')","('gfmi_c_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" "('infinite_sources_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/L.csv index 515e219..279f2e4 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/L.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/component_connection_matrices/L.csv @@ -3,9 +3,9 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/A.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/A.csv index 63dbe2e..813d915 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/A.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/A.csv @@ -1,20 +1,20 @@ -Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_c_0', 'angle_pc')","('gfmi_c_0', 'w_pc')","('gfmi_c_0', 'p_pc')","('gfmi_c_0', 'q_pc')","('gfmi_c_0', 'pi_vc')","('gfmi_c_0', 'i_vsc_d')","('gfmi_c_0', 'i_vsc_q')","('gfmi_c_0', 'i_bus_d')","('gfmi_c_0', 'i_bus_q')","('gfmi_c_0', 'v_lcl_sh_d')","('gfmi_c_0', 'v_lcl_sh_q')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'i_bus_d')",-7.5398223686155035,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-697.9546674137151,285.21657689930515,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_q')",-376.99111843077515,-7.5398223686155035,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-285.21657689930515,-697.9546674137151,0.0,0.0,0.0,0.0 +Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_c_0', 'angle_pc')","('gfmi_c_0', 'w_pc')","('gfmi_c_0', 'p_pc')","('gfmi_c_0', 'q_pc')","('gfmi_c_0', 'pi_vc')","('gfmi_c_0', 'i_vsc_d')","('gfmi_c_0', 'i_vsc_q')","('gfmi_c_0', 'i_bus_d')","('gfmi_c_0', 'i_bus_q')","('gfmi_c_0', 'v_lcl_sh_d')","('gfmi_c_0', 'v_lcl_sh_q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'i_bus_d')",-7.5398223686155035,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-723.1203801581415,-213.50908482472548,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_q')",-376.99111843077515,-7.5398223686155035,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,213.50908482472548,-723.1203801581415,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'angle_pc')",0.0,0.0,0.0,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'w_pc')",0.0,0.0,0.0,-70.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'p_pc')",0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,0.0,1305.929900975793,-175.63410139764756,552.3230020226116,-552.3353845084039,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'q_pc')",0.0,0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,-175.63410139764756,-1305.929900975793,552.3353845084039,552.3230020226116,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'pi_vc')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-9.9107712566067,1.3328965073172159,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'i_vsc_d')",0.0,0.0,0.0,-225.20563327909315,0.0,0.0,3769.9111843077517,-75.39822368615502,376.99111843077515,0.0,0.0,-7506.183924847491,502.4901450459911,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'i_vsc_q')",0.0,0.0,0.0,-707.1660898606517,0.0,0.0,0.0,-376.99111843077515,-75.39822368615502,0.0,0.0,0.0,-3769.9111843077517,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'i_bus_d')",0.0,0.0,508.04951771675695,-208.22553435471542,0.0,0.0,0.0,0.0,0.0,-56.54866776461627,376.99111843077515,1884.9555921538758,0.0,0.0,0.0,-1631.2653040736207,-944.4739764110853,0.0,0.0 -"('gfmi_c_0', 'i_bus_q')",0.0,0.0,2222.161205510431,-208.22086626754765,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-56.54866776461627,0.0,1884.9555921538758,0.0,0.0,944.4739764110853,-1631.2653040736207,0.0,0.0 -"('gfmi_c_0', 'v_lcl_sh_d')",0.0,0.0,0.0,-66.21249632048333,0.0,0.0,0.0,3769.9111843077517,0.0,-3769.9111843077517,0.0,-3769.9111843077517,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'v_lcl_sh_q')",0.0,0.0,0.0,-492.3239739610557,0.0,0.0,0.0,0.0,3769.9111843077517,0.0,-3769.9111843077517,-376.99111843077515,-3769.9111843077517,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",5234.6599794295635,2139.124316049167,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,0.0,0.0,-5654.8667481872935,0.0 -"('pa_rc_0', 'v_bus_Q')",-2139.124316049167,5234.6599794295635,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,0.0,0.0,-5654.8667481872935 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,1138.0525352410905,0.0,0.0,0.0,0.0,0.0,0.0,4893.795887751882,-2833.421915066146,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,5654.8667481872935,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,4267.955218941629,0.0,0.0,0.0,0.0,0.0,0.0,2833.421915066146,4893.795887751882,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,5654.8667481872935 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,0.0,-7.5398223686155035,376.99111843077515 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,-376.99111843077515,-7.5398223686155035 +"('gfmi_c_0', 'p_pc')",0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,0.0,987.9260565411992,23.78030884341824,-496.76780316078685,-70.54199784602056,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'q_pc')",0.0,0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,23.78030884341824,-987.9260565411992,70.54199784602056,-496.76780316078685,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'pi_vc')",0.0,0.0,0.0,0.0,0.0,-0.1,0.0,0.0,0.0,0.0,0.0,-9.997104207562575,-0.24063969567519944,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'i_vsc_d')",0.0,0.0,0.0,346.7421388408102,0.0,-37.69911184307752,3769.9111843077517,-75.39822368615502,376.99111843077515,0.0,0.0,-7538.730680585775,-90.71902801143482,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'i_vsc_q')",0.0,0.0,0.0,158.9980800432565,0.0,0.0,0.0,-376.99111843077515,-75.39822368615502,0.0,0.0,0.0,-3769.9111843077517,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'i_bus_d')",0.0,0.0,-236.09093185117854,-26.593706664312624,0.0,0.0,0.0,0.0,0.0,-56.54866776461627,376.99111843077515,1884.9555921538758,0.0,0.0,0.0,-1732.449388861673,742.7494190000986,0.0,0.0 +"('gfmi_c_0', 'i_bus_q')",0.0,0.0,1863.6945957046444,187.27704971398418,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-56.54866776461627,0.0,1884.9555921538758,0.0,0.0,-742.7494190000986,-1732.449388861673,0.0,0.0 +"('gfmi_c_0', 'v_lcl_sh_d')",0.0,0.0,0.0,8.964965227509495,0.0,0.0,0.0,376.99111843077515,0.0,-376.99111843077515,0.0,-37.69911184307752,376.99111843077515,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'v_lcl_sh_q')",0.0,0.0,0.0,-372.4393489823719,0.0,0.0,0.0,0.0,376.99111843077515,0.0,-376.99111843077515,-376.99111843077515,-37.69911184307752,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",5423.402824069048,-1601.3181281788504,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,0.0,0.0,-5654.8667481872935,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",1601.3181281788504,5423.402824069048,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,0.0,0.0,-5654.8667481872935 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,-740.2906646531766,0.0,0.0,0.0,0.0,0.0,0.0,5197.348140598278,2228.2482458590544,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,5654.8667481872935,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,-2739.060301026596,0.0,0.0,0.0,0.0,0.0,0.0,-2228.2482458590544,5197.348140598278,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,5654.8667481872935 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,0.0,-7.5398223686155035,376.99111843077515 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,753.9822368615503,0.0,-753.9822368615503,-376.99111843077515,-7.5398223686155035 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/B.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/B.csv index fa9e8a8..4e32179 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/B.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/B.csv @@ -5,16 +5,16 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('gfmi_c_0', 'w_pc')",0.0,0.0,1.0,0.0,0.0 "('gfmi_c_0', 'p_pc')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'q_pc')",0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'pi_vc')",0.0,0.0,0.0,0.0,10.0 -"('gfmi_c_0', 'i_vsc_d')",0.0,0.0,0.0,0.0,3769.9111843077517 +"('gfmi_c_0', 'pi_vc')",0.0,0.0,0.0,0.1,10.0 +"('gfmi_c_0', 'i_vsc_d')",0.0,0.0,0.0,37.69911184307752,3769.9111843077517 "('gfmi_c_0', 'i_vsc_q')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_d')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_q')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'v_lcl_sh_d')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'v_lcl_sh_q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/C.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/C.csv index 424533a..27e8754 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/C.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/C.csv @@ -1,11 +1,11 @@ -Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_c_0', 'angle_pc')","('gfmi_c_0', 'w_pc')","('gfmi_c_0', 'p_pc')","('gfmi_c_0', 'q_pc')","('gfmi_c_0', 'pi_vc')","('gfmi_c_0', 'i_vsc_d')","('gfmi_c_0', 'i_vsc_q')","('gfmi_c_0', 'i_bus_d')","('gfmi_c_0', 'i_bus_q')","('gfmi_c_0', 'v_lcl_sh_d')","('gfmi_c_0', 'v_lcl_sh_q')","('pa_rc_0', 'v_bus_D')","('pa_rc_0', 'v_bus_Q')","('pa_rc_1', 'v_bus_D')","('pa_rc_1', 'v_bus_Q')","('se_rl_0', 'i_br_D')","('se_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'i_bus_D')",0.9256911281079382,0.3782802338772842,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_Q')",-0.3782802338772842,0.9256911281079382,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'i_bus_D')",0.0,0.0,0.20125187487501822,0.0,0.0,0.0,0.0,0.0,0.0,0.8654131221254014,-0.5010590065582746,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_c_0', 'i_bus_Q')",0.0,0.0,0.7547401926508261,0.0,0.0,0.0,0.0,0.0,0.0,0.5010590065582746,0.8654131221254014,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 +Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","('gfmi_c_0', 'angle_pc')","('gfmi_c_0', 'w_pc')","('gfmi_c_0', 'p_pc')","('gfmi_c_0', 'q_pc')","('gfmi_c_0', 'pi_vc')","('gfmi_c_0', 'i_vsc_d')","('gfmi_c_0', 'i_vsc_q')","('gfmi_c_0', 'i_bus_d')","('gfmi_c_0', 'i_bus_q')","('gfmi_c_0', 'v_lcl_sh_d')","('gfmi_c_0', 'v_lcl_sh_q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'i_bus_D')",0.9590681912721563,-0.2831752187073487,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_Q')",0.2831752187073487,0.9590681912721563,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'i_bus_D')",0.0,0.0,-0.1309121324371581,0.0,0.0,0.0,0.0,0.0,0.0,0.9190929463128948,0.3940408050416634,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_c_0', 'i_bus_Q')",0.0,0.0,-0.484372209460925,0.0,0.0,0.0,0.0,0.0,0.0,-0.3940408050416634,0.9190929463128948,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/D.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/D.csv index 515e219..279f2e4 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/D.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/D.csv @@ -3,9 +3,9 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 "('gfmi_c_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('pa_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 -"('se_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/u.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/u.csv index e97dff3..0da5867 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/u.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/u.csv @@ -1,6 +1,6 @@ name,component,type,init -v_ref_d,infinite_sources_0,device,0.8770607186443364 -v_ref_q,infinite_sources_0,device,-6.183078103210549e-18 -p_ref,gfmi_c_0,device,0.8183040522662995 -q_ref,gfmi_c_0,device,0.624304539854994 -v_ref,gfmi_c_0,device,1.3176874606052849 +v_ref_d,infinite_sources_0,device,1.0602915467925695 +v_ref_q,infinite_sources_0,device,-6.184065849485258e-17 +p_ref,gfmi_c_0,device,-0.4924473672884809 +q_ref,gfmi_c_0,device,0.05787698596992697 +v_ref,gfmi_c_0,device,0.9882122222891866 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/x.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/x.csv index 85dac69..0abab9f 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/x.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/x.csv @@ -1,20 +1,20 @@ name,component,type,init -i_bus_d,infinite_sources_0,"",-0.7582024031718437 -i_bus_q,infinite_sources_0,"",0.08209677086376665 +i_bus_d,infinite_sources_0,"",0.5722653561902081 +i_bus_q,infinite_sources_0,"",-0.18339083649397844 angle_pc,gfmi_c_0,"",0.0 w_pc,gfmi_c_0,"",0.0 -p_pc,gfmi_c_0,"",0.8183040522662995 -q_pc,gfmi_c_0,"",0.624304539854994 -pi_vc,gfmi_c_0,"",1.4031838768194038 -i_vsc_d,gfmi_c_0,"",1.8758163131381695 -i_vsc_q,gfmi_c_0,"",-0.5973764958084721 -i_bus_d,gfmi_c_0,"",0.5523230020226116 -i_bus_q,gfmi_c_0,"",-0.5523353845084039 -v_lcl_sh_d,gfmi_c_0,"",1.3059299009757932 -v_lcl_sh_q,gfmi_c_0,"",-0.17563410139764757 -v_bus_D,pa_rc_0,"",1.0 -v_bus_Q,pa_rc_0,"",0.0 -v_bus_D,pa_rc_1,"",1.1552793404899095 -v_bus_Q,pa_rc_1,"",0.3574414004915346 -i_br_D,se_rl_0,"",-0.7208056523417289 -i_br_Q,se_rl_0,"",0.2961425679329845 +p_pc,gfmi_c_0,"",-0.4924473672884809 +q_pc,gfmi_c_0,"",0.05787698596992697 +pi_vc,gfmi_c_0,"",0.8875147374562453 +i_vsc_d,gfmi_c_0,"",-0.42175550635008513 +i_vsc_q,gfmi_c_0,"",0.9197620895795204 +i_bus_d,gfmi_c_0,"",-0.49676780316078684 +i_bus_q,gfmi_c_0,"",-0.07054199784602057 +v_lcl_sh_d,gfmi_c_0,"",0.9879260565411991 +v_lcl_sh_q,gfmi_c_0,"",0.02378030884341824 +v_bus_D,shunt_parallel_rc_0,"",1.0039676883731015 +v_bus_Q,shunt_parallel_rc_0,"",0.0 +v_bus_D,shunt_parallel_rc_1,"",0.9580798748577896 +v_bus_Q,shunt_parallel_rc_1,"",-0.2744797866029319 +i_br_D,branch_series_rl_0,"",0.5505748558041546 +i_br_Q,branch_series_rl_0,"",-0.08076412991454072 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/y.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/y.csv index f441917..97bddd7 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/y.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/small_signal_model/y.csv @@ -1,11 +1,11 @@ name,component,type,init -i_bus_D,infinite_sources_0,"",-0.6708056522433783 -i_bus_Q,infinite_sources_0,"",0.36280923483306304 -i_bus_D,gfmi_c_0,"",0.7547401926508261 -i_bus_Q,gfmi_c_0,"",-0.2012518748750182 -v_bus_D,pa_rc_0,"",1.0 -v_bus_Q,pa_rc_0,"",0.0 -v_bus_D,pa_rc_1,"",1.1552793404899095 -v_bus_Q,pa_rc_1,"",0.3574414004915346 -i_br_D,se_rl_0,"",-0.7208056523417289 -i_br_Q,se_rl_0,"",0.2961425679329845 +i_bus_D,infinite_sources_0,"",0.6007732403221651 +i_bus_Q,infinite_sources_0,"",-0.013832950454366671 +i_bus_D,gfmi_c_0,"",-0.484372209460925 +i_bus_Q,gfmi_c_0,"",0.1309121324371581 +v_bus_D,shunt_parallel_rc_0,"",1.0039676883731015 +v_bus_Q,shunt_parallel_rc_0,"",0.0 +v_bus_D,shunt_parallel_rc_1,"",0.9580798748577896 +v_bus_Q,shunt_parallel_rc_1,"",-0.2744797866029319 +i_br_D,branch_series_rl_0,"",0.5505748558041546 +i_br_Q,branch_series_rl_0,"",-0.08076412991454072 diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/run.py b/examples/small_signal_and_emt/2-bus_src-gfm/run.py index bc4a638..5cb7e36 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/run.py +++ b/examples/small_signal_and_emt/2-bus_src-gfm/run.py @@ -46,12 +46,12 @@ def step2(t): 'v_ref_d': step2 }, 'gfmi_c_0': { - 'p_ref': step1} + 'p_ref': step2} } t_max = 2.0 # Simulation length # Construct system and small-signal model -_, ssm = main.run_ssm(case_directory=case_dir) +sys, ssm = main.run_ssm(case_directory=case_dir) ssm.simulate_ssm(t_max=t_max, inputs=inputs) # Run EMT simulation main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/ssm_analysis.py b/examples/small_signal_and_emt/2-bus_src-gfm/ssm_analysis.py new file mode 100644 index 0000000..2a08991 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfm/ssm_analysis.py @@ -0,0 +1,157 @@ +""" + +Small signal analysis of GFMI_E v INF source under variation in Pload, Pref, and Qref. + +""" + +# Import Python standard and third-party packages +from pathlib import Path +import numpy as np +import seaborn as sns +import matplotlib.pyplot as plt + +# Import sting package +from sting.system.core import System +from sting.system.operations import SystemModifier +from sting.modules.power_flow.core import ACPowerFlow +from sting.modules.small_signal_modeling.core import SmallSignalModel + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model + +inputs = {} +t_max = 2.0 + +# Load system from CSV files +sys = System.from_csv(case_directory=case_dir) + + +# Define range of P and Q to vary over +n = 11 +Prange = np.linspace(-100,0,n) +Qrange = Prange + +Sbase = 100 # MVA +vdc_ref = 1.05 # pu + +# Case 1: Pref = Pload +results = np.zeros([n,n]) +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + sys.gfmi_c[0].minimum_active_power_MW = P + sys.gfmi_c[0].maximum_active_power_MW = P + sys.gfmi_c[0].minimum_reactive_power_MVAR = Q + sys.gfmi_c[0].maximum_reactive_power_MVAR = Q + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + sys_modifier.combine_shunts() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + + max_eig = np.max(np.linalg.eigvals(ssm.model.A).real) + + results[i,j] = max_eig + j += 1 + i += 1 + +# Plot results +fig, axes = plt.subplots(nrows=1,ncols=3, figsize=(30,10)) + +# Colorbar - use the same range for each +vmin = -6 +vmax = 0 +# Plot small-signal stability heatmap +sns.heatmap(results, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[1], vmin=vmin, vmax=vmax, cbar=False, cbar_kws = {'label': 'maximum eig. real part'}) +axes[1].set_xlabel("Q_sh (MVAR)") +axes[1].set_ylabel("P_load (MW)") +axes[1].set_title("Pref = Pload") + +## Case 2: Pref = 0.5*Pload +results = np.zeros([n,n]) +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + sys.gfmi_c[0].minimum_active_power_MW = P/2 + sys.gfmi_c[0].maximum_active_power_MW = P/2 + sys.gfmi_c[0].minimum_reactive_power_MVAR = Q + sys.gfmi_c[0].maximum_reactive_power_MVAR = Q + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + sys_modifier.combine_shunts() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + + max_eig = np.max(np.linalg.eigvals(ssm.model.A).real) + + results[i,j] = max_eig + j += 1 + i += 1 + + +sns.heatmap(results, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5, ax=axes[0], vmin=vmin, vmax=vmax, cbar=False, cbar_kws = {'label': 'maximum eig. real part'}) +axes[0].set_xlabel("Q_sh (MVAR)") +axes[0].set_ylabel("P_load (MW)") +axes[0].set_title("Pref = 0.5*Pload") + +## Case 3: Pref = 1.5*Pload +results = np.zeros([n,n]) +i = 0 +for P in Prange: + j = 0 + for Q in Qrange: + sys.gfmi_c[0].minimum_active_power_MW = P*1.5 + sys.gfmi_c[0].maximum_active_power_MW = P*1.5 + sys.gfmi_c[0].minimum_reactive_power_MVAR = Q + sys.gfmi_c[0].maximum_reactive_power_MVAR = Q + + # Run power flow + pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) + pf.solve() + + # Break down lines into branches and shunts for small-signal modeling + sys_modifier = SystemModifier(system=sys) + sys_modifier.decompose_lines() + sys_modifier.combine_shunts() + + # Construct small-signal model + ssm = SmallSignalModel(system=sys) + ssm.construct_system_ssm() + + max_eig = np.max(np.linalg.eigvals(ssm.model.A).real) + + results[i,j] = max_eig + j += 1 + i += 1 + +sns.heatmap(results, xticklabels=Qrange, yticklabels=Prange, linewidth=0.5,ax=axes[2], vmin=vmin, vmax=vmax, cbar=True, cbar_kws = {'label': 'maximum eig. real part'}) +axes[2].set_xlabel("Q_sh (MVAR)") +axes[2].set_ylabel("P_load (MW)") +axes[2].set_title("Pref = 1.5*Pload") +fig.suptitle("Effect of Pref v Pload across P & Q space") + +plt.show() +plt.savefig(str(case_dir)+"/ssm_heatmaps.png") + +print('ok') + \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/ssm_heatmaps.png b/examples/small_signal_and_emt/2-bus_src-gfm/ssm_heatmaps.png new file mode 100644 index 0000000000000000000000000000000000000000..1df24922a5e364cd399881744a18f535990efc52 GIT binary patch literal 55762 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 h;Q#L@+4bj= 0.5 else 0.0 + +def step2(t): + return 0.0 + +inputs = { + 'infinite_sources_0': { + 'v_ref_d': step2 + } + } + +t_max = 2.0 # Simulation length (in seconds) + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model +sys = System.from_csv(case_directory=case_dir) + +# Construct system and small-signal model +_, ssm = main.run_ssm(case_directory=case_dir) +ssm.simulate_ssm(t_max=t_max, inputs=inputs) + +print('\nok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/gfmi_e_v_gfmi_c.py b/examples/small_signal_and_emt/gfmi_e_v_gfmi_c.py new file mode 100644 index 0000000..8f5cb0d --- /dev/null +++ b/examples/small_signal_and_emt/gfmi_e_v_gfmi_c.py @@ -0,0 +1,275 @@ +""" +Compares the EMT and small signal response of the GFMI_C, which does not model the DC side of the converter, +the GFMI_E which models a battery and current-source load on the DC side. + +In progress - March 31 - Ruth +""" + +# Import Python standard and third-party packages +from pathlib import Path +import os +import numpy as np +import pandas as pd +import plotly.graph_objects as go +from plotly.subplots import make_subplots +from scipy.linalg import eig, inv +import seaborn as sns +import matplotlib.pyplot as plt +from sting.utils.transformations import dq02abc, abc2dq0 +from scipy import signal + + +# Import sting package +from sting import main +from sting.system.core import System + +# Specify path of the case study directory + +case_dir_gfmc = os.path.join(Path(__file__).resolve().parent,"2-bus_src-gfm") +case_dir_gfme = os.path.join(Path(__file__).resolve().parent,"2-bus-src-gfmi_e") + +# Define inputs +def step1(t): + return 0.3 if t >= 0.5 else 0.0 + +def step2(t): + return 0.0 + +# Combine inputs for gfmi_c and gfmi_e +inputs = {'infinite_sources_0': {'v_ref_d': step1}, + 'gfmi_e_0': {'p_ref': step2, + 'q_ref': step2, + 'v_ref': step2, + 'v_dc_ref': step2, + 'v_s': step2, + 'i_load_ref': step2}, + 'gfmi_c_0': {'p_ref': step2, + 'q_ref': step2, + 'v_ref': step2}} + +t_max = 2.0 + +# Run GFMI_C +_, ssm_c = main.run_ssm(case_directory=case_dir_gfmc) # Construct system and small-signal model +ssm_c.simulate_ssm(t_max=t_max, inputs=inputs) +sys_c, emt_sc_c = main.run_emt(case_directory=case_dir_gfmc, inputs=inputs, t_max=t_max) # Run EMT simulation + +# Run GFMI_E +_, ssm_e = main.run_ssm(case_directory=case_dir_gfme) # Construct system and small-signal model +ssm_e.simulate_ssm(t_max=t_max, inputs=inputs) +sys_e, emt_sc_e = main.run_emt(case_directory=case_dir_gfme, inputs=inputs, t_max=t_max) # Run EMT simulation + + +# # Compare eigenvalues of A matrix +# gfmic_A = pd.read_csv(case_dir_gfmc+"/outputs/small_signal_model/A.csv") +# gfmie_A = pd.read_csv(case_dir_gfme+"/outputs/small_signal_model/A.csv") + +# gfmie_eigs = np.linalg.eigvals(gfmie_A.iloc[:,1:].to_numpy()) +# gfmic_eigs = np.linalg.eigvals(gfmic_A.iloc[:,1:].to_numpy()) + +# # Plot eigenvalues +# fig = make_subplots( +# rows=1, cols=1, +# horizontal_spacing=0.15, +# vertical_spacing=0.05, +# ) + +# fig.add_trace(go.Scatter(x=gfmie_eigs.real, y=gfmie_eigs.imag, name="GFMI_E", mode="markers", marker=dict(size=12, opacity=1.0, symbol="circle", line=dict(color="black", width=1)))) +# fig.add_trace(go.Scatter(x=gfmic_eigs.real, y=gfmic_eigs.imag, name="GFMI_C", mode="markers", marker=dict(size=12, opacity=0.5, symbol="square", line=dict(color="black", width=1)))) +# fig.show() +# fig.write_html("examples/small_signal_and_emt/2-bus-scr-gfm_comparison/eig_comparison.html") + +# Compare EMT traces +gfmi_c = pd.read_csv(case_dir_gfmc+"/outputs/simulation_emt/gfmi_c_0_states.csv") +gfmi_e = pd.read_csv(case_dir_gfme+"/outputs/simulation_emt/gfmi_e_0_states.csv") + +fig = make_subplots( + rows=9, cols=2, + horizontal_spacing=0.15, + vertical_spacing=0.05, +) + +tps = gfmi_c["time"] + +# converting abc to dq +# GFMI_E +i_bus_de, i_bus_qe, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(gfmi_e["i_bus_a"], gfmi_e["i_bus_b"], gfmi_e["i_bus_c"], gfmi_e["angle_pc"])]) +i_vsc_de, i_vsc_qe, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(gfmi_e["i_vsc_a"], gfmi_e["i_vsc_b"], gfmi_e["i_vsc_c"], gfmi_e["angle_pc"])]) +v_sh_de, v_sh_qe, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(gfmi_e["v_sh_a"], gfmi_e["v_sh_b"], gfmi_e["v_sh_c"], gfmi_e["angle_pc"])]) + +# GFMI_c +i_bus_dc, i_bus_qc, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(gfmi_c["i_bus_a"], gfmi_c["i_bus_b"], gfmi_c["i_bus_c"], gfmi_c["angle_pc"])]) +i_vsc_dc, i_vsc_qc, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(gfmi_c["i_vsc_a"], gfmi_c["i_vsc_b"], gfmi_c["i_vsc_c"], gfmi_c["angle_pc"])]) +v_sh_dc, v_sh_qc, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(gfmi_c["v_sh_a"], gfmi_c["v_sh_b"], gfmi_c["v_sh_c"], gfmi_c["angle_pc"])]) + +r, c = 1,1 +fig.add_trace(go.Scatter(x=tps, y=gfmi_c["angle_pc"], name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["angle_pc"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='angle_pc', row=r, col=c) + +r, c = 1,2 +fig.add_trace(go.Scatter(x=tps, y=gfmi_c["w_pc"], name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["w_pc"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='w_pc', row=r, col=c) + +r, c = 2,1 +fig.add_trace(go.Scatter(x=tps, y=gfmi_c["p_pc"], name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["p_pc"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p_pc', row=r, col=c) + +r, c = 2,2 +fig.add_trace(go.Scatter(x=tps, y=gfmi_c["q_pc"], name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["q_pc"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='q_pc', row=r, col=c) + +r, c = 3,1 +fig.add_trace(go.Scatter(x=tps, y=gfmi_c["gamma"], name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["gamma"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='gamma', row=r, col=c) + +r, c = 4,1 +fig.add_trace(go.Scatter(x=tps, y=i_vsc_dc, name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_vsc_de, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_vsc_d', row=r, col=c) + +r, c = 4,2 +fig.add_trace(go.Scatter(x=tps, y=i_vsc_qc, name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_vsc_qe, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_vsc_q', row=r, col=c) + +r, c = 5,1 +fig.add_trace(go.Scatter(x=tps, y=v_sh_dc, name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=v_sh_de, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='v_sh_d', row=r, col=c) + +r, c = 5,2 +fig.add_trace(go.Scatter(x=tps, y=v_sh_qc, name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=v_sh_qe, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='v_sh_q', row=r, col=c) + +r, c = 6,1 +fig.add_trace(go.Scatter(x=tps, y=i_bus_dc, name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_bus_de, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_bus_d', row=r, col=c) + +r, c = 6,2 +fig.add_trace(go.Scatter(x=tps, y=i_bus_qc, name="GFMI_C", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=r, col=c) +fig.add_trace(go.Scatter(x=tps, y=i_bus_qe, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_bus_q', row=r, col=c) + +r, c = 7,1 +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["i_L"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_L', row=r, col=c) + +r, c = 7,2 +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["v_dc"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='v_dc', row=r, col=c) + +r, c = 8,1 +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["i_load"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='i_load', row=r, col=c) + +r, c = 8,2 +fig.add_trace(go.Scatter(x=tps, y=gfmi_e["soc"], name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='soc', row=r, col=c) + +r, c = 9,1 +p_load = np.multiply(gfmi_e["i_load"],gfmi_e["v_dc"]) +fig.add_trace(go.Scatter(x=tps, y=p_load, name="GFMI_E", mode='lines', line=dict(color='blue', dash='dot'), legendgroup='1'), row=r, col=c) +fig.update_xaxes(title_text='Time [s]', row=r, col=c) +fig.update_yaxes(title_text='p_load', row=r, col=c) + + + +# r, c = 9, 1 +# duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 +# duty_cycle = np.clip(duty_cycle, 0.0, 1.0) +# fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='blue', dash='dot')), +# row=r, col=c) +# fig.update_xaxes(title_text='Time [s]', row=r, col=c) +# fig.update_yaxes(title_text='duty cycle [p.u.]', row=r, col=c) + + + +fig.update_layout(height=1200*2, + width=800*2, + legend_tracegroupgap=400, + showlegend=False, + margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) + + + +fig.show() +fig.write_html("examples/small_signal_and_emt/gfmi_e_vs_gfmi_c_emt_traces.html") + + +## Print nadir/rocof + +# Measure some performance metrics - frequency ROCOF and nadir and steady state error ? +gfm = emt_sc_c.system.gfmi_c[0].variables_emt.x.value +w_pc = gfm[1,:] +# nadir +nadir_c = np.min(w_pc) +tps = np.linspace(0, t_max, 500) +dt = tps[1] - tps[0] + +# rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) +df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof +df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) +rocof_c = np.max(df_dt_ma) + +gfm = emt_sc_e.system.gfmi_e[0].variables_emt.x.value +w_pc = gfm[1,:] +# nadir +nadir_e = np.min(w_pc) +tps = np.linspace(0, t_max, 500) +dt = tps[1] - tps[0] + +# rocof - calculate as a moving average of duration 100ms (~25 timesteps for us) +df_dt = np.abs(np.diff(w_pc)/dt) # calculate raw rocof +df_dt_ma = np.convolve(df_dt,np.ones(25)/25) # get moving averages over 25 time steps (~100ms) +rocof_e = np.max(df_dt_ma) + + + +# participation factor analysis - can turn this into a function / method for SSM class + +# def participation_factor_plot(ssm): +# w,vr = eig(ssm.model.A) +# vl = inv(vr) # ensures normalization + +# p = np.zeros_like(vl) +# for i in range(len(w)): +# for k in range(len(w)): +# # use correct formula for complex eigenvalues +# p[k, i] = (vl[i,k].real)**2/(np.dot(vl[i,:].real,vl[i,:].real)) # participation of k-th state (row) in i-th mode (column) + +# pf_df = pd.DataFrame(p.real, index=ssm.model.x.name) +# ax = sns.heatmap(pf_df, yticklabels=pf_df.index, xticklabels=w.real>0, linewidths=1, linecolor='white') +# ax.set_yticklabels(ax.get_yticklabels(), fontsize=8) +# ax.set_xticklabels(ax.get_xticklabels(), fontsize=8) +# plt.show() +# plt.savefig("ssm.png") + + +# participation_factor_plot(ssm_c) +# participation_factor_plot(ssm_e) + + + +print('ok') diff --git a/sting/generator/gfli_e.py b/sting/generator/gfli_e.py new file mode 100644 index 0000000..d297fa6 --- /dev/null +++ b/sting/generator/gfli_e.py @@ -0,0 +1,740 @@ +""" +This module implements a GFLI that incorporates: +- LCL filter: Two Series RL branches (one branch is the transformer) and one Parallel RC shunt. +- Outer Vdc controller +- Current controller: A dq-based frame PI controller +- PLL: A basic implementation + +in progress - Ruth (April 16) +""" +# ---------------------- +# Import python packages +# ---------------------- +import numpy as np +from dataclasses import dataclass, field +from typing import NamedTuple, Optional, ClassVar +import scipy.linalg + +# ------------------ +# Import sting code +# ------------------ +from sting.utils.dynamical_systems import StateSpaceModel, DynamicalVariables +from sting.utils.transformations import dq02abc, abc2dq0 +from sting.generator.core import Generator + +# ----------- +# Sub-classes +# ----------- +class PowerFlowVariables(NamedTuple): + p_bus: float + q_bus: float + vmag_bus: float + vphase_bus: float + + +class InitialConditionsEMT(NamedTuple): + vmag_bus: float + vphase_bus: float + p_bus: float + q_bus: float + angle_ref: float + pi_cc_d: float + pi_cc_q: float + i_vsc_d: float + i_vsc_q: float + i_bus_d: float + i_bus_q: float + v_lcl_sh_d: float + v_lcl_sh_q: float + i_bus_D: float + i_bus_Q: float + v_bus_D: float + v_bus_Q: float + v_vsc_mag: float + v_vsc_DQ_phase: float + v_vsc_d: float + v_vsc_q: float + p_vsc: float + v_dc: float + int_vdc: float + int_q: float + i_dc: float + i_load: float + + +class VariablesEMT(NamedTuple): + x: DynamicalVariables + u: DynamicalVariables + y: DynamicalVariables + +# ----------- +# Main class +# ----------- +@dataclass(slots=True, kw_only=True, eq=False) +class GFLIe(Generator): + rf1_pu: float + lf1_pu: float + rsh_pu: float + csh_pu: float + txr_power_MVA: float + txr_voltage1_kV: float + txr_voltage2_kV: float + txr_r1_pu: float + txr_l1_pu: float + txr_r2_pu: float + txr_l2_pu: float + beta: float + kp_pll: float + ki_pll: float + kp_cc: float + ki_cc: float + vdc_ref: float # added + i_load_ref: float # added + c_dc: float # added + kp_oc: float # added + ki_oc: float # added + x_pll_rescale: np.ndarray = field(default_factory=lambda: np.array([[100, 0], [0, 1]])) + name: str = field(default_factory=str) + emt_init: Optional[InitialConditionsEMT] = None + ssm: Optional[StateSpaceModel] = None + + @property + def rf2_pu(self): + return (self.txr_r1_pu + self.txr_r2_pu) * self.base_power_MVA / self.txr_power_MVA + + @property + def lf2_pu(self): + return (self.txr_l1_pu + self.txr_l2_pu) * self.base_power_MVA / self.txr_power_MVA + + @property + def wbase(self): + return 2 * np.pi * self.base_frequency_Hz + + def _build_small_signal_model(self): + + # Current PI controller + kp_cc, ki_cc = self.kp_cc, self.ki_cc + pi_cc_d, pi_cc_q = self.emt_init.pi_cc_d, self.emt_init.pi_cc_q + + pi_controller = StateSpaceModel( A = np.zeros((2,2)), + B = ki_cc*np.hstack((np.eye(2), -np.eye(2))), + C = np.eye(2), + D = kp_cc*np.hstack((np.eye(2), -np.eye(2))), + u = DynamicalVariables(name=['i_bus_d_ref', 'i_bus_q_ref', 'i_bus_d', 'i_bus_q']), + y = DynamicalVariables(name=['e_d', 'e_q']), + x = DynamicalVariables( name=['pi_cc_d', 'pi_cc_q'], + init= [pi_cc_d, pi_cc_q]) ) + + # LCL filter + rf1, lf1, rf2, lf2, rsh, csh = self.rf1_pu, self.lf1_pu, self.rf2_pu, self.lf2_pu, self.rsh_pu, self.csh_pu + wb = self.wbase + i_vsc_d, i_vsc_q = self.emt_init.i_vsc_d, self.emt_init.i_vsc_q + i_bus_d, i_bus_q = self.emt_init.i_bus_d, self.emt_init.i_bus_q + v_lcl_sh_d, v_lcl_sh_q = self.emt_init.v_lcl_sh_d, self.emt_init.v_lcl_sh_q + + lcl_filter = StateSpaceModel( + A = wb*np.array([[-rf1/lf1 , 1 , 0 , 0 , -1/lf1 , 0], + [-1 , -rf1/lf1, 0 , 0 , 0 , -1/lf1], + [0 , 0 , -rf2/lf2 , 1 , 1/lf2 , 0], + [0 , 0 , -1 , -rf2/lf2, 0 , 1/lf2], + [1/csh , 0 , -1/csh , 0 , -1/(rsh*csh), 1], + [0 , 1/csh , 0 , -1/csh , -1 , -1/(rsh*csh)]]), + B = wb*np.array([[1/lf1 , 0 , 0 , 0 , i_vsc_q], + [0 , 1/lf1 , 0 , 0 , -i_vsc_d], + [0 , 0 , -1/lf2 , 0 , i_bus_q], + [0 , 0 , 0 , -1/lf2 , -i_bus_d], + [0 , 0 , 0 , 0 , v_lcl_sh_q], + [0 , 0 , 0 , 0 , -v_lcl_sh_d]]), + C = np.eye(6), + D = np.zeros((6,5)), + x = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q", "v_lcl_sh_d", "v_lcl_sh_q"], + init=[i_vsc_d, i_vsc_q, i_bus_d, i_bus_q, v_lcl_sh_d, v_lcl_sh_q]), + u = DynamicalVariables(name=['v_vsc_d', 'v_vsc_q', 'v_bus_d', 'v_bus_q', 'w']), + y = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q", "v_lcl_sh_d", "v_lcl_sh_q"])) + + # Phase-locked loop + kp_pll, ki_pll = self.kp_pll, self.ki_pll + beta = self.beta + vmag_bus = self.emt_init.vmag_bus + sinphi = np.sin(self.emt_init.angle_ref*np.pi/180) + cosphi = np.cos(self.emt_init.angle_ref*np.pi/180) + int_pll = 0 + phase_pll = self.emt_init.angle_ref*np.pi/180 + + pll = StateSpaceModel( A = np.array([ [ 0 , -vmag_bus*ki_pll], + [wb , -wb*vmag_bus*kp_pll]]), + B = np.array([ [-sinphi*ki_pll , +cosphi*ki_pll], + [-wb*kp_pll*sinphi, wb*kp_pll*cosphi]]), + C = np.array([ [0 , 1], + [1 , -1*vmag_bus*kp_pll]]), + D = np.array([ [0 , 0], + [-1*kp_pll*sinphi , 1*kp_pll*cosphi]]), + u = DynamicalVariables(name=['v_bus_D', 'v_bus_Q']), + y = DynamicalVariables(name=['phase', 'w']), + x = DynamicalVariables(name=["int_pll", "phase_pll"], + init=[int_pll, phase_pll] ) ) + + # Re-scale the states so that they are not very small numbers compared to + # other states. It was tested in EMT simulation. + pll.A = self.x_pll_rescale @ pll.A @ scipy.linalg.inv(self.x_pll_rescale) + pll.B = self.x_pll_rescale @ pll.B + pll.C = pll.C @ scipy.linalg.inv(self.x_pll_rescale) + + # Outer control + DC capacitor dynamics + Kp, Ki, Cdc = self.kp_oc, self.ki_oc, self.c_dc + r_dc = 100 + outer_control = StateSpaceModel(A = np.array([[0, 0, Ki], + [0, 0, 0], + [0, 0, -1/r_dc]]), + B = np.array([[-Ki, 0, 0, 0, 0], + [0, 0, -Ki, Ki, 0], + [0, wb/Cdc, 0, 0, -wb/Cdc]]), + C = np.array([[1, 0, Kp], + [0, 1, 0], + [0, 0, 1]]), + D = np.array([[-Kp, 0, 0, 0, 0], + [0, 0, -Kp, Kp, 0], + [0, 0, 0, 0, 0]]), + u = DynamicalVariables(name=["vdc_ref", "i_load_ref", "q_ref", "q", "idc"]), + y = DynamicalVariables(name=["i_bus_d_ref", "i_bus_q_ref", "v_dc"]), + x = DynamicalVariables(name=["int_vdc", "int_q", "v_dc"], + init=[self.emt_init.int_vdc, self.emt_init.int_q, self.emt_init.v_dc])) + + # Construction of CCM matrices + # ustack = F*ystack + G*u + + # ustack = i2cdq, v1cdq, v2cdq, w, v2cdq, vdcref, iload, qref, q, idc (14) + # y_stack = vmd, vmq, i2d, i2q, i1d, i1q, v3d, v3q, theta, w, idref, iqref, vdc (13) - extra 4 + 3 = 7 + # u = vdcref, iload, qref, v2d, v2q + # y = i2dq + + # reactive power linearization coefficients + a1 = -self.emt_init.v_lcl_sh_d + a2 = -self.emt_init.i_bus_q + a3 = self.emt_init.v_lcl_sh_q + a4 = self.emt_init.i_bus_d + + # dc power balance linearization + b1 = self.emt_init.v_vsc_d/self.emt_init.v_dc + b2 = self.emt_init.i_vsc_d/self.emt_init.v_dc + b3 = self.emt_init.v_vsc_q/self.emt_init.v_dc # is this just 0 ? + b4 = self.emt_init.i_vsc_q/self.emt_init.v_dc + b5 = - self.emt_init.i_dc/self.emt_init.v_dc + + # Construction of CCM matrices + Fccm = np.vstack( ( np.hstack((np.zeros((10, )), 1, 0, 0)) ,# i2ref_d + np.hstack((np.zeros((10, )), 0, 1, 0)) , # i2ref_q + np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,7)))), # i2c_dq + [1, 0, 0, 0, 0, -(lf1+lf2), 0, 0, 0, 0, 0, 0, 0], # v1c_d + [0, 1, 0, 0, (lf1+lf2), 0, 0, 0, -beta*vmag_bus, 0, 0, 0, 0], # v1c_q + np.zeros((13, )) , # v2c_d + np.append( np.zeros((8,)) , [-vmag_bus, 0, 0, 0, 0] ), # v2c_q + np.append( np.zeros((9,)) , [1, 0, 0, 0] ), # w + np.zeros((2, 13)), # v2c_dq + np.zeros((3, 13)), #vdcref, iload, qref + np.hstack((np.zeros(4,), [a3, a1, a2, a4], np.zeros(5,))), # q + np.hstack(([b2, b4, b1, b3], np.zeros(8,), [b5])) #idc + ) + ) + + + Gccm = np.vstack(( np.zeros((4, 5)), # i2ref_dq, i2c_dq, + [0, 0, 0, beta*cosphi , beta*sinphi], # v1c_d + [0, 0, 0, -beta*sinphi, beta*cosphi], # v1c_q + [0, 0, 0, cosphi ,sinphi], # v2c_d + [0, 0, 0, -sinphi ,cosphi], # v2c_q + np.zeros((5, )), # w + np.hstack((np.zeros((2,3)), np.eye(2) ) ), # v2dq + np.hstack((np.eye(3), np.zeros((3,2)))), # vdc_ref, iload, q_ref + np.zeros((2,5)) # q, idc + ) + ) + + Hccm = np.vstack(( [ 0, 0 , 0, 0, cosphi , -sinphi, 0, 0, -sinphi*i_bus_d-cosphi*i_bus_q, 0, 0, 0, 0], + [0, 0, 0, 0, sinphi , cosphi , 0, 0, cosphi*i_bus_d-sinphi*i_bus_q, 0, 0, 0, 0] )) + + Lccm = np.zeros((2, 5)) + + components = [pi_controller, lcl_filter, pll, outer_control] + connections = [Fccm, Gccm, Hccm, Lccm] + + # Inputs and outputs + v_bus_D, v_bus_Q= self.emt_init.v_bus_D, self.emt_init.v_bus_Q + u = DynamicalVariables( + name=["vdc_ref", "i_load_ref", "q_ref", "v_bus_D", "v_bus_Q"], + type=["device", "device", "device", "grid", "grid"], + init=[self.vdc_ref, self.emt_init.i_dc, self.emt_init.q_bus, v_bus_D, v_bus_Q]) + + i_bus_D, i_bus_Q= self.emt_init.i_bus_D, self.emt_init.i_bus_Q + y = DynamicalVariables( + name=['i_bus_D', 'i_bus_Q'], + init=[i_bus_D, i_bus_Q]) + + # Generate small-signal model + ssm = StateSpaceModel.from_interconnected(components, connections, u, y, component_label=f"{self.type_}_{self.id}") + + self.ssm = ssm + + def _calculate_emt_initial_conditions(self): + + + # Extract power flow solution + vmag_bus = self.power_flow_variables.vmag_bus + vphase_bus = self.power_flow_variables.vphase_bus + p_bus = self.power_flow_variables.p_bus + q_bus = self.power_flow_variables.q_bus + + # Voltage in the end of the LCL filter + v_bus_DQ = vmag_bus * np.exp(vphase_bus * np.pi / 180 * 1j) + angle_ref = np.angle(v_bus_DQ, deg=True) + + # Current sent from the end of the LCL filter + i_bus_DQ = (p_bus - q_bus * 1j) / np.conjugate(v_bus_DQ) + + # Voltage across the shunt element in the LCL filter + v_lcl_sh_DQ = v_bus_DQ + (self.rf2_pu + self.lf2_pu * 1j) * i_bus_DQ + + # Current flowing through shunt element of LCL filter + i_lcl_sh_DQ = v_lcl_sh_DQ * (self.csh_pu * 1j) + v_lcl_sh_DQ / self.rsh_pu + + # Current sent from the beginning of the LCL filter + i_vsc_DQ = i_bus_DQ + i_lcl_sh_DQ + v_vsc_DQ = v_lcl_sh_DQ + (self.rf1_pu + self.lf1_pu * 1j) * i_vsc_DQ + + # We refer the voltage and currents to the synchronous frames of the + # inverter + v_vsc_dq = v_vsc_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + i_vsc_dq = i_vsc_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + + v_bus_dq = v_bus_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + i_bus_dq = i_bus_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + + v_lcl_sh_dq = v_lcl_sh_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + + # Initial conditions for the integral controller + pi_cc_dq = ( + v_vsc_dq - self.beta * v_bus_dq - 1j * (self.lf1_pu + self.lf2_pu) * i_bus_dq + ) + + + # Initial conditions for outer control / dc + + # DC-side initial conditions + v_dc = self.vdc_ref + p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real # power at converter terminals + i_dc = p_vsc/v_dc + + self.emt_init = InitialConditionsEMT( + vmag_bus=vmag_bus, + vphase_bus=vphase_bus, + p_bus=p_bus, + q_bus=q_bus, + angle_ref=angle_ref, + pi_cc_d=pi_cc_dq.real, + pi_cc_q=pi_cc_dq.imag, + i_vsc_d=i_vsc_dq.real, + i_vsc_q=i_vsc_dq.imag, + i_bus_d=i_bus_dq.real, + i_bus_q=i_bus_dq.imag, + v_lcl_sh_d=v_lcl_sh_dq.real, + v_lcl_sh_q=v_lcl_sh_dq.imag, + i_bus_D=i_bus_DQ.real, + i_bus_Q=i_bus_DQ.imag, + v_bus_D=v_bus_DQ.real, + v_bus_Q=v_bus_DQ.imag, + v_vsc_mag = abs(v_vsc_DQ), + v_vsc_DQ_phase = np.angle(v_vsc_DQ, deg=True), + v_dc=v_dc, + p_vsc=p_vsc, + i_dc=i_dc, + int_vdc=i_bus_dq.real, + int_q=i_bus_dq.imag, + i_load=i_dc, + v_vsc_d=v_vsc_dq.real, + v_vsc_q=v_vsc_dq.imag + + ) + + def define_variables_emt(self): + # States + # ------ + + + + x = DynamicalVariables( + name = [], + component = f"{self.type_}_{self.id}", + init=[] + ) + + # Inputs + # ------ + + # Initial conditions + + u = DynamicalVariables( + name=["vdc_ref", "iload", "q_ref", "v_bus_a", "v_bus_b", "v_bus_c"], + component=f"{self.type_}_{self.id}", + type=["device", "device", "device", "grid", "grid"], + init=[] + ) + + # Outputs + # ------- + + y = DynamicalVariables( + name=["i_bus_a", "i_bus_b", "i_bus_c"], + component=f"{self.type_}_{self.id}", + init=[i_bus_a, i_bus_b, i_bus_c] + ) + + self.variables_emt = VariablesEMT(x=x,u=u,y=y) + + + def get_derivative_state_emt(self): + + # Get state values + # outer control / current control / lcl / pll + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + + # Get input values (external inputs) + vdc_ref, iload, qref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value + + # convert relevant quantities to dq (ibr frame) + v_sh_d, v_sh_q, _ = abc2dq0(v_sh_a, v_sh_b, v_sh_c, angle_pc) # for power controller + i_bus_d, i_bus_q, _ = abc2dq0(i_bus_a, i_bus_b, i_bus_c, angle_pc) # for power controller + i_vsc_d, i_vsc_q, _ = abc2dq0(i_vsc_a, i_vsc_b, i_vsc_c, angle_pc) + + # outer control + id_ref = int_vdc + self.kp_oc*(vdc_ref - v_dc) + iq_ref = int_q + self.kp_oc*(qref - q) + + # current controller + vmd = self.ki_cc*gamma_d + self.kp_cc*(id_ref - i_d) + vmq = self.ki_cc*gamma_q + self.kp_cc*(id_ref - i_d) + + # PLL + + + # Do Q-V droop + v_sh_mag_ref = v_ref - self.droop_q_pu*(q_pc - q_ref) # adjusting voltage reference based on reactive power error (measured at capacitor) + + # NB updating algebraic states! + # Updating converter terminal voltages + v_vsc_d = gamma + self.kp_vc_pu*(v_sh_mag_ref - (v_sh_d**2 + v_sh_q**2)**0.5) # update + v_vsc_q = 0.0 # update + + # # check magnitude wrt v_dc and limit if necessary + # if v_vsc_d > v_dc: # can check only d component because v_vsc_q= 0 + # v_vsc_d = v_dc + + # convert to abc to feed into filter dynamics + v_vsc_a, v_vsc_b, v_vsc_c = dq02abc(v_vsc_d, v_vsc_q, 0, angle_pc) # correct to use this angle? + + # DC/AC power balance + i_dc = (v_vsc_d*i_vsc_d + v_vsc_q*i_vsc_q)/v_dc + + + + # Differential equations + # ---------------------- + + # Power controller: + def outer_loop_dynamics(y, internal_inputs): + """ + PI control of Vdc error to generate id_ref + PI control of Q error to generate iq_ref + """ + int_vdc, int_q, v_dc = y[0], y[1], y[2] + + vdc_ref, iload, qref, idc, q = internal_inputs + + d_int_vdc = self.Ki_oc*(v_dc_ref - v_dc) + d_int_q = self.Ki_oc*(qref - q) + d_v_dc = (-self.wbase/self.cdc)*(idc - iload) + + return [d_int_vdc, d_int_q, d_v_dc] + + + def current_controller_dynamics(y, internal_inputs): + """ + decoupled PI controllers + """ + gamma = y[0] + + id_ref, iq_ref, id, iq = internal_inputs + + d_gamma_d = id_ref - id + d_gamma_q = iq_ref - iq + + return [d_gamma_d, d_gamma_q] + + + def lcl_filter_dynamics(y, internal_inputs): + """ + The LCL filter connects the VSC to the grid. It has three branches: the first branch (RL) connects + the VSC to the shunt element, the second branch is the shunt element (RC), and the third branch (RL) + connects the shunt element to the grid. + """ + + # Definition of states for the ODEs of the LCL filter + i_vsc_a , i_vsc_b, i_vsc_c = y[0], y[1], y[2] # currents flowing out of VSC + v_sh_a, v_sh_b, v_sh_c = y[3], y[4], y[5] # currents flowing through paralell RC shunt + i_bus_a, i_bus_b, i_bus_c = y[6], y[7], y[8] # currents flowing to bus + + # Extract the list of parameters + rf1, xf1, rf2, xf2, rsh, csh = self.rf1_pu, self.xf1_pu, self.rf2_pu, self.xf2_pu, self.rsh_pu, self.csh_pu + wb = self.wbase + r1 = rf1 + r2 = rf2 + x1 = xf1 + x2 = xf2 + + # Inputs + v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c = internal_inputs + + # Define ODEs that describe the dynamics of the LCL filter + di_vsc_a = wb/x1 *(v_vsc_a - v_sh_a - r1 * i_vsc_a) + di_vsc_b = wb/x1 *(v_vsc_b - v_sh_b - r1 * i_vsc_b) + di_vsc_c = wb/x1 *(v_vsc_c - v_sh_c - r1 * i_vsc_c) + + dv_sh_a = wb/csh * (-v_sh_a/rsh + i_vsc_a - i_bus_a) + dv_sh_b = wb/csh * (-v_sh_b/rsh + i_vsc_b - i_bus_b) + dv_sh_c = wb/csh * (-v_sh_c/rsh + i_vsc_c - i_bus_c) + + di_bus_a = wb/x2 *(v_sh_a - v_bus_a - r2 * i_bus_a) + di_bus_b = wb/x2 *(v_sh_b - v_bus_b - r2 * i_bus_b) + di_bus_c = wb/x2 *(v_sh_c - v_bus_c - r2 * i_bus_c) + + return [di_vsc_a, di_vsc_b, di_vsc_c, dv_sh_a, dv_sh_b, dv_sh_c, di_bus_a, di_bus_b, di_bus_c] + + + def pll_dynamics(y, internal_inputs): + """ + """ + + int_pll, theta = y[0], y[1] + + v_bus_q = internal_inputs + + d_int_pll = self.ki_pll*(v_bus_q) + d_theta = self.wbase*self.ki_pll*int_pll - self.wbase*vm*self.kp_pll*theta + + return [d_int_pll, d_theta] + + + dy_pc = outer_loop_dynamics([angle_pc, w_pc, p_pc, q_pc], [v_sh_d, v_sh_q, i_bus_d, i_bus_q, p_ref]) + dy_vc = current_controller_dynamics([gamma], [v_sh_mag_ref, v_sh_d, v_sh_q]) + dy_lcl = lcl_filter_dynamics([i_vsc_a , i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c], [v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c]) + dy_pll = pll_dynamics([int_pll, theta], [v_sh_a, v_sh_b, v_sh_c]) + + return np.hstack([dy_pc, dy_vc, dy_lcl]) + + + def get_output_emt(self): + + # Output is i_bus_abc + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + + return [i_bus_a, i_bus_b, i_bus_c] + + def plot_results_emt(self, output_dir): + + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + + #i_L = np.clip(i_L, -1000.0, 1.0) + + tps = self.variables_emt.x.time + + # Transform abc to dq0 + i_vsc_d, i_vsc_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_vsc_a, i_vsc_b, i_vsc_c, angle_pc)]) + v_sh_d, v_sh_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, angle_pc)]) + i_bus_d, i_bus_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, angle_pc)]) + + # calculate v_vsc + v_sh_dq = v_sh_d + np.multiply(v_sh_q, 1j) + i_vsc_dq = i_vsc_d + np.multiply(i_vsc_q, 1j) + v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.xf1_pu * 1j), i_vsc_dq) + + fig = make_subplots( + rows=14, cols=2, + horizontal_spacing=0.15, + vertical_spacing=0.05, + ) + + fig.add_trace(go.Scatter(x=tps, y=w_pc, mode='lines', line=dict(color='red', dash='solid')), + row=1, col=1) + fig.update_xaxes(title_text='Time [s]', row=1, col=1) + fig.update_yaxes(title_text='Frequency pc [p.u.]', row=1, col=1) + + fig.add_trace(go.Scatter(x=tps, y=angle_pc * 180 / np.pi, mode='lines', line=dict(color='red', dash='solid')), + row=1, col=2) + fig.update_xaxes(title_text='Time [s]', row=1, col=2) + fig.update_yaxes(title_text='Angle pc [deg]', row=1, col=2) + + fig.add_trace(go.Scatter(x=tps, y=p_pc, name="p_pc", mode='lines', line=dict(color='red', dash='solid')), + row=2, col=1) + fig.update_xaxes(title_text='Time [s]', row=2, col=1) + fig.update_yaxes(title_text='Active Power pc [p.u.]', row=2, col=1) + + fig.add_trace(go.Scatter(x=tps, y=q_pc, mode='lines', line=dict(color='red', dash='solid')), + row=2, col=2) + fig.update_xaxes(title_text='Time [s]', row=2, col=2) + fig.update_yaxes(title_text='Reactive Power pc [p.u.]', row=2, col=2) + + fig.add_trace(go.Scatter(x=tps, y=gamma, mode='lines', line=dict(color='red', dash='solid')), + row=3, col=1) + fig.update_xaxes(title_text='Time [s]', row=3, col=1) + fig.update_yaxes(title_text='Gamma [p.u.]', row=3, col=1) + + fig.add_trace(go.Scatter(x=tps, y=i_loadf, mode='lines', line=dict(color='red', dash='solid')), + row=3, col=2) + fig.update_xaxes(title_text='Time [s]', row=3, col=2) + fig.update_yaxes(title_text='iload_f [p.u.]', row=3, col=2) + + fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, mode='lines', line=dict(color='red', dash='solid')), + row=4, col=1) + fig.update_xaxes(title_text='Time [s]', row=4, col=1) + fig.update_yaxes(title_text='i_vsc_d [p.u.]', row=4, col=1) + + fig.add_trace(go.Scatter(x=tps, y=i_vsc_q, mode='lines', line=dict(color='red', dash='solid')), + row=4, col=2) + fig.update_xaxes(title_text='Time [s]', row=4, col=2) + fig.update_yaxes(title_text='i_vsc_q [p.u.]', row=4, col=2) + + fig.add_trace(go.Scatter(x=tps, y=v_sh_d, mode='lines', line=dict(color='red', dash='solid')), + row=5, col=1) + fig.update_xaxes(title_text='Time [s]', row=5, col=1) + fig.update_yaxes(title_text='v_sh_d [p.u.]', row=5, col=1) + + fig.add_trace(go.Scatter(x=tps, y=v_sh_q, mode='lines', line=dict(color='red', dash='solid')), + row=5, col=2) + fig.update_xaxes(title_text='Time [s]', row=5, col=2) + fig.update_yaxes(title_text='v_sh_q [p.u.]', row=5, col=2) + + fig.add_trace(go.Scatter(x=tps, y=i_bus_d, mode='lines', line=dict(color='red', dash='solid')), + row=6, col=1) + fig.update_xaxes(title_text='Time [s]', row=6, col=1) + fig.update_yaxes(title_text='i_bus_d [p.u.]', row=6, col=1) + + fig.add_trace(go.Scatter(x=tps, y=i_bus_q, mode='lines', line=dict(color='red', dash='solid')), + row=6, col=2) + fig.update_xaxes(title_text='Time [s]', row=6, col=2) + fig.update_yaxes(title_text='i_bus_q [p.u.]', row=6, col=2) + + fig.add_trace(go.Scatter(x=tps, y=v_dc, mode='lines', line=dict(color='red', dash='solid')), + row=7, col=1) + fig.update_xaxes(title_text='Time [s]', row=7, col=1) + fig.update_yaxes(title_text='v_dc [p.u.]', row=7, col=1) + + fig.add_trace(go.Scatter(x=tps, y=i_L, mode='lines', line=dict(color='red', dash='solid')), + row=7, col=2) + fig.update_xaxes(title_text='Time [s]', row=7, col=2) + fig.update_yaxes(title_text='i_L [p.u.]', row=7, col=2) + + + fig.add_trace(go.Scatter(x=tps, y=v_dcf, mode='lines', line=dict(color='red', dash='solid')), + row=8, col=1) + fig.update_xaxes(title_text='Time [s]', row=8, col=1) + fig.update_yaxes(title_text='v_dcf [p.u.]', row=8, col=1) + + fig.add_trace(go.Scatter(x=tps, y=i_Lf, mode='lines', line=dict(color='red', dash='solid')), + row=8, col=2) + fig.update_xaxes(title_text='Time [s]', row=8, col=2) + fig.update_yaxes(title_text='i_Lf [p.u.]', row=8, col=2) + + + fig.add_trace(go.Scatter(x=tps, y=i_dcf, mode='lines', line=dict(color='red', dash='solid')), + row=9, col=1) + fig.update_xaxes(title_text='Time [s]', row=9, col=1) + fig.update_yaxes(title_text='i_dcf [p.u.]', row=9, col=1) + + fig.add_trace(go.Scatter(x=tps, y=x1, mode='lines', line=dict(color='red', dash='solid')), + row=9, col=2) + fig.update_xaxes(title_text='Time [s]', row=9, col=2) + fig.update_yaxes(title_text='x1 [p.u.]', row=9, col=2) + + fig.add_trace(go.Scatter(x=tps, y=x2, mode='lines', line=dict(color='red', dash='solid')), + row=10, col=1) + fig.update_xaxes(title_text='Time [s]', row=10, col=1) + fig.update_yaxes(title_text='x2 [p.u.]', row=10, col=1) + + fig.add_trace(go.Scatter(x=tps, y=i_load, mode='lines', line=dict(color='red', dash='solid')), + row=10, col=2) + fig.update_xaxes(title_text='Time [s]', row=10, col=2) + fig.update_yaxes(title_text='i_load [p.u.]', row=10, col=2) + + # power comparisons (calculated) + p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real + p_load = i_load*v_dc + p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value + p_bat = i_L*v_s + p_capacitor = p_vsc + p_load - p_bat + fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid')), + row=11, col=1) + fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid')), + row=11, col=1) + fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid')), + row=11, col=1) + fig.add_trace(go.Scatter(x=tps, y=p_capacitor, name="p_cap", mode='lines', line=dict(color='pink', dash='solid')), + row=11, col=1) + + fig.add_trace(go.Scatter(x=tps, y=np.ones_like(p_bat)*self.Pbat_max_pu, name="p_bat_lim", mode='lines', line=dict(color='green', dash='dot')), + row=11, col=1) + + fig.update_xaxes(title_text='Time [s]', row=11, col=1) + fig.update_yaxes(title_text='p [p.u.]', row=11, col=1) + + # v_vsc (calculated) + fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.real, mode='lines', line=dict(color='red', dash='solid')), + row=12, col=1) + fig.update_xaxes(title_text='Time [s]', row=12, col=1) + fig.update_yaxes(title_text='v_vsc_d [p.u.]', row=12, col=1) + + fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.imag, mode='lines', line=dict(color='red', dash='solid')), + row=12, col=2) + fig.update_xaxes(title_text='Time [s]', row=12, col=2) + fig.update_yaxes(title_text='v_vsc_q [p.u.]', row=12, col=2) + + e_cap = 0.5*self.c_dc*(v_dc**2) + e_ind = 0.5*self.l_dc*(i_L**2) + fig.add_trace(go.Scatter(x=tps, y=soc, name='battery soc', mode='lines', line=dict(color='red', dash='solid')), + row=13, col=1) + fig.add_trace(go.Scatter(x=tps, y=e_cap, name='cap energy', mode='lines', line=dict(color='blue', dash='dot')), + row=13, col=1) + fig.add_trace(go.Scatter(x=tps, y=e_ind, name='ind energy', mode='lines', line=dict(color='pink', dash='dot')), + row=13, col=1) + + fig.add_trace(go.Scatter(x=tps, y=np.ones_like(soc)*self.SOC_max_pu, name="soc lim", mode='lines', line=dict(color='green', dash='dot')), + row=13, col=1) + fig.update_xaxes(title_text='Time [s]', row=13, col=1) + fig.update_yaxes(title_text='energy [p.u.]', row=13, col=1) + + duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 + duty_cycle = np.clip(duty_cycle, 0.0, 1.0) + fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='red', dash='solid')), + row=13, col=2) + fig.update_xaxes(title_text='Time [s]', row=13, col=2) + fig.update_yaxes(title_text='duty cycle [p.u.]', row=13, col=2) + + fig.add_trace(go.Scatter(x=tps, y=x3, mode='lines', line=dict(color='red', dash='solid')), + row=14, col=1) + fig.update_xaxes(title_text='Time [s]', row=14, col=1) + fig.update_yaxes(title_text='x3 [p.u.]', row=14, col=1) + + + name = f"{self.type_}_{self.id}" + fig.update_layout( title_text = name, + title_x=0.5, + showlegend = False, + ) + + fig.update_layout(height=1200*2, + width=800*2, + showlegend=False, + margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) + + fig.write_html(os.path.join(output_dir, name + ".html")) + + + \ No newline at end of file diff --git a/sting/generator/gfmi_e.py b/sting/generator/gfmi_e.py index 99cb9db..7a06f9f 100644 --- a/sting/generator/gfmi_e.py +++ b/sting/generator/gfmi_e.py @@ -113,12 +113,12 @@ class GFMIe(Generator): Ti_load: float # for DC/DC controller - measurement filter Tload: float # time constant for actuation of load current change i_load_ref: float - #bus_id: int = None + Pbat_max_pu: float # maximum power capacity of battery (pu by S) + SOC_max_pu: float # maximum energy capacity of battery (pu by S) + SOC_init_pu: float # initial battery state of charge (pu) name: str = field(default_factory=str) - #pf: Optional[Power_flow_variables] = None emt_init: Optional[InitialConditionsEMT] = None ssm: Optional[StateSpaceModel] = None - #tags: ClassVar[list[str]] = ["generator"] @property def rf2_pu(self): @@ -470,9 +470,9 @@ def define_variables_emt(self): v_dc, i_dc, i_L, x1, x2, i_load = self.emt_init.v_dc, self.emt_init.i_dc, self.emt_init.i_L, self.emt_init.x_1, self.emt_init.x_2, self.emt_init.i_load x = DynamicalVariables( - name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_a", "i_vsc_b","i_vsc_c", "v_sh_a", "v_sh_b","v_sh_c", "i_bus_a", "i_bus_b", "i_bus_c", 'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load'], + name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_a", "i_vsc_b","i_vsc_c", "v_sh_a", "v_sh_b","v_sh_c", "i_bus_a", "i_bus_b", "i_bus_c", 'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load', 'soc', 'x3'], component = f"{self.type_}_{self.id}", - init=[angle_ref*np.pi/180, 1.0, p_ref, q_ref, v_vsc_d, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_L, v_dc, i_dc, i_load, x1, x2, i_L, v_dc, i_load] + init=[angle_ref*np.pi/180, 1.0, p_ref, q_ref, v_vsc_d, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_L, v_dc, i_dc, i_load, x1, x2, i_L, v_dc, i_load, self.SOC_init_pu, 0.0] ) # Inputs @@ -504,7 +504,7 @@ def define_variables_emt(self): def get_derivative_state_emt(self): # Get state values - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load = self.variables_emt.x.value + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value # Get input values (external inputs) p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value @@ -515,20 +515,24 @@ def get_derivative_state_emt(self): i_vsc_d, i_vsc_q, _ = abc2dq0(i_vsc_a, i_vsc_b, i_vsc_c, angle_pc) # Do Q-V droop - v_sh_mag_ref = v_ref - self.droop_q_pu*(q_pc - q_ref) # droop on error from ref + v_sh_mag_ref = v_ref - self.droop_q_pu*(q_pc - q_ref) # adjusting voltage reference based on reactive power error (measured at capacitor) # NB updating algebraic states! + # Updating converter terminal voltages v_vsc_d = gamma + self.kp_vc_pu*(v_sh_mag_ref - (v_sh_d**2 + v_sh_q**2)**0.5) # update v_vsc_q = 0.0 # update + # # check magnitude wrt v_dc and limit if necessary + # if v_vsc_d > v_dc: # can check only d component because v_vsc_q= 0 + # v_vsc_d = v_dc + # convert to abc to feed into filter dynamics v_vsc_a, v_vsc_b, v_vsc_c = dq02abc(v_vsc_d, v_vsc_q, 0, angle_pc) # correct to use this angle? - # power balance + # DC/AC power balance i_dc = (v_vsc_d*i_vsc_d + v_vsc_q*i_vsc_q)/v_dc - # abc->dq conversions # Differential equations # ---------------------- @@ -623,7 +627,7 @@ def dc_side(y, internal_inputs): DC-DC controller + circuit + load control """ # Define states - i_Lf, v_dcf, i_dcf, i_loadf, x_1, x_2, i_L, v_dc, i_load = y[0], y[1], y[2], y[3], y[4], y[5], y[6], y[7], y[8] + i_Lf, v_dcf, i_dcf, i_loadf, x_1, x_2, i_L, v_dc, i_load, soc = y[0], y[1], y[2], y[3], y[4], y[5], y[6], y[7], y[8], y[9] # Inputs i_dc, i_load_ref, v_dc_ref, v_s = internal_inputs @@ -632,6 +636,11 @@ def dc_side(y, internal_inputs): l_dc, c_dc, TiL, Tvdc, Tidc, Kp_vdc, Ki_vdc, Kp_iL, Ki_iL, Kff_idc, Kff_iload, Ti_load, Tload = self.l_dc, self.c_dc, self.Ti_L, self.Tv_dc, self.Ti_dc, self.kp_v_dc, self.ki_v_dc, self.kp_i_L, self.ki_i_L, self.Kff_idc, self.Kff_iload, self.Ti_load, self.Tload wb = self.wbase + # # DC side limits + # # P_battery = i_L*v_s + # if i_L > 1.0: + # i_L = 1.0 + # ODEs # DC-DC controller @@ -643,6 +652,14 @@ def dc_side(y, internal_inputs): d_x_2 = Ki_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) duty_cycle = Kp_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) + x_2 + # # limit duty cycle + # if duty_cycle > 0.9: + # duty_cycle = 0.9 + + d_soc = i_L*v_s + # soc controller + d_x3 = (soc - self.SOC_init_pu) + # Circuit equations d_v_dc = (wb/c_dc)*(-i_dc - i_load + (1-duty_cycle)*i_L) d_i_L = (wb/l_dc)*(v_s - (1-duty_cycle)*v_dc) @@ -650,14 +667,13 @@ def dc_side(y, internal_inputs): # Load control d_i_load = (1/Tload)*(i_load_ref - i_load) - return [d_i_Lf, d_v_dcf, d_i_dcf, d_i_load_f, d_x_1, d_x_2, d_i_L, d_v_dc, d_i_load] - #return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + return [d_i_Lf, d_v_dcf, d_i_dcf, d_i_load_f, d_x_1, d_x_2, d_i_L, d_v_dc, d_i_load, d_soc, d_x3] dy_pc = power_controller_dynamics([angle_pc, w_pc, p_pc, q_pc], [v_sh_d, v_sh_q, i_bus_d, i_bus_q, p_ref]) dy_vc = voltage_controller_dynamics([gamma], [v_sh_mag_ref, v_sh_d, v_sh_q]) dy_lcl = lcl_filter_dynamics([i_vsc_a , i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c], [v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c]) - dy_dc_side = dc_side([i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load], [i_dc, i_load_ref, v_dc_ref, v_s]) + dy_dc_side = dc_side([i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3], [i_dc, i_load_ref, v_dc_ref, v_s]) return np.hstack([dy_pc, dy_vc, dy_lcl, dy_dc_side]) @@ -665,13 +681,15 @@ def dc_side(y, internal_inputs): def get_output_emt(self): # Output is i_bus_abc - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load = self.variables_emt.x.value + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value return [i_bus_a, i_bus_b, i_bus_c] def plot_results_emt(self, output_dir): - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load = self.variables_emt.x.value + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + + #i_L = np.clip(i_L, -1000.0, 1.0) tps = self.variables_emt.x.time @@ -686,9 +704,7 @@ def plot_results_emt(self, output_dir): v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.xf1_pu * 1j), i_vsc_dq) fig = make_subplots( - rows=12, cols=2, - horizontal_spacing=0.15, - vertical_spacing=0.05, + rows=14, cols=2 ) fig.add_trace(go.Scatter(x=tps, y=w_pc, mode='lines', line=dict(color='red', dash='solid')), @@ -721,8 +737,6 @@ def plot_results_emt(self, output_dir): fig.update_xaxes(title_text='Time [s]', row=3, col=2) fig.update_yaxes(title_text='iload_f [p.u.]', row=3, col=2) - - fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, mode='lines', line=dict(color='red', dash='solid')), row=4, col=1) fig.update_xaxes(title_text='Time [s]', row=4, col=1) @@ -800,16 +814,20 @@ def plot_results_emt(self, output_dir): p_load = i_load*v_dc p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value p_bat = i_L*v_s - fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid')), + p_capacitor = p_vsc + p_load - p_bat + fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid')), + fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid'), legendgroup='1'), row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid')), + fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid'), legendgroup='1'), + row=11, col=1) + fig.add_trace(go.Scatter(x=tps, y=p_capacitor, name="p_cap", mode='lines', line=dict(color='pink', dash='solid'), legendgroup='1'), + row=11, col=1) + fig.add_trace(go.Scatter(x=tps, y=np.ones_like(p_bat)*self.Pbat_max_pu, name="p_bat_lim", mode='lines', line=dict(color='green', dash='dot'), legendgroup='1'), row=11, col=1) - fig.update_xaxes(title_text='Time [s]', row=11, col=1) - fig.update_yaxes(title_text='p [p.u.]', row=11, col=1) + fig.update_yaxes(title_text='power [p.u.]', row=11, col=1) # v_vsc (calculated) fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.real, mode='lines', line=dict(color='red', dash='solid')), @@ -822,6 +840,32 @@ def plot_results_emt(self, output_dir): fig.update_xaxes(title_text='Time [s]', row=12, col=2) fig.update_yaxes(title_text='v_vsc_q [p.u.]', row=12, col=2) + e_cap = 0.5*self.c_dc*(v_dc**2) + e_ind = 0.5*self.l_dc*(i_L**2) + fig.add_trace(go.Scatter(x=tps, y=soc, name='battery soc', mode='lines', line=dict(color='red', dash='solid')), + row=13, col=1) + fig.add_trace(go.Scatter(x=tps, y=e_cap, name='cap energy', mode='lines', line=dict(color='blue', dash='dot')), + row=13, col=1) + fig.add_trace(go.Scatter(x=tps, y=e_ind, name='ind energy', mode='lines', line=dict(color='pink', dash='dot')), + row=13, col=1) + + fig.add_trace(go.Scatter(x=tps, y=np.ones_like(soc)*self.SOC_max_pu, name="soc lim", mode='lines', line=dict(color='green', dash='dot')), + row=13, col=1) + fig.update_xaxes(title_text='Time [s]', row=13, col=1) + fig.update_yaxes(title_text='energy [p.u.]', row=13, col=1) + + duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 + duty_cycle = np.clip(duty_cycle, 0.0, 1.0) + fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='red', dash='solid')), + row=13, col=2) + fig.update_xaxes(title_text='Time [s]', row=13, col=2) + fig.update_yaxes(title_text='duty cycle [p.u.]', row=13, col=2) + + fig.add_trace(go.Scatter(x=tps, y=x3, mode='lines', line=dict(color='red', dash='solid')), + row=14, col=1) + fig.update_xaxes(title_text='Time [s]', row=14, col=1) + fig.update_yaxes(title_text='x3 [p.u.]', row=14, col=1) + name = f"{self.type_}_{self.id}" fig.update_layout( title_text = name, @@ -829,9 +873,9 @@ def plot_results_emt(self, output_dir): showlegend = False, ) - fig.update_layout(height=1200*2, + fig.update_layout(height=1200*3, width=800*2, - showlegend=False, + showlegend=True, margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) fig.write_html(os.path.join(output_dir, name + ".html")) From d921a6ad28b9e452021e85ea27c529454204b00b Mon Sep 17 00:00:00 2001 From: rkravis Date: Mon, 11 May 2026 10:47:48 -0700 Subject: [PATCH 2/6] added: gfli_e + example --- .../2-bus-src-gfli_e/inputs/gfli_e.csv | 4 +- .../inputs/infinite_sources.csv | 2 +- .../2-bus-src-gfli_e/inputs/lines.csv | 2 +- .../active_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/bus_voltage.csv | 4 +- .../outputs/ac_power_flow/costs_summary.csv | 2 +- .../ac_power_flow/generator_dispatch.csv | 4 +- .../outputs/ac_power_flow/line_flows.csv | 2 +- .../outputs/ac_power_flow/load_shedding.csv | 4 +- .../reactive_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/solver_status.csv | 2 +- .../component_connection_matrices/F.csv | 4 +- .../component_connection_matrices/G.csv | 6 +- .../component_connection_matrices/L.csv | 2 +- .../2-bus-src-gfli_e/run.py | 35 +- sting/generator/gfli_e.py | 584 +++++++----------- sting/system/core.py | 2 + 17 files changed, 265 insertions(+), 402 deletions(-) diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv index 491615b..8bdc652 100755 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv @@ -1,2 +1,2 @@ -name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,lf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_l1_pu,txr_r2_pu,txr_l2_pu,beta,kp_pll,ki_pll,kp_cc,ki_cc,vdc_ref,i_load_ref,c_dc,kp_oc,ki_oc -solar,santiago,10,10,0,0,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0,1,10,1,10,2,0,20,1,10 \ No newline at end of file +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,lf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_l1_pu,txr_r2_pu,txr_l2_pu,beta,kp_pll_pu,ki_pll_puHz,kp_cc_pu,ki_cc_puHz,v_dc_ref,c_dc,kp_oc_pu,ki_oc_puHz,Tload,r_dc +solar,santiago,50,50,-10,10,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0,1,5,1,5,1,20,0.01,0,0.01,10 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv index 9d95931..fed2eb7 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv @@ -1,2 +1,2 @@ name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,r_pu,x_pu -gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.5 \ No newline at end of file +gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.04 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv index 74a86b2..b74276c 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv @@ -1,2 +1,2 @@ name,from_bus,to_bus,base_power_MVA,base_voltage_kV,base_frequency_Hz,r_pu,x_pu,g_pu,b_pu -tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.01,0.5,0.05,0.066666667 \ No newline at end of file +tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.01,0.2,0.05,0.066666667 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv index bc24fde..6c7977c 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,timepoint_1,-0.058375056098752195,-9.974940964399896e-9,0.0,-0.05837506532163607 -santiago,timepoint_1,10.0,-9.974940964460538e-9,0.0,9.999999989460255 +lima,timepoint_1,-39.80828383129473,-9.974940963696508e-9,0.0,-39.808283840763764 +santiago,timepoint_1,50.0,-9.97494096379835e-9,0.0,49.999999989909305 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv index fa0fc34..66021f4 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv @@ -1,3 +1,3 @@ id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg -0,lima,timepoint_1,0.9795934248147871,0.0 -1,santiago,timepoint_1,1.013581804636969,1.3637928689337457 +0,lima,timepoint_1,0.9943823157326167,0.0 +1,santiago,timepoint_1,1.0045025151823652,5.136441290604731 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv index 940fe42..6ef2ea8 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv @@ -1,2 +1,2 @@ component,cost -total_cost_USD,-0.00003989976383138582 +total_cost_USD,-0.00003989976385267742 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv index 80a132f..9a20949 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv @@ -1,3 +1,3 @@ id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,timepoint_1,-0.058375056098752195,-12.902934347861217 -0,gfli_e,solar,timepoint_1,10.0,0.0 +0,infinite_sources,gen1,timepoint_1,-39.80828383129473,-7.380487609970588 +0,gfli_e,solar,timepoint_1,50.0,-1.8861486979909645 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv index 2ffcc27..8f4a02e 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv @@ -1,2 +1,2 @@ line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,-0.05837506532163335,-12.902934358094463,9.999999989460255,-9.95750355277473e-9,9.94162492413862,-12.902934368051966 +tx_1,lima,santiago,inf,-39.80828384076382,-7.380487617787122,49.9999999899092,-1.8861487031750395,10.191716149145378,-9.266636320962162 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv index 529532a..ffef917 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv @@ -1,3 +1,3 @@ bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,timepoint_1,-9.974940964399896e-9,-9.97494096436451e-9 -santiago,timepoint_1,-9.974940964460538e-9,-9.974940938160877e-9 +lima,timepoint_1,-9.974940963696508e-9,-9.974940964376057e-9 +santiago,timepoint_1,-9.97494096379835e-9,-9.974940960806504e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv index 961177c..fb89795 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,timepoint_1,-12.902934347861217,-9.97494096436451e-9,0.0,-12.90293435809444 -santiago,timepoint_1,0.0,-9.974940938160877e-9,0.0,-9.95750355277473e-9 +lima,timepoint_1,-7.380487609970588,-9.974940964376057e-9,0.0,-7.380487617787122 +santiago,timepoint_1,-1.8861486979909645,-9.974940960806504e-9,0.0,-1.8861487031749502 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv index 1402c84..61ad3cd 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.10544800758361816 +time_spent_seconds,0.06075906753540039 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv index 85259ac..25ec4a0 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv @@ -3,9 +3,9 @@ Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","( "('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'vdc_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'v_dc_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfli_e_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'q_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_e_0', 'i_bus_q_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfli_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 "('gfli_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 "('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv index 1beb4b5..c46e286 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv @@ -1,11 +1,11 @@ -Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'vdc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'q_ref')" +Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'v_dc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'i_bus_q_ref')" "('infinite_sources_0', 'v_ref_d')",1.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_ref_q')",0.0,1.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'vdc_ref')",0.0,0.0,1.0,0.0,0.0 +"('gfli_e_0', 'v_dc_ref')",0.0,0.0,1.0,0.0,0.0 "('gfli_e_0', 'i_load_ref')",0.0,0.0,0.0,1.0,0.0 -"('gfli_e_0', 'q_ref')",0.0,0.0,0.0,0.0,1.0 +"('gfli_e_0', 'i_bus_q_ref')",0.0,0.0,0.0,0.0,1.0 "('gfli_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 "('gfli_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 "('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv index e6014c3..95c81c6 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv @@ -1,4 +1,4 @@ -Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'vdc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'q_ref')" +Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'v_dc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'i_bus_q_ref')" "('infinite_sources_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 "('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 "('gfli_e_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py index 8dcb018..9bb1cf4 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py @@ -1,28 +1,52 @@ # Import Python standard and third-party packages from pathlib import Path - +from scipy.linalg import eig, inv +import matplotlib.pyplot as plt +import sys +import seaborn as sns +import pandas as pd +import numpy as np +import os +from plotly.subplots import make_subplots +import plotly.graph_objects as go + +sys.path.append("/Users/ruthkravis/Documents/STING") # Import sting package from sting import main from sting.system.core import System +from scipy import signal + + # Step-change input to applied to the system def step1(t): - return 0.1 if t >= 0.5 else 0.0 + return 0.2 if t >= 0.2 else 0.0 def step2(t): return 0.0 +def step3(t): + return 0.1 if t >= 1.0 else 0.0 + + +def sin_oscillation(t): + return 0.05*np.sin(2*np.pi*20*t) if t < 1 else 0 #1 Hz oscillation + +def square_oscillation(t): + osc = 0.05*signal.square(2 * np.pi * 14 * t) + return osc + inputs = { 'infinite_sources_0': { - 'v_ref_d': step2 + 'v_ref_d': step1 }, 'gfli_e_0': { - 'i_load_ref': step1 + 'i_load_ref': step2 } } -t_max = 2.0 # Simulation length (in seconds) +t_max = 1.0 # Simulation length (in seconds) # Specify path of the case study directory case_dir = Path(__file__).resolve().parent @@ -33,5 +57,6 @@ def step2(t): # Construct system and small-signal model _, ssm = main.run_ssm(case_directory=case_dir) ssm.simulate_ssm(t_max=t_max, inputs=inputs) +main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) print('\nok') \ No newline at end of file diff --git a/sting/generator/gfli_e.py b/sting/generator/gfli_e.py index d297fa6..26ce334 100644 --- a/sting/generator/gfli_e.py +++ b/sting/generator/gfli_e.py @@ -1,18 +1,18 @@ """ This module implements a GFLI that incorporates: - LCL filter: Two Series RL branches (one branch is the transformer) and one Parallel RC shunt. -- Outer Vdc controller +- Outer DC voltage controller +- DC-side capacitor dynamics with resistor and current source representing a load - Current controller: A dq-based frame PI controller - PLL: A basic implementation -in progress - Ruth (April 16) """ # ---------------------- # Import python packages # ---------------------- import numpy as np from dataclasses import dataclass, field -from typing import NamedTuple, Optional, ClassVar +from typing import NamedTuple, Optional import scipy.linalg # ------------------ @@ -57,9 +57,8 @@ class InitialConditionsEMT(NamedTuple): p_vsc: float v_dc: float int_vdc: float - int_q: float i_dc: float - i_load: float + i_load_ref: float class VariablesEMT(NamedTuple): @@ -84,15 +83,16 @@ class GFLIe(Generator): txr_r2_pu: float txr_l2_pu: float beta: float - kp_pll: float - ki_pll: float - kp_cc: float - ki_cc: float - vdc_ref: float # added - i_load_ref: float # added + kp_pll_pu: float + ki_pll_puHz: float + kp_cc_pu: float + ki_cc_puHz: float + v_dc_ref: float # added c_dc: float # added - kp_oc: float # added - ki_oc: float # added + kp_oc_pu: float # added + ki_oc_puHz: float # added + Tload: float # added + r_dc: float # added x_pll_rescale: np.ndarray = field(default_factory=lambda: np.array([[100, 0], [0, 1]])) name: str = field(default_factory=str) emt_init: Optional[InitialConditionsEMT] = None @@ -113,7 +113,7 @@ def wbase(self): def _build_small_signal_model(self): # Current PI controller - kp_cc, ki_cc = self.kp_cc, self.ki_cc + kp_cc, ki_cc = self.kp_cc_pu, self.ki_cc_puHz pi_cc_d, pi_cc_q = self.emt_init.pi_cc_d, self.emt_init.pi_cc_q pi_controller = StateSpaceModel( A = np.zeros((2,2)), @@ -153,7 +153,7 @@ def _build_small_signal_model(self): y = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q", "v_lcl_sh_d", "v_lcl_sh_q"])) # Phase-locked loop - kp_pll, ki_pll = self.kp_pll, self.ki_pll + kp_pll, ki_pll = self.kp_pll_pu, self.ki_pll_puHz beta = self.beta vmag_bus = self.emt_init.vmag_bus sinphi = np.sin(self.emt_init.angle_ref*np.pi/180) @@ -181,77 +181,75 @@ def _build_small_signal_model(self): pll.C = pll.C @ scipy.linalg.inv(self.x_pll_rescale) # Outer control + DC capacitor dynamics - Kp, Ki, Cdc = self.kp_oc, self.ki_oc, self.c_dc - r_dc = 100 - outer_control = StateSpaceModel(A = np.array([[0, 0, Ki], - [0, 0, 0], - [0, 0, -1/r_dc]]), - B = np.array([[-Ki, 0, 0, 0, 0], - [0, 0, -Ki, Ki, 0], - [0, wb/Cdc, 0, 0, -wb/Cdc]]), - C = np.array([[1, 0, Kp], - [0, 1, 0], - [0, 0, 1]]), - D = np.array([[-Kp, 0, 0, 0, 0], - [0, 0, -Kp, Kp, 0], - [0, 0, 0, 0, 0]]), - u = DynamicalVariables(name=["vdc_ref", "i_load_ref", "q_ref", "q", "idc"]), - y = DynamicalVariables(name=["i_bus_d_ref", "i_bus_q_ref", "v_dc"]), - x = DynamicalVariables(name=["int_vdc", "int_q", "v_dc"], - init=[self.emt_init.int_vdc, self.emt_init.int_q, self.emt_init.v_dc])) + Kp, Ki, Cdc, Tload, r_dc = self.kp_oc_pu, self.ki_oc_puHz, self.c_dc, self.Tload, self.r_dc + outer_control = StateSpaceModel(A = np.array([[0, Ki, 0], + [0, -1/r_dc, -wb/Cdc], + [0, 0, -1/Tload]]), + B = np.array([[-Ki, 0, 0], + [0, -wb/Cdc, -wb/Cdc], + [0, 1/Tload, 0]]), + C = np.array([[1, Kp, 0], + [0, 1, 0]]), + D = np.array([[-Kp, 0, 0], + [0, 0, 0]]), + u = DynamicalVariables(name=["v_dc_ref", "i_load_ref", "idc"]), + y = DynamicalVariables(name=["i_bus_d_ref", "v_dc"]), + x = DynamicalVariables(name=["int_vdc", "v_dc", "i_load"], + init=[self.emt_init.int_vdc, self.emt_init.v_dc, self.emt_init.i_load_ref])) # Construction of CCM matrices # ustack = F*ystack + G*u - # ustack = i2cdq, v1cdq, v2cdq, w, v2cdq, vdcref, iload, qref, q, idc (14) - # y_stack = vmd, vmq, i2d, i2q, i1d, i1q, v3d, v3q, theta, w, idref, iqref, vdc (13) - extra 4 + 3 = 7 - # u = vdcref, iload, qref, v2d, v2q + # ustack = i2dq_c, v1dq_c, v2dq_c, w, v2dq, vdcref, iload_ref, idc (12) + # y_stack = e1d, e1q, i1d_c, i1q_c, i2d_c, i2q_c, v3d_c, v3q_c, theta, w, i2d_ref, vdc (12) + # u = vdc_ref, iload_ref, i2q_ref, v2d, v2q # y = i2dq - # reactive power linearization coefficients - a1 = -self.emt_init.v_lcl_sh_d - a2 = -self.emt_init.i_bus_q - a3 = self.emt_init.v_lcl_sh_q - a4 = self.emt_init.i_bus_d - # dc power balance linearization - b1 = self.emt_init.v_vsc_d/self.emt_init.v_dc - b2 = self.emt_init.i_vsc_d/self.emt_init.v_dc - b3 = self.emt_init.v_vsc_q/self.emt_init.v_dc # is this just 0 ? - b4 = self.emt_init.i_vsc_q/self.emt_init.v_dc - b5 = - self.emt_init.i_dc/self.emt_init.v_dc + v_dc = self.emt_init.v_dc + + b1 = self.emt_init.v_vsc_d/v_dc # i1d + b2 = self.emt_init.i_vsc_d/v_dc #e1d + b3 = - (self.emt_init.i_vsc_d/v_dc)*(self.lf1_pu+self.lf2_pu) #i2q + b4 = self.emt_init.v_vsc_q/v_dc # i1q + b5 = self.emt_init.i_vsc_q/v_dc # e1q + b6 = (self.emt_init.i_vsc_q/v_dc)*(self.lf1_pu+self.lf2_pu) #i2d + b7 = - self.emt_init.i_dc/v_dc #vdc + b8 = -(self.emt_init.i_vsc_q/v_dc)*self.beta*vmag_bus # theta # Construction of CCM matrices - Fccm = np.vstack( ( np.hstack((np.zeros((10, )), 1, 0, 0)) ,# i2ref_d - np.hstack((np.zeros((10, )), 0, 1, 0)) , # i2ref_q - np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,7)))), # i2c_dq - [1, 0, 0, 0, 0, -(lf1+lf2), 0, 0, 0, 0, 0, 0, 0], # v1c_d - [0, 1, 0, 0, (lf1+lf2), 0, 0, 0, -beta*vmag_bus, 0, 0, 0, 0], # v1c_q - np.zeros((13, )) , # v2c_d - np.append( np.zeros((8,)) , [-vmag_bus, 0, 0, 0, 0] ), # v2c_q - np.append( np.zeros((9,)) , [1, 0, 0, 0] ), # w - np.zeros((2, 13)), # v2c_dq - np.zeros((3, 13)), #vdcref, iload, qref - np.hstack((np.zeros(4,), [a3, a1, a2, a4], np.zeros(5,))), # q - np.hstack(([b2, b4, b1, b3], np.zeros(8,), [b5])) #idc + Fccm = np.vstack( ( np.hstack((np.zeros((10, )), 1, 0)) ,# i2d_ref + np.zeros((12,)), # i2q_ref + np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,6)))), # i2dq_c + [1, 0, 0, 0, 0, -(lf1+lf2), 0, 0, 0, 0, 0, 0], # v1d_c + [0, 1, 0, 0, (lf1+lf2), 0, 0, 0, -beta*vmag_bus, 0, 0, 0], # v1q_c + np.zeros((12, )) , # v2d_c + np.append( np.zeros((8,)) , [-vmag_bus, 0, 0, 0] ), # v2q_c + np.append( np.zeros((9,)) , [1, 0, 0] ), # w + np.zeros((2, 12)), # v2_dq + np.zeros((2, 12)), #vdc_ref, iload_ref + [b2, b5, b1, b4, b6, b3, 0, 0, b8, 0, 0, b7] #idc ) ) - Gccm = np.vstack(( np.zeros((4, 5)), # i2ref_dq, i2c_dq, - [0, 0, 0, beta*cosphi , beta*sinphi], # v1c_d - [0, 0, 0, -beta*sinphi, beta*cosphi], # v1c_q - [0, 0, 0, cosphi ,sinphi], # v2c_d - [0, 0, 0, -sinphi ,cosphi], # v2c_q + Gccm = np.vstack(( np.zeros(5,), # i2ref_d, + [0, 0, 1, 0, 0], #i2q_ref + np.zeros((2,5)), #i2dq_c, + [0, 0, 0, beta*cosphi , beta*sinphi], # v1d_c + [0, 0, 0, -beta*sinphi, beta*cosphi], # v1q_c + [0, 0, 0, cosphi ,sinphi], # v2d_c + [0, 0, 0, -sinphi ,cosphi], # v2q_c np.zeros((5, )), # w np.hstack((np.zeros((2,3)), np.eye(2) ) ), # v2dq - np.hstack((np.eye(3), np.zeros((3,2)))), # vdc_ref, iload, q_ref - np.zeros((2,5)) # q, idc + [1, 0, 0, 0, 0], #vdc_ref + [0, 1, 0, 0, 0], #iload_ref + np.zeros((1,5)) # idc ) ) - Hccm = np.vstack(( [ 0, 0 , 0, 0, cosphi , -sinphi, 0, 0, -sinphi*i_bus_d-cosphi*i_bus_q, 0, 0, 0, 0], - [0, 0, 0, 0, sinphi , cosphi , 0, 0, cosphi*i_bus_d-sinphi*i_bus_q, 0, 0, 0, 0] )) + Hccm = np.vstack(( [ 0, 0 , 0, 0, cosphi , -sinphi, 0, 0, -sinphi*i_bus_d-cosphi*i_bus_q, 0, 0, 0], + [0, 0, 0, 0, sinphi , cosphi , 0, 0, cosphi*i_bus_d-sinphi*i_bus_q, 0, 0, 0] )) Lccm = np.zeros((2, 5)) @@ -261,9 +259,9 @@ def _build_small_signal_model(self): # Inputs and outputs v_bus_D, v_bus_Q= self.emt_init.v_bus_D, self.emt_init.v_bus_Q u = DynamicalVariables( - name=["vdc_ref", "i_load_ref", "q_ref", "v_bus_D", "v_bus_Q"], + name=["v_dc_ref", "i_load_ref", "i_bus_q_ref", "v_bus_D", "v_bus_Q"], type=["device", "device", "device", "grid", "grid"], - init=[self.vdc_ref, self.emt_init.i_dc, self.emt_init.q_bus, v_bus_D, v_bus_Q]) + init=[self.emt_init.v_dc, self.emt_init.i_load_ref, self.emt_init.i_bus_q, v_bus_D, v_bus_Q]) i_bus_D, i_bus_Q= self.emt_init.i_bus_D, self.emt_init.i_bus_Q y = DynamicalVariables( @@ -316,13 +314,11 @@ def _calculate_emt_initial_conditions(self): v_vsc_dq - self.beta * v_bus_dq - 1j * (self.lf1_pu + self.lf2_pu) * i_bus_dq ) - - # Initial conditions for outer control / dc - # DC-side initial conditions - v_dc = self.vdc_ref + v_dc = self.v_dc_ref p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real # power at converter terminals i_dc = p_vsc/v_dc + i_load = -v_dc/self.r_dc - i_dc self.emt_init = InitialConditionsEMT( vmag_bus=vmag_bus, @@ -348,37 +344,49 @@ def _calculate_emt_initial_conditions(self): p_vsc=p_vsc, i_dc=i_dc, int_vdc=i_bus_dq.real, - int_q=i_bus_dq.imag, - i_load=i_dc, + i_load_ref=i_load, v_vsc_d=v_vsc_dq.real, v_vsc_q=v_vsc_dq.imag - ) def define_variables_emt(self): # States # ------ - + # Initial conditions + angle_ref = self.emt_init.angle_ref + pi_cc_d, pi_cc_q = self.emt_init.pi_cc_d, self.emt_init.pi_cc_q + + # these quantities are already in the converter ref frame (defined by angle_ref) + i_bus_d, i_bus_q = self.emt_init.i_bus_d, self.emt_init.i_bus_q + i_vsc_d, i_vsc_q = self.emt_init.i_vsc_d, self.emt_init.i_vsc_q + v_sh_d, v_sh_q = self.emt_init.v_lcl_sh_d, self.emt_init.v_lcl_sh_q + # convert to abc + i_bus_a, i_bus_b, i_bus_c = dq02abc(i_bus_d, i_bus_q, 0, angle_ref*np.pi/180) + i_vsc_a, i_vsc_b, i_vsc_c = dq02abc(i_vsc_d, i_vsc_q, 0, angle_ref*np.pi/180) + v_sh_a, v_sh_b, v_sh_c = dq02abc(v_sh_d, v_sh_q, 0, angle_ref*np.pi/180) + x = DynamicalVariables( - name = [], + name = ['pi_cc_d', 'pi_cc_q', 'theta_pll', 'gamma_pll', "i_vsc_a", "i_vsc_b", "i_vsc_c", "v_sh_a", "v_sh_b","v_sh_c", "i_bus_a", "i_bus_b", "i_bus_c", "int_vdc", "v_dc", "i_load"], component = f"{self.type_}_{self.id}", - init=[] + init = [pi_cc_d, pi_cc_q, angle_ref * np.pi/180, 0, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, self.emt_init.int_vdc, self.emt_init.v_dc, self.emt_init.i_load_ref] ) - + # Inputs # ------ # Initial conditions + v_bus_D, v_bus_Q = self.emt_init.v_bus_D, self.emt_init.v_bus_Q + v_bus_a, v_bus_b, v_bus_c = dq02abc(v_bus_D, v_bus_Q, 0, 0) u = DynamicalVariables( - name=["vdc_ref", "iload", "q_ref", "v_bus_a", "v_bus_b", "v_bus_c"], + name=["v_dc_ref", "i_load_ref", "i_bus_q_ref", "v_bus_a", "v_bus_b", "v_bus_c"], component=f"{self.type_}_{self.id}", - type=["device", "device", "device", "grid", "grid"], - init=[] + type=["device", "device", "device", "grid", "grid", "grid"], + init=[self.emt_init.v_dc, self.emt_init.i_load_ref, self.emt_init.i_bus_q, v_bus_a, v_bus_b, v_bus_c] ) - + # Outputs # ------- @@ -389,89 +397,88 @@ def define_variables_emt(self): ) self.variables_emt = VariablesEMT(x=x,u=u,y=y) - - + def get_derivative_state_emt(self): - - # Get state values - # outer control / current control / lcl / pll - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + """ + It returns a vector with the differential equations that describe the dynamics of the GFLI. + This model includes: pi controller, pll, and LCL filter. + """ + # Get state values # here in progress + pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, int_vdc, v_dc, i_load = self.variables_emt.x.value # Get input values (external inputs) - vdc_ref, iload, qref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value - - # convert relevant quantities to dq (ibr frame) - v_sh_d, v_sh_q, _ = abc2dq0(v_sh_a, v_sh_b, v_sh_c, angle_pc) # for power controller - i_bus_d, i_bus_q, _ = abc2dq0(i_bus_a, i_bus_b, i_bus_c, angle_pc) # for power controller - i_vsc_d, i_vsc_q, _ = abc2dq0(i_vsc_a, i_vsc_b, i_vsc_c, angle_pc) + v_dc_ref, i_load_ref, i_bus_q_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value - # outer control - id_ref = int_vdc + self.kp_oc*(vdc_ref - v_dc) - iq_ref = int_q + self.kp_oc*(qref - q) - - # current controller - vmd = self.ki_cc*gamma_d + self.kp_cc*(id_ref - i_d) - vmq = self.ki_cc*gamma_q + self.kp_cc*(id_ref - i_d) - - # PLL - + # convert relevant quantities to dq + v_bus_d, v_bus_q, _ = abc2dq0(v_bus_a, v_bus_b, v_bus_c, theta_pll) + i_bus_d, i_bus_q, _ = abc2dq0(i_bus_a, i_bus_b, i_bus_c, theta_pll) + + # Update algebraic states - # Do Q-V droop - v_sh_mag_ref = v_ref - self.droop_q_pu*(q_pc - q_ref) # adjusting voltage reference based on reactive power error (measured at capacitor) + # outer loop Vdc control + i_bus_d_ref = self.kp_oc_pu*(-v_dc_ref + v_dc) + int_vdc - # NB updating algebraic states! - # Updating converter terminal voltages - v_vsc_d = gamma + self.kp_vc_pu*(v_sh_mag_ref - (v_sh_d**2 + v_sh_q**2)**0.5) # update - v_vsc_q = 0.0 # update + # current controller output + e_d = pi_cc_d + self.kp_cc_pu * (i_bus_d_ref - i_bus_d) + e_q = pi_cc_q + self.kp_cc_pu * (i_bus_q_ref - i_bus_q) - # # check magnitude wrt v_dc and limit if necessary - # if v_vsc_d > v_dc: # can check only d component because v_vsc_q= 0 - # v_vsc_d = v_dc + v_vsc_d = e_d + self.beta * v_bus_d - (self.lf1_pu + self.lf2_pu) * i_bus_q + v_vsc_q = e_q + self.beta * v_bus_q + (self.lf1_pu + self.lf2_pu) * i_bus_d - # convert to abc to feed into filter dynamics - v_vsc_a, v_vsc_b, v_vsc_c = dq02abc(v_vsc_d, v_vsc_q, 0, angle_pc) # correct to use this angle? - - # DC/AC power balance + # calculate idc + i_vsc_d, i_vsc_q, _ = abc2dq0(i_vsc_a, i_vsc_b, i_vsc_c, theta_pll) i_dc = (v_vsc_d*i_vsc_d + v_vsc_q*i_vsc_q)/v_dc - - - - # Differential equations - # ---------------------- - - # Power controller: - def outer_loop_dynamics(y, internal_inputs): + + # convert to abc to feed into filter dynamics + v_vsc_a, v_vsc_b, v_vsc_c = dq02abc(v_vsc_d, v_vsc_q, 0, theta_pll) + + def current_controller_dynamics(y, internal_inputs): """ - PI control of Vdc error to generate id_ref - PI control of Q error to generate iq_ref + It returns the differential equations that describe the dynamics of the current controller. + The current controller has: virtual inertia, filter for active and reactive power. """ - int_vdc, int_q, v_dc = y[0], y[1], y[2] - - vdc_ref, iload, qref, idc, q = internal_inputs + + # Definition of states for the ODEs of the current controller + pi_cc_d, pi_cc_q = y[0], y[1] + + # Extract the list of parameters + kp_cc = self.kp_cc_pu # proportional gain of current controller + ki_cc = self.ki_cc_puHz # integral gain of current controller + + # Define internal inputs for the current controller at timepoint "t" + i_bus_d_ref, i_bus_q_ref, i_bus_d, i_bus_q = internal_inputs + + # Define ODEs that describe the dynamics of the current controller + d_pi_cc_d = ki_cc * (i_bus_d_ref - i_bus_d) + d_pi_cc_q = ki_cc * (i_bus_q_ref - i_bus_q) - d_int_vdc = self.Ki_oc*(v_dc_ref - v_dc) - d_int_q = self.Ki_oc*(qref - q) - d_v_dc = (-self.wbase/self.cdc)*(idc - iload) + return [d_pi_cc_d, d_pi_cc_q] - return [d_int_vdc, d_int_q, d_v_dc] - - - def current_controller_dynamics(y, internal_inputs): + def pll_dynamics(y, internal_inputs): """ - decoupled PI controllers + It returns the differential equations that describe the dynamics of the PLL. + The PLL tracks the phase of the grid voltage. """ - gamma = y[0] - - id_ref, iq_ref, id, iq = internal_inputs - - d_gamma_d = id_ref - id - d_gamma_q = iq_ref - iq + # Definition of states for the ODEs of the pll + theta_pll, gamma_pll = y[0], y[1] + + # Extract the list of parameters + kp_pll = self.kp_pll_pu # proportional gain of PLL + ki_pll = self.ki_pll_puHz # integral gain of PLL + w_base = self.wbase # base frequency of the system + + # Define voltage at bus + v_bus_q = internal_inputs + + # Define ODEs that describe the dynamics of the PLL + d_theta_pll = kp_pll * v_bus_q * w_base + gamma_pll + w_base + d_gamma_pll = ki_pll * v_bus_q + + return [d_theta_pll, d_gamma_pll] - return [d_gamma_d, d_gamma_q] - - def lcl_filter_dynamics(y, internal_inputs): """ + It returns the differential equations that describe the dynamics of the LCL filter. The LCL filter connects the VSC to the grid. It has three branches: the first branch (RL) connects the VSC to the shunt element, the second branch is the shunt element (RC), and the third branch (RL) connects the shunt element to the grid. @@ -483,258 +490,87 @@ def lcl_filter_dynamics(y, internal_inputs): i_bus_a, i_bus_b, i_bus_c = y[6], y[7], y[8] # currents flowing to bus # Extract the list of parameters - rf1, xf1, rf2, xf2, rsh, csh = self.rf1_pu, self.xf1_pu, self.rf2_pu, self.xf2_pu, self.rsh_pu, self.csh_pu - wb = self.wbase - r1 = rf1 - r2 = rf2 - x1 = xf1 - x2 = xf2 - - # Inputs + r1 = self.rf1_pu # resistance [p.u.] of first branch of filter + l1 = self.lf1_pu # inductance [p.u.] of first branch of filter + r2 = self.rf2_pu # resistance [p.u.] of second branch of filter + l2 = self.lf2_pu # inductance [p.u.] of second branch of filter + rsh = self.rsh_pu # resistance [p.u.] of series RC shunt + csh = self.csh_pu # capacitance [p.u.] of series RC shunt + wb = self.wbase # nominal frequency of the system + + # Define voltage at vsc at timepoint "t" v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c = internal_inputs # Define ODEs that describe the dynamics of the LCL filter - di_vsc_a = wb/x1 *(v_vsc_a - v_sh_a - r1 * i_vsc_a) - di_vsc_b = wb/x1 *(v_vsc_b - v_sh_b - r1 * i_vsc_b) - di_vsc_c = wb/x1 *(v_vsc_c - v_sh_c - r1 * i_vsc_c) + di_vsc_a = wb/l1 *(v_vsc_a - v_sh_a - r1 * i_vsc_a) + di_vsc_b = wb/l1 *(v_vsc_b - v_sh_b - r1 * i_vsc_b) + di_vsc_c = wb/l1 *(v_vsc_c - v_sh_c - r1 * i_vsc_c) dv_sh_a = wb/csh * (-v_sh_a/rsh + i_vsc_a - i_bus_a) dv_sh_b = wb/csh * (-v_sh_b/rsh + i_vsc_b - i_bus_b) dv_sh_c = wb/csh * (-v_sh_c/rsh + i_vsc_c - i_bus_c) - di_bus_a = wb/x2 *(v_sh_a - v_bus_a - r2 * i_bus_a) - di_bus_b = wb/x2 *(v_sh_b - v_bus_b - r2 * i_bus_b) - di_bus_c = wb/x2 *(v_sh_c - v_bus_c - r2 * i_bus_c) + di_bus_a = wb/l2 *(v_sh_a - v_bus_a - r2 * i_bus_a) + di_bus_b = wb/l2 *(v_sh_b - v_bus_b - r2 * i_bus_b) + di_bus_c = wb/l2 *(v_sh_c - v_bus_c - r2 * i_bus_c) return [di_vsc_a, di_vsc_b, di_vsc_c, dv_sh_a, dv_sh_b, dv_sh_c, di_bus_a, di_bus_b, di_bus_c] - - - def pll_dynamics(y, internal_inputs): - """ - """ - - int_pll, theta = y[0], y[1] + + def outer_loop_and_dc_side(y, internal_inputs): + int_vdc, v_dc, i_load = y[0], y[1], y[2] - v_bus_q = internal_inputs - - d_int_pll = self.ki_pll*(v_bus_q) - d_theta = self.wbase*self.ki_pll*int_pll - self.wbase*vm*self.kp_pll*theta + v_dc_ref, i_load_ref, i_dc = internal_inputs - return [d_int_pll, d_theta] - + d_int_vdc = self.ki_oc_puHz*(-v_dc_ref + v_dc) + d_vdc = (self.wbase/self.c_dc)*(-i_load - v_dc/self.r_dc - i_dc) + d_iload = 1/(self.Tload)*(i_load_ref - i_load) + + return [d_int_vdc, d_vdc, d_iload] - dy_pc = outer_loop_dynamics([angle_pc, w_pc, p_pc, q_pc], [v_sh_d, v_sh_q, i_bus_d, i_bus_q, p_ref]) - dy_vc = current_controller_dynamics([gamma], [v_sh_mag_ref, v_sh_d, v_sh_q]) - dy_lcl = lcl_filter_dynamics([i_vsc_a , i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c], [v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c]) - dy_pll = pll_dynamics([int_pll, theta], [v_sh_a, v_sh_b, v_sh_c]) + d_pi_cc_d, d_pi_cc_q = current_controller_dynamics([pi_cc_d, pi_cc_q], [i_bus_d_ref, i_bus_q_ref, i_bus_d, i_bus_q]) + + d_theta_pll, d_gamma_pll = pll_dynamics([theta_pll, gamma_pll], v_bus_q) - return np.hstack([dy_pc, dy_vc, dy_lcl]) + di_vsc_a, di_vsc_b, di_vsc_c, dv_sh_a, dv_sh_b, dv_sh_c, di_bus_a, di_bus_b, di_bus_c = lcl_filter_dynamics([i_vsc_a , i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c], [v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c]) + d_int_vdc, d_vdc, d_iload = outer_loop_and_dc_side([int_vdc, v_dc, i_load], [v_dc_ref, i_load_ref, i_dc]) + return [d_pi_cc_d, d_pi_cc_q, d_theta_pll, d_gamma_pll, di_vsc_a, di_vsc_b, di_vsc_c, dv_sh_a, dv_sh_b, dv_sh_c, di_bus_a, di_bus_b, di_bus_c, d_int_vdc, d_vdc, d_iload] + def get_output_emt(self): - # Output is i_bus_abc - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value - + pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, int_vdc, v_dc, i_load = self.variables_emt.x.value return [i_bus_a, i_bus_b, i_bus_c] - def plot_results_emt(self, output_dir): - - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value - - #i_L = np.clip(i_L, -1000.0, 1.0) + def plot_results_emt(self) -> DynamicalVariables: + """ + Plot EMT simulation results + """ + + pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, int_vdc, v_dc, i_load = self.variables_emt.x.value tps = self.variables_emt.x.time # Transform abc to dq0 - i_vsc_d, i_vsc_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_vsc_a, i_vsc_b, i_vsc_c, angle_pc)]) - v_sh_d, v_sh_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, angle_pc)]) - i_bus_d, i_bus_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, angle_pc)]) + i_vsc_d, i_vsc_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_vsc_a, i_vsc_b, i_vsc_c, theta_pll)]) + v_sh_d, v_sh_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, theta_pll)]) + i_bus_d, i_bus_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, theta_pll)]) - # calculate v_vsc v_sh_dq = v_sh_d + np.multiply(v_sh_q, 1j) i_vsc_dq = i_vsc_d + np.multiply(i_vsc_q, 1j) - v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.xf1_pu * 1j), i_vsc_dq) - - fig = make_subplots( - rows=14, cols=2, - horizontal_spacing=0.15, - vertical_spacing=0.05, - ) - - fig.add_trace(go.Scatter(x=tps, y=w_pc, mode='lines', line=dict(color='red', dash='solid')), - row=1, col=1) - fig.update_xaxes(title_text='Time [s]', row=1, col=1) - fig.update_yaxes(title_text='Frequency pc [p.u.]', row=1, col=1) - - fig.add_trace(go.Scatter(x=tps, y=angle_pc * 180 / np.pi, mode='lines', line=dict(color='red', dash='solid')), - row=1, col=2) - fig.update_xaxes(title_text='Time [s]', row=1, col=2) - fig.update_yaxes(title_text='Angle pc [deg]', row=1, col=2) - - fig.add_trace(go.Scatter(x=tps, y=p_pc, name="p_pc", mode='lines', line=dict(color='red', dash='solid')), - row=2, col=1) - fig.update_xaxes(title_text='Time [s]', row=2, col=1) - fig.update_yaxes(title_text='Active Power pc [p.u.]', row=2, col=1) - - fig.add_trace(go.Scatter(x=tps, y=q_pc, mode='lines', line=dict(color='red', dash='solid')), - row=2, col=2) - fig.update_xaxes(title_text='Time [s]', row=2, col=2) - fig.update_yaxes(title_text='Reactive Power pc [p.u.]', row=2, col=2) - - fig.add_trace(go.Scatter(x=tps, y=gamma, mode='lines', line=dict(color='red', dash='solid')), - row=3, col=1) - fig.update_xaxes(title_text='Time [s]', row=3, col=1) - fig.update_yaxes(title_text='Gamma [p.u.]', row=3, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_loadf, mode='lines', line=dict(color='red', dash='solid')), - row=3, col=2) - fig.update_xaxes(title_text='Time [s]', row=3, col=2) - fig.update_yaxes(title_text='iload_f [p.u.]', row=3, col=2) - - fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, mode='lines', line=dict(color='red', dash='solid')), - row=4, col=1) - fig.update_xaxes(title_text='Time [s]', row=4, col=1) - fig.update_yaxes(title_text='i_vsc_d [p.u.]', row=4, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_vsc_q, mode='lines', line=dict(color='red', dash='solid')), - row=4, col=2) - fig.update_xaxes(title_text='Time [s]', row=4, col=2) - fig.update_yaxes(title_text='i_vsc_q [p.u.]', row=4, col=2) - - fig.add_trace(go.Scatter(x=tps, y=v_sh_d, mode='lines', line=dict(color='red', dash='solid')), - row=5, col=1) - fig.update_xaxes(title_text='Time [s]', row=5, col=1) - fig.update_yaxes(title_text='v_sh_d [p.u.]', row=5, col=1) - - fig.add_trace(go.Scatter(x=tps, y=v_sh_q, mode='lines', line=dict(color='red', dash='solid')), - row=5, col=2) - fig.update_xaxes(title_text='Time [s]', row=5, col=2) - fig.update_yaxes(title_text='v_sh_q [p.u.]', row=5, col=2) - - fig.add_trace(go.Scatter(x=tps, y=i_bus_d, mode='lines', line=dict(color='red', dash='solid')), - row=6, col=1) - fig.update_xaxes(title_text='Time [s]', row=6, col=1) - fig.update_yaxes(title_text='i_bus_d [p.u.]', row=6, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_bus_q, mode='lines', line=dict(color='red', dash='solid')), - row=6, col=2) - fig.update_xaxes(title_text='Time [s]', row=6, col=2) - fig.update_yaxes(title_text='i_bus_q [p.u.]', row=6, col=2) - - fig.add_trace(go.Scatter(x=tps, y=v_dc, mode='lines', line=dict(color='red', dash='solid')), - row=7, col=1) - fig.update_xaxes(title_text='Time [s]', row=7, col=1) - fig.update_yaxes(title_text='v_dc [p.u.]', row=7, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_L, mode='lines', line=dict(color='red', dash='solid')), - row=7, col=2) - fig.update_xaxes(title_text='Time [s]', row=7, col=2) - fig.update_yaxes(title_text='i_L [p.u.]', row=7, col=2) - - - fig.add_trace(go.Scatter(x=tps, y=v_dcf, mode='lines', line=dict(color='red', dash='solid')), - row=8, col=1) - fig.update_xaxes(title_text='Time [s]', row=8, col=1) - fig.update_yaxes(title_text='v_dcf [p.u.]', row=8, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_Lf, mode='lines', line=dict(color='red', dash='solid')), - row=8, col=2) - fig.update_xaxes(title_text='Time [s]', row=8, col=2) - fig.update_yaxes(title_text='i_Lf [p.u.]', row=8, col=2) - - - fig.add_trace(go.Scatter(x=tps, y=i_dcf, mode='lines', line=dict(color='red', dash='solid')), - row=9, col=1) - fig.update_xaxes(title_text='Time [s]', row=9, col=1) - fig.update_yaxes(title_text='i_dcf [p.u.]', row=9, col=1) - - fig.add_trace(go.Scatter(x=tps, y=x1, mode='lines', line=dict(color='red', dash='solid')), - row=9, col=2) - fig.update_xaxes(title_text='Time [s]', row=9, col=2) - fig.update_yaxes(title_text='x1 [p.u.]', row=9, col=2) - - fig.add_trace(go.Scatter(x=tps, y=x2, mode='lines', line=dict(color='red', dash='solid')), - row=10, col=1) - fig.update_xaxes(title_text='Time [s]', row=10, col=1) - fig.update_yaxes(title_text='x2 [p.u.]', row=10, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_load, mode='lines', line=dict(color='red', dash='solid')), - row=10, col=2) - fig.update_xaxes(title_text='Time [s]', row=10, col=2) - fig.update_yaxes(title_text='i_load [p.u.]', row=10, col=2) - - # power comparisons (calculated) + i_bus_dq = i_bus_d + np.multiply(i_bus_q, 1j) + v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.lf1_pu * 1j), i_vsc_dq) p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real - p_load = i_load*v_dc - p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value - p_bat = i_L*v_s - p_capacitor = p_vsc + p_load - p_bat - fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid')), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid')), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid')), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_capacitor, name="p_cap", mode='lines', line=dict(color='pink', dash='solid')), - row=11, col=1) - - fig.add_trace(go.Scatter(x=tps, y=np.ones_like(p_bat)*self.Pbat_max_pu, name="p_bat_lim", mode='lines', line=dict(color='green', dash='dot')), - row=11, col=1) - fig.update_xaxes(title_text='Time [s]', row=11, col=1) - fig.update_yaxes(title_text='p [p.u.]', row=11, col=1) + p_cap = (v_sh_dq*np.conjugate(i_bus_dq)).real + q_cap = (v_sh_dq*np.conjugate(i_bus_dq)).imag - # v_vsc (calculated) - fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.real, mode='lines', line=dict(color='red', dash='solid')), - row=12, col=1) - fig.update_xaxes(title_text='Time [s]', row=12, col=1) - fig.update_yaxes(title_text='v_vsc_d [p.u.]', row=12, col=1) + p_load = v_dc*i_load - fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.imag, mode='lines', line=dict(color='red', dash='solid')), - row=12, col=2) - fig.update_xaxes(title_text='Time [s]', row=12, col=2) - fig.update_yaxes(title_text='v_vsc_q [p.u.]', row=12, col=2) - - e_cap = 0.5*self.c_dc*(v_dc**2) - e_ind = 0.5*self.l_dc*(i_L**2) - fig.add_trace(go.Scatter(x=tps, y=soc, name='battery soc', mode='lines', line=dict(color='red', dash='solid')), - row=13, col=1) - fig.add_trace(go.Scatter(x=tps, y=e_cap, name='cap energy', mode='lines', line=dict(color='blue', dash='dot')), - row=13, col=1) - fig.add_trace(go.Scatter(x=tps, y=e_ind, name='ind energy', mode='lines', line=dict(color='pink', dash='dot')), - row=13, col=1) - - fig.add_trace(go.Scatter(x=tps, y=np.ones_like(soc)*self.SOC_max_pu, name="soc lim", mode='lines', line=dict(color='green', dash='dot')), - row=13, col=1) - fig.update_xaxes(title_text='Time [s]', row=13, col=1) - fig.update_yaxes(title_text='energy [p.u.]', row=13, col=1) - - duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 - duty_cycle = np.clip(duty_cycle, 0.0, 1.0) - fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='red', dash='solid')), - row=13, col=2) - fig.update_xaxes(title_text='Time [s]', row=13, col=2) - fig.update_yaxes(title_text='duty cycle [p.u.]', row=13, col=2) - - fig.add_trace(go.Scatter(x=tps, y=x3, mode='lines', line=dict(color='red', dash='solid')), - row=14, col=1) - fig.update_xaxes(title_text='Time [s]', row=14, col=1) - fig.update_yaxes(title_text='x3 [p.u.]', row=14, col=1) - - - name = f"{self.type_}_{self.id}" - fig.update_layout( title_text = name, - title_x=0.5, - showlegend = False, - ) - - fig.update_layout(height=1200*2, - width=800*2, - showlegend=False, - margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) - - fig.write_html(os.path.join(output_dir, name + ".html")) - - - \ No newline at end of file + results = DynamicalVariables( + name=['pi_cc_d', 'pi_cc_q', 'theta_pll', 'gamma_pll', 'i_vsc_d', 'i_vsc_q', 'v_sh_d', 'v_sh_q', 'i_bus_d', 'i_bus_q', 'int_vdc', 'v_dc', 'i_load', 'p_vsc', 'p_cap', 'pload', 'q_cap'], + component=f"{self.type_}_{self.id}", + value=[pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, int_vdc, v_dc, i_load, p_vsc, p_cap, p_load, q_cap], + time=tps + ) + return results \ No newline at end of file diff --git a/sting/system/core.py b/sting/system/core.py index aaddfa3..976f144 100644 --- a/sting/system/core.py +++ b/sting/system/core.py @@ -28,6 +28,7 @@ from sting.generator.gfmi_c import GFMIc from sting.generator.gfmi_d import GFMId from sting.generator.gfmi_e import GFMIe +from sting.generator.gfli_e import GFLIe from sting.reduced_order_model.linear_subsystem import LinearSubsystem from sting.line.pi_model import LinePiModel from sting.branch.series_rl import BranchSeriesRL @@ -59,6 +60,7 @@ class System: gfmi_d: list[GFMId] = None gfmi_e: list[GFMIe] = None gfli_a: list[GFLIa] = None + gfli_e: list[GFLIe] = None linear_subsystems: list[LinearSubsystem] = None buses: list[Bus] = None loads: list[Load] = None From a9f9b11edcf030284f288face3802d6295e87491 Mon Sep 17 00:00:00 2001 From: rkravis Date: Mon, 11 May 2026 12:29:46 -0700 Subject: [PATCH 3/6] minor fix to gfli_e example --- .../2-bus-src-gfli_e/run.py | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py index 9bb1cf4..fd226ef 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py @@ -1,23 +1,9 @@ # Import Python standard and third-party packages from pathlib import Path -from scipy.linalg import eig, inv -import matplotlib.pyplot as plt -import sys -import seaborn as sns -import pandas as pd -import numpy as np -import os -from plotly.subplots import make_subplots -import plotly.graph_objects as go - -sys.path.append("/Users/ruthkravis/Documents/STING") + # Import sting package from sting import main -from sting.system.core import System -from scipy import signal - - # Step-change input to applied to the system def step1(t): @@ -26,17 +12,6 @@ def step1(t): def step2(t): return 0.0 -def step3(t): - return 0.1 if t >= 1.0 else 0.0 - - -def sin_oscillation(t): - return 0.05*np.sin(2*np.pi*20*t) if t < 1 else 0 #1 Hz oscillation - -def square_oscillation(t): - osc = 0.05*signal.square(2 * np.pi * 14 * t) - return osc - inputs = { 'infinite_sources_0': { 'v_ref_d': step1 @@ -51,9 +26,6 @@ def square_oscillation(t): # Specify path of the case study directory case_dir = Path(__file__).resolve().parent -# Construct system and small-signal model -sys = System.from_csv(case_directory=case_dir) - # Construct system and small-signal model _, ssm = main.run_ssm(case_directory=case_dir) ssm.simulate_ssm(t_max=t_max, inputs=inputs) From 992430bd04d163b618e4492b3930ebb317e8ec69 Mon Sep 17 00:00:00 2001 From: rkravis Date: Tue, 12 May 2026 14:28:23 -0700 Subject: [PATCH 4/6] Merge remote-tracking branch 'origin/main' into ruth --- .../2-bus-src-gfli_e/inputs/buses.csv | 4 +- .../2-bus-src-gfli_e/inputs/gfli_e.csv | 4 +- .../inputs/infinite_sources.csv | 2 +- .../2-bus-src-gfli_e/inputs/lines.csv | 2 +- .../2-bus-src-gfli_e/inputs/loads.csv | 2 +- .../2-bus-src-gfli_e/inputs/timepoints.csv | 2 +- .../2-bus-src-gfli_e/run.py | 42 +++- .../2-bus_src_gfli_a/inputs/buses.csv | 3 - .../2-bus_src_gfli_a/inputs/gfli_a.csv | 2 - .../inputs/infinite_sources.csv | 2 - .../2-bus_src_gfli_a/inputs/lines.csv | 2 - .../2-bus_src_gfli_a/inputs/loads.csv | 3 - .../2-bus_src_gfli_a/inputs/timepoints.csv | 2 - .../2-bus_src_gfli_a/run.py | 34 --- sting/generator/gfli_c.py | 234 ------------------ sting/generator/gfli_e.py | 93 +++---- 16 files changed, 95 insertions(+), 338 deletions(-) mode change 100755 => 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/buses.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/gfli_a.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/infinite_sources.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/lines.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/loads.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/timepoints.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src_gfli_a/run.py delete mode 100644 sting/generator/gfli_c.py diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv index 3817d3d..b50acd6 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/buses.csv @@ -1,3 +1,3 @@ name,base_power_MVA,base_voltage_kV,base_frequency_Hz,minimum_voltage_pu,maximum_voltage_pu -lima,1.00E+02,2.30E+02,60,0.95,1.05 -santiago,1.00E+02,2.30E+02,60,0.95,1.05 \ No newline at end of file +lima,1.00E+02,2.30E+02,60,1,1 +santiago,1.00E+02,2.30E+02,60,0.95,1.3 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv old mode 100755 new mode 100644 index 8bdc652..f033174 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/gfli_e.csv @@ -1,2 +1,2 @@ -name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,lf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_l1_pu,txr_r2_pu,txr_l2_pu,beta,kp_pll_pu,ki_pll_puHz,kp_cc_pu,ki_cc_puHz,v_dc_ref,c_dc,kp_oc_pu,ki_oc_puHz,Tload,r_dc -solar,santiago,50,50,-10,10,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0,1,5,1,5,1,20,0.01,0,0.01,10 \ No newline at end of file +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,beta,kp_pll_pu,ki_pll_puHz,kp_cc_pu,ki_cc_puHz,v_dc_ref,c_dc,kp_oc_pu,ki_oc_puHz,Tload,r_dc +solar,santiago,-80,-80,0,0,1.00E+02,4.80E-01,60,10,0.001,0.05,1,0.1,1.00E+02,4.80E-01,2.30E+02,0.002,0.05,0.002,0.05,0.75,100,2500,0.45,6,1,20,2,10,0.001,10 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv index fed2eb7..b13b274 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/infinite_sources.csv @@ -1,2 +1,2 @@ name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,r_pu,x_pu -gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.04 \ No newline at end of file +gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.001,0.02 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv index b74276c..3b76ba4 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/lines.csv @@ -1,2 +1,2 @@ name,from_bus,to_bus,base_power_MVA,base_voltage_kV,base_frequency_Hz,r_pu,x_pu,g_pu,b_pu -tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.01,0.2,0.05,0.066666667 \ No newline at end of file +tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.001,0.05,0.05,0.006666667 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv index 79d806e..ed9aa45 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/loads.csv @@ -1,3 +1,3 @@ bus,timepoint,load_MW,load_MVAR lima,t_1,0,0 -chile,t_1,0,0 \ No newline at end of file +santiago,t_1,0,0 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv index ad319a5..fb3202e 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/inputs/timepoints.csv @@ -1,2 +1,2 @@ name -timepoint_1 \ No newline at end of file +t_1 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py index fd226ef..48b9f58 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py @@ -1,17 +1,33 @@ # Import Python standard and third-party packages from pathlib import Path +import numpy as np +import pandas as pd +from scipy.linalg import eig, inv +import seaborn as sns +import matplotlib.pyplot as plt + +import sys +sys.path.append('/Users/ruthkravis/Documents/STING') + # Import sting package from sting import main +from sting.utils.transformations import dq02abc, abc2dq0 # Step-change input to applied to the system def step1(t): - return 0.2 if t >= 0.2 else 0.0 + return 0.3 if t >= 0.1 else 0.0 def step2(t): return 0.0 +def step3(t): + return 0.1 if t >= 0.5 else 0.0 + +def oscillating_load(t): + return 0.01*np.square(2*np.pi*0.5*t) if t >= 0.1 else 0.0 + inputs = { 'infinite_sources_0': { 'v_ref_d': step1 @@ -21,7 +37,7 @@ def step2(t): } } -t_max = 1.0 # Simulation length (in seconds) +t_max = 2.0 # Simulation length (in seconds) # Specify path of the case study directory case_dir = Path(__file__).resolve().parent @@ -31,4 +47,26 @@ def step2(t): ssm.simulate_ssm(t_max=t_max, inputs=inputs) main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) +# def participation_factor_plot(ssm): +# w,vr = eig(ssm.model.A) +# vl = inv(vr) # ensures normalization + +# p = np.zeros_like(vl) +# for i in range(len(w)): +# for k in range(len(w)): +# # use correct formula for complex eigenvalues +# p[k, i] = (vl[i,k].real)**2/(np.dot(vl[i,:].real,vl[i,:].real)) # participation of k-th state (row) in i-th mode (column) + +# pf_df = pd.DataFrame(p.real, index=ssm.model.x.name) +# fig = plt.figure(figsize=(12,8)) +# ax = sns.heatmap(pf_df, yticklabels=pf_df.index, xticklabels=np.round(w.real,3), linewidths=1, linecolor='white') +# ax.set_yticklabels(ax.get_yticklabels(), fontsize=8) +# ax.set_xticklabels(ax.get_xticklabels(), fontsize=8) +# plt.show() +# plt.savefig("ssm.png") + + +# participation_factor_plot(ssm) + + print('\nok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/buses.csv b/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/buses.csv deleted file mode 100644 index 3817d3d..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/buses.csv +++ /dev/null @@ -1,3 +0,0 @@ -name,base_power_MVA,base_voltage_kV,base_frequency_Hz,minimum_voltage_pu,maximum_voltage_pu -lima,1.00E+02,2.30E+02,60,0.95,1.05 -santiago,1.00E+02,2.30E+02,60,0.95,1.05 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/gfli_a.csv b/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/gfli_a.csv deleted file mode 100644 index b2beaad..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/gfli_a.csv +++ /dev/null @@ -1,2 +0,0 @@ -name,bus_idx,p_min,p_max,q_min,q_max,sbase,vbase,fbase,rf1,lf1,rsh,csh,txr_sbase,txr_v1base,txr_v2base,txr_r1,txr_l1,txr_r2,txr_l2,beta,kp_pll,ki_pll,kp_cc,ki_cc,v_dc -solar,2,0.8,0.8,0.5,0.5,1.00E+08,4.80E+02,60,0.02,0.1,1,0.1,1.00E+08,4.80E+02,2.30E+05,0.01,0.1,0.02,0.1,0.75,1,5,1,5,1 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/infinite_sources.csv b/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/infinite_sources.csv deleted file mode 100644 index 9d95931..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/infinite_sources.csv +++ /dev/null @@ -1,2 +0,0 @@ -name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,r_pu,x_pu -gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.5 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/lines.csv b/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/lines.csv deleted file mode 100644 index 74a86b2..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/lines.csv +++ /dev/null @@ -1,2 +0,0 @@ -name,from_bus,to_bus,base_power_MVA,base_voltage_kV,base_frequency_Hz,r_pu,x_pu,g_pu,b_pu -tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.01,0.5,0.05,0.066666667 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/loads.csv b/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/loads.csv deleted file mode 100644 index 79d806e..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/loads.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,load_MW,load_MVAR -lima,t_1,0,0 -chile,t_1,0,0 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/timepoints.csv b/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/timepoints.csv deleted file mode 100644 index ad319a5..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/inputs/timepoints.csv +++ /dev/null @@ -1,2 +0,0 @@ -name -timepoint_1 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src_gfli_a/run.py b/examples/small_signal_and_emt/2-bus_src_gfli_a/run.py deleted file mode 100644 index 98b8144..0000000 --- a/examples/small_signal_and_emt/2-bus_src_gfli_a/run.py +++ /dev/null @@ -1,34 +0,0 @@ - -# Import Python standard and third-party packages -from pathlib import Path - -# Import sting package -from sting import main -from sting.system.core import System - -# Step-change input to applied to the system -def step1(t): - return 0.1 if t >= 0.5 else 0.0 - -def step2(t): - return 0.0 - -inputs = { - 'infinite_sources_0': { - 'v_ref_d': step2 - } - } - -t_max = 2.0 # Simulation length (in seconds) - -# Specify path of the case study directory -case_dir = Path(__file__).resolve().parent - -# Construct system and small-signal model -sys = System.from_csv(case_directory=case_dir) - -# Construct system and small-signal model -_, ssm = main.run_ssm(case_directory=case_dir) -ssm.simulate_ssm(t_max=t_max, inputs=inputs) - -print('\nok') \ No newline at end of file diff --git a/sting/generator/gfli_c.py b/sting/generator/gfli_c.py deleted file mode 100644 index 1d9f15c..0000000 --- a/sting/generator/gfli_c.py +++ /dev/null @@ -1,234 +0,0 @@ -""" -Untested, still in development. -""" - -# Import standard python packages -import pandas as pd -import numpy as np -from scipy.linalg import block_diag -from dataclasses import dataclass, field -from typing import NamedTuple, Optional - -# Import src packages -from sting.utils import linear_systems_tools -from sting.utils.linear_systems_tools import State_space_model - - -class Power_flow_variables(NamedTuple): - p_bus: float - q_bus: float - vmag_bus: float - vphase_bus: float - - -class EMT_initial_conditions(NamedTuple): - vmag_bus: float - vphase_bus: float - p_bus: float - q_bus: float - angle_ref: float - pi_cc_d: float - pi_cc_q: float - i_bus_d: float - i_bus_q: float - i_bus_D: float - i_bus_Q: float - v_bus_D: float - v_bus_Q: float - v_vsc_mag: float - v_vsc_DQ_phase: float - - -@dataclass(slots=True) -class GFLI_c: - """Grid-following inverter that has L filter and PI current controller. No PLL and No DC-side - dynamics are included.""" - - idx: str - bus_idx: str - p_min: float - p_max: float - q_min: float - q_max: float - sbase: float - vbase: float - vbase: float - fbase: float - v_dc: float - rf: float - lf: float - txr_sbase: float - txr_r1: float - txr_l1: float - txr_r2: float - txr_l2: float - beta: float - kp_cc: float - ki_cc: float - name: str = field(default_factory=str) - type: str = "generator" - pf: Optional[Power_flow_variables] = None - emt_init_cond: Optional[EMT_initial_conditions] = None - ssm: Optional[State_space_model] = None - - @property - def txr_r(self): - return (self.txr_r1 + self.txr_r2) * self.sbase / self.txr_sbase - - @property - def txr_l(self): - return (self.txr_l1 + self.txr_l2) * self.sbase / self.txr_sbase - - @property - def wbase(self): - return 2 * np.pi * self.fbase - - def _load_power_flow_solution(self, power_flow_instance): - sol = power_flow_instance.generators.loc[self.idx] - self.pf = Power_flow_variables( - p_bus=sol.p.item(), - q_bus=sol.q.item(), - vmag_bus=sol.bus_vmag.item(), - vphase_bus=sol.bus_vphase.item(), - ) - - def _calculate_emt_initial_conditions(self): - vmag_bus = self.pf.vmag_bus - vphase_bus = self.pf.vphase_bus - p_bus = self.pf.p_bus - q_bus = self.pf.q_bus - - # Voltage in the end of the L filter - v_bus_DQ = vmag_bus * np.exp(vphase_bus * np.pi / 180 * 1j) - ref_angle = np.angle(v_bus_DQ, deg=True) - - # Current sent from the end of the L filter - i_bus_DQ = (p_bus - q_bus * 1j) / np.conjugate(v_bus_DQ) - - # Voltage at vsc - v_vsc_DQ = ( - v_bus_DQ + (self.rf + self.txr_r + (self.lf + self.txr_l) * 1j) * i_bus_DQ - ) - - # We refer the voltage and currents to the synchronous frames of the - # inverter - v_vsc_dq = v_vsc_DQ * np.exp(-ref_angle * np.pi / 180 * 1j) - v_bus_dq = v_bus_DQ * np.exp(-ref_angle * np.pi / 180 * 1j) - i_bus_dq = i_bus_DQ * np.exp(-ref_angle * np.pi / 180 * 1j) - - # Initial conditions for the integral controller - pi_cc_dq = ( - v_vsc_dq - 1j * (self.lf + self.txr_l) * i_bus_dq - self.beta * v_bus_dq - ) - - self.emt_init_cond = EMT_initial_conditions( - vmag_bus=vmag_bus, - vphase_bus=vphase_bus, - p_bus=p_bus, - q_bus=q_bus, - angle_ref=ref_angle, - pi_cc_d=pi_cc_dq.real, - pi_cc_q=pi_cc_dq.imag, - i_bus_d=i_bus_dq.real, - i_bus_q=i_bus_dq.imag, - i_bus_D=i_bus_DQ.real, - i_bus_Q=i_bus_DQ.imag, - v_bus_D=v_bus_DQ.real, - v_bus_Q=v_bus_DQ.imag, - v_vsc_mag=abs(v_vsc_DQ), - v_vsc_DQ_phase=np.angle(v_vsc_DQ, deg=True), - ) - - def _build_small_signal_model(self): - - # Current PI controller - kp_cc, ki_cc = self.kp_cc, self.ki_cc - pi_cc_d, pi_cc_q = self.emt_init_cond.pi_cc_d, self.emt_init_cond.pi_cc_q - - pi_controller = State_space_model( - A=np.zeros((2, 2)), - B=ki_cc * np.hstack((np.eye(2), -np.eye(2))), - C=np.eye(2), - D=kp_cc * np.hstack((np.eye(2), -np.eye(2))), - inputs=["i_bus_d_ref", "i_bus_q_ref"], - states=["pi_cc_d", "pi_cc_q"], - outputs=["e_d", "e_q"], - initial_states=np.array([[pi_cc_d], [pi_cc_q]]), - ) - - # L filter - rf = self.rf + self.txr_r - lf = self.lf + self.txr_l - wb = self.wbase - beta = self.beta - i_bus_d, i_bus_q = self.emt_init_cond.i_bus_d, self.emt_init_cond.i_bus_q - sinphi = np.sin(self.emt_init_cond.angle_ref * np.pi / 180) - cosphi = np.cos(self.emt_init_cond.angle_ref * np.pi / 180) - # fmt: off - l_filter = State_space_model( A = wb*np.array([[-rf/lf, 1], - [-1 , -rf/lf]]), - B = wb*np.array([[ 1/lf , 0 ,-1/lf ,0] , - [0, 1/lf, 0, -1/lf]]), - C = np.eye(2), - D = np.zeros((2,4)), - states= ['i_bus_d', 'i_bus_q'], - inputs=['v_vsc_d', 'v_vsc_q', 'v_bus_d', 'v_bus_q'], - outputs=['i_bus_d', 'i_bus_q'], - initial_states = np.array([[i_bus_d], [i_bus_q]])) - - - # Interconnection matrices - Fccm = np.vstack( ( np.zeros((1,4)) , - np.zeros((1,4)) , - np.hstack((np.zeros((2,2)), np.eye(2))), - [1, 0, 0, -lf ], - [0, 1, lf, 0 ], - np.zeros((2,4)) )) - - Gccm = np.vstack(( [ 1, 0, 0, 0], - [0, 1, 0, 0], - np.zeros((2,4)), - [0, 0, beta*cosphi , beta*sinphi], - [0, 0, -beta*sinphi, beta*cosphi], - [0, 0, cosphi ,sinphi], - [0, 0, -sinphi ,cosphi], - ) ) - - Hccm = np.vstack(( [ 0, 0 ,cosphi , -sinphi], - [0, 0, sinphi , cosphi ] )) - # fmt: on - Lccm = np.zeros((2, 4)) - - # Generate small-signal model - ssm = linear_systems_tools.connect_models_via_CCM( - Fccm, Gccm, Hccm, Lccm, [pi_controller, l_filter] - ) - - # Note that states and initial states do not need to be defined as they result from stacking states in ccm tool. - - # Inputs and outputs - device_side_inputs = ["i_bus_d_ref", "i_bus_q_ref"] - initial_device_side_inputs = np.array([[i_bus_d], [i_bus_q]]) - - grid_side_inputs = ["v_bus_D", "v_bus_Q"] - v_bus_D, v_bus_Q = self.emt_init_cond.v_bus_D, self.emt_init_cond.v_bus_Q - initial_grid_side_inputs = np.array([[v_bus_D], [v_bus_Q]]) - - outputs = ["i_bus_D", "i_bus_Q"] - i_bus_D, i_bus_Q = self.emt_init_cond.i_bus_D, self.emt_init_cond.i_bus_Q - initial_outputs = np.array([[i_bus_D], [i_bus_Q]]) - - self.ssm = State_space_model( - A=ssm.A, - B=ssm.B, - C=ssm.C, - D=ssm.D, - states=ssm.states, - initial_states=ssm.initial_states, - outputs=outputs, - initial_outputs=initial_outputs, - device_side_inputs=device_side_inputs, - initial_device_side_inputs=initial_device_side_inputs, - grid_side_inputs=grid_side_inputs, - initial_grid_side_inputs=initial_grid_side_inputs, - ) diff --git a/sting/generator/gfli_e.py b/sting/generator/gfli_e.py index 26ce334..3ca7f2f 100644 --- a/sting/generator/gfli_e.py +++ b/sting/generator/gfli_e.py @@ -1,7 +1,7 @@ """ This module implements a GFLI that incorporates: - LCL filter: Two Series RL branches (one branch is the transformer) and one Parallel RC shunt. -- Outer DC voltage controller +- Outer loop DC voltage PI controller - DC-side capacitor dynamics with resistor and current source representing a load - Current controller: A dq-based frame PI controller - PLL: A basic implementation @@ -13,7 +13,6 @@ import numpy as np from dataclasses import dataclass, field from typing import NamedTuple, Optional -import scipy.linalg # ------------------ # Import sting code @@ -72,16 +71,16 @@ class VariablesEMT(NamedTuple): @dataclass(slots=True, kw_only=True, eq=False) class GFLIe(Generator): rf1_pu: float - lf1_pu: float + xf1_pu: float rsh_pu: float csh_pu: float txr_power_MVA: float txr_voltage1_kV: float txr_voltage2_kV: float txr_r1_pu: float - txr_l1_pu: float + txr_x1_pu: float txr_r2_pu: float - txr_l2_pu: float + txr_x2_pu: float beta: float kp_pll_pu: float ki_pll_puHz: float @@ -103,8 +102,8 @@ def rf2_pu(self): return (self.txr_r1_pu + self.txr_r2_pu) * self.base_power_MVA / self.txr_power_MVA @property - def lf2_pu(self): - return (self.txr_l1_pu + self.txr_l2_pu) * self.base_power_MVA / self.txr_power_MVA + def xf2_pu(self): + return (self.txr_x1_pu + self.txr_x2_pu) * self.base_power_MVA / self.txr_power_MVA @property def wbase(self): @@ -126,23 +125,23 @@ def _build_small_signal_model(self): init= [pi_cc_d, pi_cc_q]) ) # LCL filter - rf1, lf1, rf2, lf2, rsh, csh = self.rf1_pu, self.lf1_pu, self.rf2_pu, self.lf2_pu, self.rsh_pu, self.csh_pu + rf1, xf1, rf2, xf2, rsh, csh = self.rf1_pu, self.xf1_pu, self.rf2_pu, self.xf2_pu, self.rsh_pu, self.csh_pu wb = self.wbase i_vsc_d, i_vsc_q = self.emt_init.i_vsc_d, self.emt_init.i_vsc_q i_bus_d, i_bus_q = self.emt_init.i_bus_d, self.emt_init.i_bus_q v_lcl_sh_d, v_lcl_sh_q = self.emt_init.v_lcl_sh_d, self.emt_init.v_lcl_sh_q lcl_filter = StateSpaceModel( - A = wb*np.array([[-rf1/lf1 , 1 , 0 , 0 , -1/lf1 , 0], - [-1 , -rf1/lf1, 0 , 0 , 0 , -1/lf1], - [0 , 0 , -rf2/lf2 , 1 , 1/lf2 , 0], - [0 , 0 , -1 , -rf2/lf2, 0 , 1/lf2], + A = wb*np.array([[-rf1/xf1 , 1 , 0 , 0 , -1/xf1 , 0], + [-1 , -rf1/xf1, 0 , 0 , 0 , -1/xf1], + [0 , 0 , -rf2/xf2 , 1 , 1/xf2 , 0], + [0 , 0 , -1 , -rf2/xf2, 0 , 1/xf2], [1/csh , 0 , -1/csh , 0 , -1/(rsh*csh), 1], [0 , 1/csh , 0 , -1/csh , -1 , -1/(rsh*csh)]]), - B = wb*np.array([[1/lf1 , 0 , 0 , 0 , i_vsc_q], - [0 , 1/lf1 , 0 , 0 , -i_vsc_d], - [0 , 0 , -1/lf2 , 0 , i_bus_q], - [0 , 0 , 0 , -1/lf2 , -i_bus_d], + B = wb*np.array([[1/xf1 , 0 , 0 , 0 , i_vsc_q], + [0 , 1/xf1 , 0 , 0 , -i_vsc_d], + [0 , 0 , -1/xf2 , 0 , i_bus_q], + [0 , 0 , 0 , -1/xf2 , -i_bus_d], [0 , 0 , 0 , 0 , v_lcl_sh_q], [0 , 0 , 0 , 0 , -v_lcl_sh_d]]), C = np.eye(6), @@ -162,23 +161,23 @@ def _build_small_signal_model(self): phase_pll = self.emt_init.angle_ref*np.pi/180 pll = StateSpaceModel( A = np.array([ [ 0 , -vmag_bus*ki_pll], - [wb , -wb*vmag_bus*kp_pll]]), + [1 , -1*vmag_bus*kp_pll]]), B = np.array([ [-sinphi*ki_pll , +cosphi*ki_pll], - [-wb*kp_pll*sinphi, wb*kp_pll*cosphi]]), + [-1*kp_pll*sinphi, 1*kp_pll*cosphi]]), C = np.array([ [0 , 1], - [1 , -1*vmag_bus*kp_pll]]), + [1/wb , -1/wb * vmag_bus*kp_pll]]), D = np.array([ [0 , 0], - [-1*kp_pll*sinphi , 1*kp_pll*cosphi]]), + [-1/wb * kp_pll * sinphi , 1/wb * kp_pll * cosphi]]), u = DynamicalVariables(name=['v_bus_D', 'v_bus_Q']), y = DynamicalVariables(name=['phase', 'w']), x = DynamicalVariables(name=["int_pll", "phase_pll"], init=[int_pll, phase_pll] ) ) - + # Re-scale the states so that they are not very small numbers compared to # other states. It was tested in EMT simulation. - pll.A = self.x_pll_rescale @ pll.A @ scipy.linalg.inv(self.x_pll_rescale) - pll.B = self.x_pll_rescale @ pll.B - pll.C = pll.C @ scipy.linalg.inv(self.x_pll_rescale) + #pll.A = self.x_pll_rescale @ pll.A @ scipy.linalg.inv(self.x_pll_rescale) + #pll.B = self.x_pll_rescale @ pll.B + #pll.C = pll.C @ scipy.linalg.inv(self.x_pll_rescale) # Outer control + DC capacitor dynamics Kp, Ki, Cdc, Tload, r_dc = self.kp_oc_pu, self.ki_oc_puHz, self.c_dc, self.Tload, self.r_dc @@ -186,7 +185,7 @@ def _build_small_signal_model(self): [0, -1/r_dc, -wb/Cdc], [0, 0, -1/Tload]]), B = np.array([[-Ki, 0, 0], - [0, -wb/Cdc, -wb/Cdc], + [0, 0, -wb/Cdc], [0, 1/Tload, 0]]), C = np.array([[1, Kp, 0], [0, 1, 0]]), @@ -210,10 +209,10 @@ def _build_small_signal_model(self): b1 = self.emt_init.v_vsc_d/v_dc # i1d b2 = self.emt_init.i_vsc_d/v_dc #e1d - b3 = - (self.emt_init.i_vsc_d/v_dc)*(self.lf1_pu+self.lf2_pu) #i2q + b3 = - (self.emt_init.i_vsc_d/v_dc)*(self.xf1_pu+self.xf2_pu) #i2q b4 = self.emt_init.v_vsc_q/v_dc # i1q b5 = self.emt_init.i_vsc_q/v_dc # e1q - b6 = (self.emt_init.i_vsc_q/v_dc)*(self.lf1_pu+self.lf2_pu) #i2d + b6 = (self.emt_init.i_vsc_q/v_dc)*(self.xf1_pu+self.xf2_pu) #i2d b7 = - self.emt_init.i_dc/v_dc #vdc b8 = -(self.emt_init.i_vsc_q/v_dc)*self.beta*vmag_bus # theta @@ -221,8 +220,8 @@ def _build_small_signal_model(self): Fccm = np.vstack( ( np.hstack((np.zeros((10, )), 1, 0)) ,# i2d_ref np.zeros((12,)), # i2q_ref np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,6)))), # i2dq_c - [1, 0, 0, 0, 0, -(lf1+lf2), 0, 0, 0, 0, 0, 0], # v1d_c - [0, 1, 0, 0, (lf1+lf2), 0, 0, 0, -beta*vmag_bus, 0, 0, 0], # v1q_c + [1, 0, 0, 0, 0, -(xf1+xf2), 0, 0, 0, 0, 0, 0], # v1d_c + [0, 1, 0, 0, (xf1+xf2), 0, 0, 0, -beta*vmag_bus, 0, 0, 0], # v1q_c np.zeros((12, )) , # v2d_c np.append( np.zeros((8,)) , [-vmag_bus, 0, 0, 0] ), # v2q_c np.append( np.zeros((9,)) , [1, 0, 0] ), # w @@ -290,14 +289,14 @@ def _calculate_emt_initial_conditions(self): i_bus_DQ = (p_bus - q_bus * 1j) / np.conjugate(v_bus_DQ) # Voltage across the shunt element in the LCL filter - v_lcl_sh_DQ = v_bus_DQ + (self.rf2_pu + self.lf2_pu * 1j) * i_bus_DQ + v_lcl_sh_DQ = v_bus_DQ + (self.rf2_pu + self.xf2_pu * 1j) * i_bus_DQ # Current flowing through shunt element of LCL filter i_lcl_sh_DQ = v_lcl_sh_DQ * (self.csh_pu * 1j) + v_lcl_sh_DQ / self.rsh_pu # Current sent from the beginning of the LCL filter i_vsc_DQ = i_bus_DQ + i_lcl_sh_DQ - v_vsc_DQ = v_lcl_sh_DQ + (self.rf1_pu + self.lf1_pu * 1j) * i_vsc_DQ + v_vsc_DQ = v_lcl_sh_DQ + (self.rf1_pu + self.xf1_pu * 1j) * i_vsc_DQ # We refer the voltage and currents to the synchronous frames of the # inverter @@ -309,9 +308,9 @@ def _calculate_emt_initial_conditions(self): v_lcl_sh_dq = v_lcl_sh_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) - # Initial conditions for the integral controller + # Initial conditions for the current controller pi_cc_dq = ( - v_vsc_dq - self.beta * v_bus_dq - 1j * (self.lf1_pu + self.lf2_pu) * i_bus_dq + v_vsc_dq - self.beta * v_bus_dq - 1j * (self.xf1_pu + self.xf2_pu) * i_bus_dq ) # DC-side initial conditions @@ -422,8 +421,8 @@ def get_derivative_state_emt(self): e_d = pi_cc_d + self.kp_cc_pu * (i_bus_d_ref - i_bus_d) e_q = pi_cc_q + self.kp_cc_pu * (i_bus_q_ref - i_bus_q) - v_vsc_d = e_d + self.beta * v_bus_d - (self.lf1_pu + self.lf2_pu) * i_bus_q - v_vsc_q = e_q + self.beta * v_bus_q + (self.lf1_pu + self.lf2_pu) * i_bus_d + v_vsc_d = e_d + self.beta * v_bus_d - (self.xf1_pu + self.xf2_pu) * i_bus_q + v_vsc_q = e_q + self.beta * v_bus_q + (self.xf1_pu + self.xf2_pu) * i_bus_d # calculate idc i_vsc_d, i_vsc_q, _ = abc2dq0(i_vsc_a, i_vsc_b, i_vsc_c, theta_pll) @@ -471,7 +470,7 @@ def pll_dynamics(y, internal_inputs): v_bus_q = internal_inputs # Define ODEs that describe the dynamics of the PLL - d_theta_pll = kp_pll * v_bus_q * w_base + gamma_pll + w_base + d_theta_pll = kp_pll * v_bus_q + gamma_pll + w_base d_gamma_pll = ki_pll * v_bus_q return [d_theta_pll, d_gamma_pll] @@ -479,9 +478,7 @@ def pll_dynamics(y, internal_inputs): def lcl_filter_dynamics(y, internal_inputs): """ It returns the differential equations that describe the dynamics of the LCL filter. - The LCL filter connects the VSC to the grid. It has three branches: the first branch (RL) connects - the VSC to the shunt element, the second branch is the shunt element (RC), and the third branch (RL) - connects the shunt element to the grid. + The LCL filter connects the VSC to the grid. It has three branches: the first branch (RL) connects the VSC to the shunt element, the second branch is the shunt element (RC), and the third branch (RL) connects the shunt element to the grid. """ # Definition of states for the ODEs of the LCL filter @@ -491,9 +488,9 @@ def lcl_filter_dynamics(y, internal_inputs): # Extract the list of parameters r1 = self.rf1_pu # resistance [p.u.] of first branch of filter - l1 = self.lf1_pu # inductance [p.u.] of first branch of filter + l1 = self.xf1_pu # inductance [p.u.] of first branch of filter r2 = self.rf2_pu # resistance [p.u.] of second branch of filter - l2 = self.lf2_pu # inductance [p.u.] of second branch of filter + l2 = self.xf2_pu # inductance [p.u.] of second branch of filter rsh = self.rsh_pu # resistance [p.u.] of series RC shunt csh = self.csh_pu # capacitance [p.u.] of series RC shunt wb = self.wbase # nominal frequency of the system @@ -517,6 +514,9 @@ def lcl_filter_dynamics(y, internal_inputs): return [di_vsc_a, di_vsc_b, di_vsc_c, dv_sh_a, dv_sh_b, dv_sh_c, di_bus_a, di_bus_b, di_bus_c] def outer_loop_and_dc_side(y, internal_inputs): + """ + Dynamics of PI controller (of Vdc), Vdc (capacitor dynamics based on current balance), and the load (current source). + """ int_vdc, v_dc, i_load = y[0], y[1], y[2] v_dc_ref, i_load_ref, i_dc = internal_inputs @@ -556,21 +556,22 @@ def plot_results_emt(self) -> DynamicalVariables: v_sh_d, v_sh_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, theta_pll)]) i_bus_d, i_bus_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, theta_pll)]) + # Additional quantities to plot v_sh_dq = v_sh_d + np.multiply(v_sh_q, 1j) i_vsc_dq = i_vsc_d + np.multiply(i_vsc_q, 1j) i_bus_dq = i_bus_d + np.multiply(i_bus_q, 1j) - v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.lf1_pu * 1j), i_vsc_dq) + v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.xf1_pu * 1j), i_vsc_dq) p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real - p_cap = (v_sh_dq*np.conjugate(i_bus_dq)).real - q_cap = (v_sh_dq*np.conjugate(i_bus_dq)).imag + p_sh = (v_sh_dq*np.conjugate(i_bus_dq)).real + q_sh = (v_sh_dq*np.conjugate(i_bus_dq)).imag p_load = v_dc*i_load results = DynamicalVariables( - name=['pi_cc_d', 'pi_cc_q', 'theta_pll', 'gamma_pll', 'i_vsc_d', 'i_vsc_q', 'v_sh_d', 'v_sh_q', 'i_bus_d', 'i_bus_q', 'int_vdc', 'v_dc', 'i_load', 'p_vsc', 'p_cap', 'pload', 'q_cap'], + name=['pi_cc_d', 'pi_cc_q', 'theta_pll', 'gamma_pll', 'i_vsc_d', 'i_vsc_q', 'v_sh_d', 'v_sh_q', 'i_bus_d', 'i_bus_q', 'int_vdc', 'v_dc', 'i_load', 'p_vsc', 'p_sh', 'pload', 'q_sh'], component=f"{self.type_}_{self.id}", - value=[pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, int_vdc, v_dc, i_load, p_vsc, p_cap, p_load, q_cap], + value=[pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, int_vdc, v_dc, i_load, p_vsc, p_sh, p_load, q_sh], time=tps ) return results \ No newline at end of file From dfc21c8a1b3e8d8f845307ccb7f61bf7700d9157 Mon Sep 17 00:00:00 2001 From: rkravis Date: Tue, 12 May 2026 18:55:10 -0700 Subject: [PATCH 5/6] added: gfli_d (dc dynamics+battery), minor edits to gfli_e and gfmi_e --- .../active_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/bus_voltage.csv | 4 +- .../outputs/ac_power_flow/costs_summary.csv | 2 +- .../ac_power_flow/generator_dispatch.csv | 4 +- .../outputs/ac_power_flow/line_flows.csv | 2 +- .../outputs/ac_power_flow/load_shedding.csv | 4 +- .../reactive_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/solver_status.csv | 2 +- .../2-bus-src-gfmi_e/inputs/gfmi_e.csv | 2 +- .../outputs/ac_power_flow/solver_status.csv | 2 +- .../outputs/small_signal_model/A.csv | 2 +- .../outputs/small_signal_model/B.csv | 2 +- .../outputs/small_signal_model/u.csv | 2 +- .../outputs/small_signal_model/x.csv | 10 +- .../2-bus-src-gfmi_e/run.py | 26 +- .../2-bus-src-gfmi_e/run_compare_ssm_emt.py | 10 +- .../inputs/infinite_sources.csv | 2 +- .../2-bus_src-gfli_a/inputs/lines.csv | 2 +- .../active_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/bus_voltage.csv | 2 +- .../ac_power_flow/generator_dispatch.csv | 4 +- .../outputs/ac_power_flow/line_flows.csv | 2 +- .../outputs/ac_power_flow/load_shedding.csv | 4 +- .../reactive_power_balance_by_bus.csv | 4 +- .../outputs/ac_power_flow/solver_status.csv | 2 +- .../2-bus_src-gfli_a/run.py | 9 +- .../2-bus_src-gfli_d/inputs/buses.csv | 3 + .../2-bus_src-gfli_d/inputs/gfli_d.csv | 2 + .../inputs/infinite_sources.csv | 2 + .../2-bus_src-gfli_d/inputs/lines.csv | 2 + .../2-bus_src-gfli_d/inputs/loads.csv | 3 + .../2-bus_src-gfli_d/inputs/timepoints.csv | 2 + .../active_power_balance_by_bus.csv | 3 + .../outputs/ac_power_flow/bus_voltage.csv | 3 + .../outputs/ac_power_flow/costs_summary.csv | 2 + .../ac_power_flow/generator_dispatch.csv | 3 + .../outputs/ac_power_flow/line_flows.csv | 2 + .../outputs/ac_power_flow/load_shedding.csv | 3 + .../reactive_power_balance_by_bus.csv | 3 + .../outputs/ac_power_flow/solver_status.csv | 5 + .../component_connection_matrices/F.csv | 20 + .../component_connection_matrices/G.csv | 20 + .../component_connection_matrices/H.csv | 11 + .../component_connection_matrices/L.csv | 11 + .../2-bus_src-gfli_d/run.py | 70 ++ .../outputs/ac_power_flow/solver_status.csv | 2 +- sting/generator/gfli_d.py | 616 ++++++++++++++++++ sting/generator/gfli_e.py | 36 +- sting/generator/gfmi_e.py | 405 ++++++------ sting/system/core.py | 2 + 50 files changed, 1093 insertions(+), 255 deletions(-) create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/buses.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/infinite_sources.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/lines.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/loads.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/timepoints.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv create mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/run.py create mode 100644 sting/generator/gfli_d.py diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv index 6c7977c..867ad1b 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,timepoint_1,-39.80828383129473,-9.974940963696508e-9,0.0,-39.808283840763764 -santiago,timepoint_1,50.0,-9.97494096379835e-9,0.0,49.999999989909305 +lima,t_1,90.0582035350247,-9.97494096631705e-9,0.0,90.05820352504989 +santiago,t_1,-80.0,-9.974940966510371e-9,0.0,-80.00000000997504 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv index 66021f4..bbf6498 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv @@ -1,3 +1,3 @@ id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg -0,lima,timepoint_1,0.9943823157326167,0.0 -1,santiago,timepoint_1,1.0045025151823652,5.136441290604731 +0,lima,t_1,1.0,0.0 +1,santiago,t_1,0.9985756950410833,-2.439253738537986 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv index 6ef2ea8..5521476 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv @@ -1,2 +1,2 @@ component,cost -total_cost_USD,-0.00003989976385267742 +total_cost_USD,-800.0000398997639 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv index 9a20949..7b8db7e 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv @@ -1,3 +1,3 @@ id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,timepoint_1,-39.80828383129473,-7.380487609970588 -0,gfli_e,solar,timepoint_1,50.0,-1.8861486979909645 +0,infinite_sources,gen1,t_1,90.0582035350247,2.2903854129179724 +0,gfli_e,solar,t_1,-80.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv index 8f4a02e..a69015f 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv @@ -1,2 +1,2 @@ line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,-39.80828384076382,-7.380487617787122,49.9999999899092,-1.8861487031750395,10.191716149145378,-9.266636320962162 +tx_1,lima,santiago,inf,90.05820352504956,2.2903854029422632,-80.00000000997518,-9.975292198719353e-9,10.058203515074382,2.290385392966971 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv index ffef917..35eb28c 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv @@ -1,3 +1,3 @@ bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,timepoint_1,-9.974940963696508e-9,-9.974940964376057e-9 -santiago,timepoint_1,-9.97494096379835e-9,-9.974940960806504e-9 +lima,t_1,-9.97494096631705e-9,-9.974940964406027e-9 +santiago,t_1,-9.974940966510371e-9,-9.974940967337148e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv index fb89795..675272d 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,timepoint_1,-7.380487609970588,-9.974940964376057e-9,0.0,-7.380487617787122 -santiago,timepoint_1,-1.8861486979909645,-9.974940960806504e-9,0.0,-1.8861487031749502 +lima,t_1,2.2903854129179724,-9.974940964406027e-9,0.0,2.2903854029429738 +santiago,t_1,0.0,-9.974940967337148e-9,0.0,-9.974937433366244e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv index 61ad3cd..79f46b3 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.06075906753540039 +time_spent_seconds,0.06361007690429688 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv index 5e0b55d..5cd4c69 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/inputs/gfmi_e.csv @@ -1,2 +1,2 @@ name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,h_s,kd_pu,droop_q_pu,tau_pc_s,kp_vc_pu,ki_vc_puHz,kp_v_dc,ki_v_dc,kp_i_L,ki_i_L,l_dc,c_dc,v_dc_ref,v_s,Ti_L,Tv_dc,Ti_dc,Kff_idc,Kff_iload,Ti_load,Tload,i_load_ref,Pbat_max_pu,SOC_max_pu,SOC_init_pu -solar,santiago,-50,-50,-10,10,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0.5,70,0.01,0.001,1,10,1.2,20,1,10,0.1,20,1.05,0.5,0.01,0.01,0.01,1,1,0.01,0.001,0.5,1.5,0.25,0.125 \ No newline at end of file +solar,santiago,-50,-50,-10,10,1.00E+02,4.80E-01,60,0,0.02,0.1,10,1,1.00E+02,4.80E-01,2.30E+02,0.01,0.1,0.02,0.1,0.5,70,0.01,0.001,1,10,1.2,20,1,10,0.1,20,1.05,0.5,0.01,0.01,0.01,1,1,0.01,0.001,0.3,1.5,0.25,0.125 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv index 9d22778..69a4dea 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.11365103721618652 +time_spent_seconds,0.06470894813537598 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv index 84befb8..08b6962 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/A.csv @@ -19,7 +19,7 @@ Index,"('infinite_sources_0', 'i_bus_d')","('infinite_sources_0', 'i_bus_q')","( "('gfmi_e_0', 'x_1')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-20.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'x_2')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-10.0,-12.0,10.0,10.0,10.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_L')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-3958.406743523139,-4750.0880922277665,3958.406743523139,3958.406743523139,3958.406743523139,3958.406743523139,0.0,-1795.1958020513098,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.0,0.0,-0.07571337144916976,7.571337144916975,-15.932627309401225,-1.9616030330278627e-15,0.0,0.0,-7.569144642832431,-0.18219642664071545,5.680719791051915,6.816863749262298,-5.680719791051915,-5.680719791051915,-5.680719791051915,-5.680719791051915,8.97597901025655,-6.399688855584481,-18.84955592153876,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.0,0.0,-0.07571337144916976,7.571337144916975,-15.932627309401225,-1.9616030330278627e-15,0.0,0.0,-7.569144642832431,-0.18219642664071545,-2.236093695994364,-2.6833124351932365,2.236093695994364,2.236093695994364,2.236093695994364,2.236093695994364,8.97597901025655,-6.399688855584481,-18.84955592153876,0.0,0.0,0.0,0.0,0.0,0.0 "('gfmi_e_0', 'i_load')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1000.0,0.0,0.0,0.0,0.0,0.0,0.0 "('shunt_parallel_rc_0', 'v_bus_D')",5423.402824069048,-1601.3181281788504,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-282.7433374093647,376.99111843077515,0.0,0.0,-5654.8667481872935,0.0 "('shunt_parallel_rc_0', 'v_bus_Q')",1601.3181281788504,5423.402824069048,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-376.99111843077515,-282.7433374093647,0.0,0.0,0.0,-5654.8667481872935 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv index 3553091..76eedf1 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/B.csv @@ -19,7 +19,7 @@ Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","( "('gfmi_e_0', 'x_1')",0.0,0.0,0.0,0.0,0.0,20.0,0.0,0.0 "('gfmi_e_0', 'x_2')",0.0,0.0,0.0,0.0,0.0,12.0,0.0,0.0 "('gfmi_e_0', 'i_L')",0.0,0.0,0.0,0.0,0.0,4750.0880922277665,3769.9111843077517,0.0 -"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.07571337144916976,7.571337144916975,-6.816863749262298,0.0,0.0 +"('gfmi_e_0', 'v_dc')",0.0,0.0,0.0,0.07571337144916976,7.571337144916975,2.6833124351932365,0.0,0.0 "('gfmi_e_0', 'i_load')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1000.0 "('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 "('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv index 6d1790e..f28156b 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/u.csv @@ -6,4 +6,4 @@ q_ref,gfmi_e_0,device,0.05787698596992697 v_ref,gfmi_e_0,device,0.9882122222891866 v_dc_ref,gfmi_e_0,device,1.05 v_s,gfmi_e_0,device,0.5 -i_load_ref,gfmi_e_0,device,0.5 +i_load_ref,gfmi_e_0,device,0.3 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv index 787fd4f..17ce8e3 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/outputs/small_signal_model/x.csv @@ -12,15 +12,15 @@ i_bus_d,gfmi_e_0,"",-0.49676780316078684 i_bus_q,gfmi_e_0,"",-0.07054199784602057 v_lcl_sh_d,gfmi_e_0,"",0.9879260565411991 v_lcl_sh_q,gfmi_e_0,"",0.02378030884341824 -i_l_f,gfmi_e_0,"",0.30137154502195707 +i_l_f,gfmi_e_0,"",-0.11862845497804296 v_dc_f,gfmi_e_0,"",1.05 i_dc_f,gfmi_e_0,"",-0.35648974046573473 -i_load_f,gfmi_e_0,"",0.5 -x_1,gfmi_e_0,"",0.15786128548769174 +i_load_f,gfmi_e_0,"",0.3 +x_1,gfmi_e_0,"",-0.062138714512308235 x_2,gfmi_e_0,"",0.5238095238095238 -i_L,gfmi_e_0,"",0.30137154502195707 +i_L,gfmi_e_0,"",-0.11862845497804296 v_dc,gfmi_e_0,"",1.05 -i_load,gfmi_e_0,"",0.5 +i_load,gfmi_e_0,"",0.3 v_bus_D,shunt_parallel_rc_0,"",1.0039676883731015 v_bus_Q,shunt_parallel_rc_0,"",0.0 v_bus_D,shunt_parallel_rc_1,"",0.9580798748577896 diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py index 2908494..615f5e4 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run.py @@ -7,8 +7,14 @@ from scipy import signal import numpy as np +import sys +sys.path.append("/Users/ruthkravis/Documents/STING") # Import sting package + from sting import main +from sting.system.core import System + + # from sting.system.core import System # from sting.system.operations import SystemModifier # from sting.modules.power_flow.core import ACPowerFlow @@ -20,39 +26,43 @@ # Construct system and small-signal model def step1(t): - return 0.5 if t >= 0.5 else 0.0 + return 0.3 if t >= 0.2 else 0.0 def step2(t): return 0.0 def step3(t): - return 0.25 if t > 1.0 else 0.0 + return 0.1 if t > 1.0 else 0.0 def step3_neg(t): return -0.25 if t > 1.0 else 0.0 +def sin_oscillation(t): + return 0.05*np.sin(2*np.pi*20*t) if t < 1 else 0 #1 Hz oscillation + def square_oscillation(t): - osc = 0.1*signal.square(2 * np.pi * 14 * t) + osc = 0.05*signal.square(2 * np.pi * 14 * t) return osc # Specify inputs to excite - any constant input does not need to be specified # NB: input is a perturbation from the nominal value inputs = {'infinite_sources_0': {'v_ref_d': step2}, - 'gfmi_e_0': {'p_ref': step3_neg, + 'gfmi_e_0': {'p_ref': step2, 'q_ref': step2, 'v_ref': step2, 'v_dc_ref': step2, 'v_s': step2, - 'i_load_ref': step3}} + 'i_load_ref': sin_oscillation}} -t_max = 4.0 +t_max = 1.0 # Construct system and small-signal model -sys, ssm = main.run_ssm(case_directory=case_dir) +_, ssm = main.run_ssm(case_directory=case_dir) ssm.simulate_ssm(t_max=t_max, inputs=inputs) -# Run EMT simulation main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) +#run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) + # # Load system from CSV files # sys = System.from_csv(case_directory=case_dir) diff --git a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py index 1785da0..5ea78ad 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py @@ -18,6 +18,7 @@ from plotly.subplots import make_subplots from pathlib import Path from typing import Callable +from scipy import signal # Import sting package from sting import main @@ -128,15 +129,20 @@ def p_ref(t): def slow_small_oscillation(t): return 0.1*np.sin(2*np.pi*1*t) if t < 1 else 0 #1 Hz oscillation + +def square_oscillation(t): + osc = 0.1*signal.square(2 * np.pi * 14 * t) + return osc + # Specify inputs to excite - any constant input does not need to be specified # NB: input is a perturbation from the nominal value inputs = {'infinite_sources_0': {'v_ref_d': no_step}, - 'gfmi_e_0': {'p_ref': step1, + 'gfmi_e_0': {'p_ref': no_step, 'q_ref': no_step, 'v_ref': no_step, 'v_dc_ref': no_step, 'v_s': no_step, - 'i_load_ref': slow_small_oscillation}} + 'i_load_ref': square_oscillation}} t_max = 4.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/infinite_sources.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/infinite_sources.csv index 9d95931..fed2eb7 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/infinite_sources.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/infinite_sources.csv @@ -1,2 +1,2 @@ name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,r_pu,x_pu -gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.5 \ No newline at end of file +gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.01,0.04 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/lines.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/lines.csv index 74a86b2..143dc35 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/lines.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/inputs/lines.csv @@ -1,2 +1,2 @@ name,from_bus,to_bus,base_power_MVA,base_voltage_kV,base_frequency_Hz,r_pu,x_pu,g_pu,b_pu -tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.01,0.5,0.05,0.066666667 \ No newline at end of file +tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.001,0.2,0.05,0.0066666667 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/active_power_balance_by_bus.csv index 096f4f7..73c2a94 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,t_1,-67.08056522433783,-9.97494096311732e-9,0.0,-67.08056523417291 -santiago,t_1,80.0,-9.974940965584796e-9,0.0,79.99999998990339 +lima,t_1,-69.04077956682313,-9.974940963069624e-9,0.0,-69.04077957676964 +santiago,t_1,80.0,-9.97494096281436e-9,0.0,79.99999998999941 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/bus_voltage.csv index b996b04..f00a9b0 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/bus_voltage.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/bus_voltage.csv @@ -1,3 +1,3 @@ id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg 0,lima,t_1,1.0,0.0 -1,santiago,t_1,1.2093116675812525,17.191975663648932 +1,santiago,t_1,1.0854031619193631,7.82145244237828 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/generator_dispatch.csv index feec2d2..3c6384c 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/generator_dispatch.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/generator_dispatch.csv @@ -1,3 +1,3 @@ id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,t_1,-67.08056522433783,-36.2809234833063 -0,gfli_a,solar,t_1,80.0,50.227752474633064 +0,infinite_sources,gen1,t_1,-69.04077956682313,-37.9492675668311 +0,gfli_a,solar,t_1,80.0,50.241259638158475 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/line_flows.csv index 365c6aa..dd21bb9 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/line_flows.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/line_flows.csv @@ -1,2 +1,2 @@ line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,-67.08056523417287,-36.28092349329843,79.99999998990337,50.22775246504935,12.919434755730506,13.946828971750918 +tx_1,lima,santiago,inf,-69.04077957676962,-37.9492675768077,79.99999998999951,50.24125962836842,10.959220413229886,12.291992051560719 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/load_shedding.csv index 3562de7..77bb571 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/load_shedding.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/load_shedding.csv @@ -1,3 +1,3 @@ bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,t_1,-9.97494096311732e-9,-9.974940964300128e-9 -santiago,t_1,-9.974940965584796e-9,-9.974940954696962e-9 +lima,t_1,-9.974940963069624e-9,-9.974940964295529e-9 +santiago,t_1,-9.97494096281436e-9,-9.974940965925015e-9 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/reactive_power_balance_by_bus.csv index d1ff11e..4fe968c 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -1,3 +1,3 @@ bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,t_1,-36.2809234833063,-9.974940964300128e-9,0.0,-36.28092349329843 -santiago,t_1,50.227752474633064,-9.974940954696962e-9,0.0,50.22775246504937 +lima,t_1,-37.9492675668311,-9.974940964295529e-9,0.0,-37.949267576807614 +santiago,t_1,50.241259638158475,-9.974940965925015e-9,0.0,50.24125962836832 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/solver_status.csv index 24d3832..dfda357 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.02163100242614746 +time_spent_seconds,0.15887808799743652 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_a/run.py b/examples/small_signal_and_emt/2-bus_src-gfli_a/run.py index 7604bac..d86bebc 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_a/run.py +++ b/examples/small_signal_and_emt/2-bus_src-gfli_a/run.py @@ -21,9 +21,14 @@ │ -1537.006 ┆ 0.0 ┆ 244.622 ┆ 1.0 ┆ 0.0007 │ └───────────┴───────────┴──────────────────────┴──────────────────┴───────────────────────┘ """ +import sys +sys.path.append('/Users/ruthkravis/Documents/STING') # Import Python standard and third-party packages from pathlib import Path +import sys +sys.path.append("/Users/ruthkravis/Documents/STING") + # Import sting package from sting import main from sting.system.core import System @@ -52,8 +57,8 @@ def step2(t): # Construct system and small-signal model _, ssm = main.run_ssm(case_directory=case_dir) -ssm.simulate_ssm(t_max=t_max, inputs=inputs) +#ssm.simulate_ssm(t_max=t_max, inputs=inputs) # Run EMT simulation (not implemented yet for gfli_a) -main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) +#main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) print('\nok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/buses.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/buses.csv new file mode 100644 index 0000000..b50acd6 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/buses.csv @@ -0,0 +1,3 @@ +name,base_power_MVA,base_voltage_kV,base_frequency_Hz,minimum_voltage_pu,maximum_voltage_pu +lima,1.00E+02,2.30E+02,60,1,1 +santiago,1.00E+02,2.30E+02,60,0.95,1.3 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv new file mode 100644 index 0000000..e0dcc29 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv @@ -0,0 +1,2 @@ +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,beta,kp_pll_pu,ki_pll_puHz,kp_cc_pu,ki_cc_puHz,kp_v_dc,ki_v_dc,kp_i_L,ki_i_L,l_dc,c_dc,v_dc_ref,v_s,Ti_L,Tv_dc,Ti_dc,Kff_idc,Kff_iload,Ti_load,Tload +solar,santiago,-80,-80,0,0,1.00E+02,4.80E-01,60,10,0.001,0.05,1,0.1,1.00E+02,4.80E-01,2.30E+02,0.002,0.05,0.002,0.05,0.75,100,2500,0.45,6,5,50,1,10,0.1,20,1.05,0.5,0.01,0.01,0.01,1,1,0.01,0.0001 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/infinite_sources.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/infinite_sources.csv new file mode 100644 index 0000000..b13b274 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/infinite_sources.csv @@ -0,0 +1,2 @@ +name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,r_pu,x_pu +gen1,lima,-200,200,-500,500,1.00E+02,2.30E+02,60,0,0.001,0.02 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/lines.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/lines.csv new file mode 100644 index 0000000..3b76ba4 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/lines.csv @@ -0,0 +1,2 @@ +name,from_bus,to_bus,base_power_MVA,base_voltage_kV,base_frequency_Hz,r_pu,x_pu,g_pu,b_pu +tx_1,lima,santiago,1.00E+02,2.30E+02,60,0.001,0.05,0.05,0.006666667 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/loads.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/loads.csv new file mode 100644 index 0000000..ed9aa45 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/loads.csv @@ -0,0 +1,3 @@ +bus,timepoint,load_MW,load_MVAR +lima,t_1,0,0 +santiago,t_1,0,0 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/timepoints.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/timepoints.csv new file mode 100644 index 0000000..fb3202e --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/timepoints.csv @@ -0,0 +1,2 @@ +name +t_1 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv new file mode 100644 index 0000000..867ad1b --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv @@ -0,0 +1,3 @@ +bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW +lima,t_1,90.0582035350247,-9.97494096631705e-9,0.0,90.05820352504989 +santiago,t_1,-80.0,-9.974940966510371e-9,0.0,-80.00000000997504 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv new file mode 100644 index 0000000..bbf6498 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv @@ -0,0 +1,3 @@ +id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg +0,lima,t_1,1.0,0.0 +1,santiago,t_1,0.9985756950410833,-2.439253738537986 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv new file mode 100644 index 0000000..5521476 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv @@ -0,0 +1,2 @@ +component,cost +total_cost_USD,-800.0000398997639 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv new file mode 100644 index 0000000..645bbc7 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv @@ -0,0 +1,3 @@ +id,type,generator,timepoint,active_power_MW,reactive_power_MVAR +0,infinite_sources,gen1,t_1,90.0582035350247,2.2903854129179724 +0,gfli_d,solar,t_1,-80.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv new file mode 100644 index 0000000..a69015f --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv @@ -0,0 +1,2 @@ +line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR +tx_1,lima,santiago,inf,90.05820352504956,2.2903854029422632,-80.00000000997518,-9.975292198719353e-9,10.058203515074382,2.290385392966971 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv new file mode 100644 index 0000000..35eb28c --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv @@ -0,0 +1,3 @@ +bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR +lima,t_1,-9.97494096631705e-9,-9.974940964406027e-9 +santiago,t_1,-9.974940966510371e-9,-9.974940967337148e-9 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv new file mode 100644 index 0000000..675272d --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv @@ -0,0 +1,3 @@ +bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR +lima,t_1,2.2903854129179724,-9.974940964406027e-9,0.0,2.2903854029429738 +santiago,t_1,0.0,-9.974940967337148e-9,0.0,-9.974937433366244e-9 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv new file mode 100644 index 0000000..34b0df2 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv @@ -0,0 +1,5 @@ +attribute,value +solver_name,ipopt +solver_status,ok +termination_condition,optimal +time_spent_seconds,0.06843376159667969 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv new file mode 100644 index 0000000..6e099b6 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv @@ -0,0 +1,20 @@ +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_d_0', 'i_bus_D')","('gfli_d_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'v_ref_d')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_d_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_q_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'v_dc_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'v_s')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('gfli_d_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv new file mode 100644 index 0000000..21a08d2 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv @@ -0,0 +1,20 @@ +Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_d_0', 'i_bus_d_ref')","('gfli_d_0', 'i_bus_q_ref')","('gfli_d_0', 'v_dc_ref')","('gfli_d_0', 'v_s')","('gfli_d_0', 'i_load_ref')" +"('infinite_sources_0', 'v_ref_d')",1.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_ref_q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_d_ref')",0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_q_ref')",0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('gfli_d_0', 'v_dc_ref')",0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('gfli_d_0', 'v_s')",0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('gfli_d_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,1.0 +"('gfli_d_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv new file mode 100644 index 0000000..4bd0f1d --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv @@ -0,0 +1,11 @@ +Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_d_0', 'i_bus_D')","('gfli_d_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" +"('infinite_sources_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv new file mode 100644 index 0000000..0bb906b --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv @@ -0,0 +1,11 @@ +Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_d_0', 'i_bus_d_ref')","('gfli_d_0', 'i_bus_q_ref')","('gfli_d_0', 'v_dc_ref')","('gfli_d_0', 'v_s')","('gfli_d_0', 'i_load_ref')" +"('infinite_sources_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('gfli_d_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 +"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py b/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py new file mode 100644 index 0000000..8ab2abe --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py @@ -0,0 +1,70 @@ +""" +Simulates an infinite source and GFLI_d connected via a transmission line. + +""" +# Import Python standard and third-party packages +from pathlib import Path +import sys +sys.path.append("/Users/ruthkravis/Documents/STING") + +import numpy as np +import pandas as pd +from scipy.linalg import eig, inv +import seaborn as sns +import matplotlib.pyplot as plt + +# Import sting package +from sting import main +from sting.system.core import System + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Construct system and small-signal model +sys = System.from_csv(case_directory=case_dir) + +# Step function to simulate +def step1(t): + return 0.1 if t >= 0.5 else 0.0 + +def step2(t): + return 0.0 + +inputs = { + 'infinite_sources_0': { + 'v_ref_d': step2 + }, + 'gfli_d_0': { + 'i_bus_d_ref': step1} + } +t_max = 2.0 # Simulation length + +# Construct system and small-signal model +_, ssm = main.run_ssm(case_directory=case_dir) +ssm.simulate_ssm(t_max=t_max, inputs=inputs) +# Run EMT simulation (not implemented yet for gfli_a) +main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) + +# def participation_factor_plot(ssm): +# w,vr = eig(ssm.model.A) +# vl = inv(vr) # ensures normalization + +# p = np.zeros_like(vl) +# for i in range(len(w)): +# for k in range(len(w)): +# # use correct formula for complex eigenvalues +# p[k, i] = (vl[i,k].real)**2/(np.dot(vl[i,:].real,vl[i,:].real)) # participation of k-th state (row) in i-th mode (column) + +# pf_df = pd.DataFrame(p.real, index=ssm.model.x.name) +# fig = plt.figure(figsize=(12,8)) +# ax = sns.heatmap(pf_df, yticklabels=pf_df.index, xticklabels=np.round(w.real,3), linewidths=1, linecolor='white') +# ax.set_yticklabels(ax.get_yticklabels(), fontsize=8) +# ax.set_xticklabels(ax.get_xticklabels(), fontsize=8) +# plt.show() +# plt.savefig("ssm.png") + + +# participation_factor_plot(ssm) + + +print('\nok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv index 69f8c08..a4c292e 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfm/outputs/ac_power_flow/solver_status.csv @@ -2,4 +2,4 @@ attribute,value solver_name,ipopt solver_status,ok termination_condition,optimal -time_spent_seconds,0.0771188735961914 +time_spent_seconds,0.0744011402130127 diff --git a/sting/generator/gfli_d.py b/sting/generator/gfli_d.py new file mode 100644 index 0000000..0536aa3 --- /dev/null +++ b/sting/generator/gfli_d.py @@ -0,0 +1,616 @@ +""" +This module implements a GFLI that incorporates: +- LCL filter: Two Series RL branches (one branch is the transformer) and one Parallel RC shunt. +- Current controller: A dq-based frame PI controller +- PLL: It that tracks the phase of the grid voltage. The PLL has a proportional and integral gain. +- DC-DC converter for controlling Vdc +- DC side circuit with load modeled as current source +""" +# ---------------------- +# Import python packages +# ---------------------- +import numpy as np +from dataclasses import dataclass +from typing import NamedTuple, Optional +from sting.generator.core import Generator + +# ------------------ +# Import sting code +# ------------------ +from sting.utils.dynamical_systems import StateSpaceModel, DynamicalVariables +from sting.modules.simulation_emt.utils import VariablesEMT +from sting.utils.transformations import dq02abc, abc2dq0 + +# ----------- +# Sub-classes +# ----------- +class InitialConditionsEMT(NamedTuple): + vmag_bus: float + vphase_bus: float + p_bus: float + q_bus: float + angle_ref: float + pi_cc_d: float + pi_cc_q: float + i_vsc_d: float + i_vsc_q: float + i_bus_d: float + i_bus_q: float + v_lcl_sh_d: float + v_lcl_sh_q: float + i_bus_D: float + i_bus_Q: float + v_bus_D: float + v_bus_Q: float + v_vsc_mag: float + v_vsc_DQ_phase: float + v_vsc_d: float + v_vsc_q: float + v_dc: float # DC bus voltage + d: float # DC/DC converter duty cycle + i_dc: float # current into/out of inverter + i_L: float # DC/DC converter inductor current + x_1: float # DC voltage regulator integrator + x_2: float # DC current regulator integrator + i_load_ref: float + p_vsc: float + +# ----------- +# Main class +# ----------- +@dataclass(slots=True, kw_only=True, eq=False) +class GFLId(Generator): + rf1_pu: float + xf1_pu: float + csh_pu: float + rsh_pu: float + txr_power_MVA: float + txr_voltage1_kV: float + txr_voltage2_kV: float + txr_r1_pu: float + txr_x1_pu: float + txr_r2_pu: float + txr_x2_pu: float + beta: float + kp_pll_pu: float + ki_pll_puHz: float + kp_cc_pu: float + ki_cc_puHz: float + kp_v_dc: float # DC voltage regulator P gain + ki_v_dc: float # DC voltage regulator I gain [1/s] + kp_i_L: float # DC current regulator P gain + ki_i_L: float # DC current regulator I gain [1/s] + l_dc: float # DC/DC converter inductance, [pu] + c_dc: float # DC link capacitance, [pu] + v_dc_ref: float # DC bus reference voltage, [pu] + v_s: float # DC voltage source voltage, [pu] + Ti_L: float # measurement filter time constants [s] + Tv_dc: float + Ti_dc: float + Kff_idc: float + Kff_iload: float + Ti_load: float # for DC/DC controller - measurement filter + Tload: float # time constant for actuation of load current change + emt_init: Optional[InitialConditionsEMT] = None + + @property + def rf2_pu(self): + return (self.txr_r1_pu + self.txr_r2_pu) * self.base_power_MVA / self.txr_power_MVA + + @property + def xf2_pu(self): + return (self.txr_x1_pu + self.txr_x2_pu) * self.base_power_MVA / self.txr_power_MVA + + @property + def wbase(self): + return 2 * np.pi * self.base_frequency_Hz + + def _build_small_signal_model(self): + + # Current PI controller + kp_cc, ki_cc = self.kp_cc_pu, self.ki_cc_puHz + pi_cc_d, pi_cc_q = self.emt_init.pi_cc_d, self.emt_init.pi_cc_q + + pi_controller = StateSpaceModel( A = np.zeros((2,2)), + B = ki_cc*np.hstack((np.eye(2), -np.eye(2))), + C = np.eye(2), + D = kp_cc*np.hstack((np.eye(2), -np.eye(2))), + u = DynamicalVariables(name=['i_bus_d_ref', 'i_bus_q_ref', 'i_bus_d', 'i_bus_q']), + y = DynamicalVariables(name=['e_d', 'e_q']), + x = DynamicalVariables( name=['pi_cc_d', 'pi_cc_q'], + init= [pi_cc_d, pi_cc_q]) ) + + # LCL filter + rf1, lf1, rf2, lf2, rsh, csh = self.rf1_pu, self.xf1_pu, self.rf2_pu, self.xf2_pu, self.rsh_pu, self.csh_pu + wb = self.wbase + i_vsc_d, i_vsc_q = self.emt_init.i_vsc_d, self.emt_init.i_vsc_q + i_bus_d, i_bus_q = self.emt_init.i_bus_d, self.emt_init.i_bus_q + v_lcl_sh_d, v_lcl_sh_q = self.emt_init.v_lcl_sh_d, self.emt_init.v_lcl_sh_q + + lcl_filter = StateSpaceModel( + A = wb*np.array([[-rf1/lf1 , 1 , 0 , 0 , -1/lf1 , 0], + [-1 , -rf1/lf1, 0 , 0 , 0 , -1/lf1], + [0 , 0 , -rf2/lf2 , 1 , 1/lf2 , 0], + [0 , 0 , -1 , -rf2/lf2, 0 , 1/lf2], + [1/csh , 0 , -1/csh , 0 , -1/(rsh*csh), 1], + [0 , 1/csh , 0 , -1/csh , -1 , -1/(rsh*csh)]]), + B = wb*np.array([[1/lf1 , 0 , 0 , 0 , i_vsc_q], + [0 , 1/lf1 , 0 , 0 , -i_vsc_d], + [0 , 0 , -1/lf2 , 0 , i_bus_q], + [0 , 0 , 0 , -1/lf2 , -i_bus_d], + [0 , 0 , 0 , 0 , v_lcl_sh_q], + [0 , 0 , 0 , 0 , -v_lcl_sh_d]]), + C = np.hstack((np.eye(4), np.zeros((4,2)))), + D = np.zeros((4,5)), + x = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q", "v_lcl_sh_d", "v_lcl_sh_q"], + init=[i_vsc_d, i_vsc_q, i_bus_d, i_bus_q, v_lcl_sh_d, v_lcl_sh_q]), + u = DynamicalVariables(name=['v_vsc_d', 'v_vsc_q', 'v_bus_d', 'v_bus_q', 'w']), + y = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q"])) + + # Phase-locked loop + kp_pll, ki_pll = self.kp_pll_pu, self.ki_pll_puHz + beta = self.beta + vmag_bus = self.emt_init.vmag_bus + sinphi = np.sin(self.emt_init.angle_ref*np.pi/180) + cosphi = np.cos(self.emt_init.angle_ref*np.pi/180) + int_pll = 0 + phase_pll = self.emt_init.angle_ref*np.pi/180 + + pll = StateSpaceModel( A = np.array([ [ 0 , -vmag_bus*ki_pll], + [1 , -1*vmag_bus*kp_pll]]), + B = np.array([ [-sinphi*ki_pll , +cosphi*ki_pll], + [-1*kp_pll*sinphi, 1*kp_pll*cosphi]]), + C = np.array([ [0 , 1], + [1/wb , -1/wb * vmag_bus*kp_pll]]), + D = np.array([ [0 , 0], + [-1/wb * kp_pll * sinphi , 1/wb * kp_pll * cosphi]]), + u = DynamicalVariables(name=['v_bus_D', 'v_bus_Q']), + y = DynamicalVariables(name=['phase', 'w']), + x = DynamicalVariables(name=["int_pll", "phase_pll"], + init=[int_pll, phase_pll] ) ) + + # DC-DC controller + # Parameters + l_dc, c_dc, TiL, Tvdc, Tidc, Kp_vdc, Ki_vdc, Kp_iL, Ki_iL, Kff_idc, Kff_iload, Ti_load, Tload = self.l_dc, self.c_dc, self.Ti_L, self.Tv_dc, self.Ti_dc, self.kp_v_dc, self.ki_v_dc, self.kp_i_L, self.ki_i_L, self.Kff_idc, self.Kff_iload, self.Ti_load, self.Tload + + # Initial conditions + v_dc, duty_cycle, i_dc, i_L, x1, x2, i_load = self.emt_init.v_dc, self.emt_init.d, self.emt_init.i_dc, self.emt_init.i_L, self.emt_init.x_1, self.emt_init.x_2, self.emt_init.i_load_ref + + + # DC control + dc_dc_controller = StateSpaceModel( + A = np.array([ + [-1/TiL, 0, 0, 0, 0, 0], + [0,-1/Tvdc, 0, 0, 0, 0], + [0, 0, -1/Tidc, 0, 0, 0], + [0, 0, 0, -1/Ti_load, 0, 0], + [0, -Ki_vdc, 0, 0, 0, 0], + [-Ki_iL, -Kp_vdc*Ki_iL, Kff_idc*Ki_iL, Kff_iload*Ki_iL, Ki_iL, 0]]), + B = np.array([ + [1/TiL, 0, 0, 0, 0], + [0, 1/Tvdc, 0, 0, 0], + [0, 0, 1/Tidc, 0, 0], + [0, 0, 0, 1/Ti_load, 0], + [0, 0, 0, 0, Ki_vdc], + [0, 0, 0, 0, Ki_iL*Kp_vdc]]), + + C = np.array([[-Kp_iL, -Kp_iL*Kp_vdc, Kp_iL*Kff_idc, Kp_iL*Kff_iload, Kp_iL, 1]]), + D = np.array([[0, 0, 0, 0, Kp_iL*Kp_vdc]]), + u = DynamicalVariables(name=['i_L', 'v_dc', 'i_dc', 'i_load', 'v_dc_ref']), + y = DynamicalVariables(name=['d']), + x = DynamicalVariables( + name = ['i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2'], + init = [i_L, v_dc, i_dc, i_load, x1, x2] + ) + ) + + # DC circuit - includes capacitor, inductor, and load dynamics + dc_circuit = StateSpaceModel( + A = wb*np.array([[0, (duty_cycle-1)/l_dc, 0], + [(1-duty_cycle)/c_dc, 0, -1/c_dc], + [0, 0, -1/(wb*Tload)]]), + B = wb*np.array([[1/l_dc, v_dc/l_dc, 0, 0], + [0, -i_L/c_dc, -1/c_dc, 0], + [0, 0, 0, 1/(wb*Tload)]]), + C = np.eye(3), + D = np.zeros((3,4)), + u = DynamicalVariables(name=['v_s', 'd', 'i_dc','i_load_ref']), + y = DynamicalVariables(name=['i_L','v_dc', 'i_load']), + x = DynamicalVariables( + name = ['i_L', 'v_dc', 'iload'], + init = [i_L, v_dc, i_load] + ) + ) + + # Construction of CCM matrices + + # dc power balance linearization coefficients + v_dc = self.emt_init.v_dc + + b1 = self.emt_init.v_vsc_d/v_dc # i1d + b2 = self.emt_init.i_vsc_d/v_dc #e1d + b3 = - (self.emt_init.i_vsc_d/v_dc)*(self.xf1_pu+self.xf2_pu) #i2q + b4 = self.emt_init.v_vsc_q/v_dc # i1q + b5 = self.emt_init.i_vsc_q/v_dc # e1q + b6 = (self.emt_init.i_vsc_q/v_dc)*(self.xf1_pu+self.xf2_pu) #i2d + b7 = - self.emt_init.i_dc/v_dc #vdc + b8 = -(self.emt_init.i_vsc_q/v_dc)*self.beta*vmag_bus # phi + + Fccm = np.vstack( ( np.zeros((12, )) ,# i2ref_d + np.zeros((12, )) , # i2ref_q + np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,6)))), # i2c_dq + [1, 0 , 0, 0, 0 , -(lf1+lf2) , 0 , 0, 0, 0, 0, 0], # v1c_d + [0, 1 , 0, 0, (lf1+lf2) , 0 , -beta*vmag_bus , 0, 0, 0, 0, 0], # v1c_q + np.zeros((12, )) , # v2c_d + np.append( np.zeros((1,6)) , [-vmag_bus, 0, 0, 0, 0, 0] ), # v2c_q + np.append( np.zeros((1,7)) , [1, 0, 0, 0, 0] ), # w + np.zeros((2,12)),# v2c_dq + np.hstack((np.zeros(9,), [1, 0, 0])), #iL + np.hstack((np.zeros(9,), [0, 1, 0])), #vdc + [b2, b5, b1, b4, b6, b3, b8, 0, 0, 0, b7, 0], #idc + np.hstack((np.zeros(11,), [1])), #iload + np.zeros((2,12)), #vdc_ref, v_s + np.hstack((np.zeros(8,), [1, 0, 0, 0])), #d + [b2, b5, b1, b4, b6, b3, b8, 0, 0, 0, b7, 0], #idc + np.zeros(12,) # iload_ref + )) + + Gccm = np.vstack(( [1, 0, 0, 0, 0, 0, 0], # i2ref_d + [0, 1, 0, 0, 0, 0, 0], # i2ref_q + np.zeros((2,7)), # i2c_dq + [0, 0, 0, 0, 0, beta*cosphi , beta*sinphi], # v1c_d + [0, 0, 0, 0, 0, -beta*sinphi, beta*cosphi], # v1c_q + [0, 0, 0, 0, 0, cosphi ,sinphi], # v2c_d + [0, 0, 0, 0, 0, -sinphi ,cosphi], # v2c_q + np.zeros((7, )), # w + np.hstack( (np.zeros((2,5)), np.eye(2) ) ),# v2_dq + np.zeros((4,7)), #iL, vdc, idc, iload + [0, 0, 1, 0, 0, 0, 0], #vdc_ref + [0, 0, 0, 1, 0, 0, 0], #vs + np.zeros((2,7)), #d, idc + [0, 0, 0, 0, 1, 0, 0] #i_load_ref + ) ) + + Hccm = np.vstack(( [ 0, 0 , 0, 0, cosphi , -sinphi, -sinphi*i_bus_d-cosphi*i_bus_q, 0, 0, 0, 0, 0], + [0, 0, 0, 0, sinphi , cosphi , cosphi*i_bus_d-sinphi*i_bus_q, 0, 0, 0, 0, 0] )) + + Lccm = np.zeros((2, 7)) + + components = [pi_controller, lcl_filter, pll, dc_dc_controller, dc_circuit] + connections = [Fccm, Gccm, Hccm, Lccm] + + # Inputs and outputs + v_bus_D, v_bus_Q= self.emt_init.v_bus_D, self.emt_init.v_bus_Q + u = DynamicalVariables( + name=["i_bus_d_ref", "i_bus_q_ref", "v_dc_ref", "v_s", "i_load_ref", "v_bus_D", "v_bus_Q"], + type=["device", "device", "device", "device", "device", "grid", "grid"], + init=[i_bus_d, i_bus_q, v_dc, self.v_s, i_load, v_bus_D, v_bus_Q]) + + i_bus_D, i_bus_Q= self.emt_init.i_bus_D, self.emt_init.i_bus_Q + y = DynamicalVariables( + name=['i_bus_D', 'i_bus_Q'], + init=[i_bus_D, i_bus_Q]) + + # Generate small-signal model + self.ssm = StateSpaceModel.from_interconnected(components, connections, u, y, component_label=f"{self.type_}_{self.id}") + + def _calculate_emt_initial_conditions(self): + + # Extract power flow solution + vmag_bus = self.power_flow_variables.vmag_bus + vphase_bus = self.power_flow_variables.vphase_bus + p_bus = self.power_flow_variables.p_bus + q_bus = self.power_flow_variables.q_bus + + # Voltage in the end of the LCL filter + v_bus_DQ = vmag_bus * np.exp(vphase_bus * np.pi / 180 * 1j) + angle_ref = np.angle(v_bus_DQ, deg=True) + + # Current sent from the end of the LCL filter + i_bus_DQ = (p_bus - q_bus * 1j) / np.conjugate(v_bus_DQ) + + # Voltage across the shunt element in the LCL filter + v_lcl_sh_DQ = v_bus_DQ + (self.rf2_pu + self.xf2_pu * 1j) * i_bus_DQ + + # Current flowing through shunt element of LCL filter + i_lcl_sh_DQ = v_lcl_sh_DQ * (self.csh_pu * 1j) + v_lcl_sh_DQ / self.rsh_pu + + # Current sent from the beginning of the LCL filter + i_vsc_DQ = i_bus_DQ + i_lcl_sh_DQ + v_vsc_DQ = v_lcl_sh_DQ + (self.rf1_pu + self.xf1_pu * 1j) * i_vsc_DQ + + # We refer the voltage and currents to the synchronous frames of the + # inverter + v_vsc_dq = v_vsc_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + i_vsc_dq = i_vsc_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + + v_bus_dq = v_bus_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + i_bus_dq = i_bus_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + + v_lcl_sh_dq = v_lcl_sh_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) + + # Initial conditions for the integral controller + pi_cc_dq = ( + v_vsc_dq - self.beta * v_bus_dq - 1j * (self.xf1_pu + self.xf2_pu) * i_bus_dq + ) + + # DC side + # DC-side initial conditions + v_dc = self.v_dc_ref + p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real # power at converter terminals + i_dc = p_vsc/v_dc + i_load = -i_dc # negative sign because of how direction of idc is defined + duty_cycle = (v_dc - self.v_s)/v_dc + i_L = (i_load+i_dc)/(1-duty_cycle) + x_1 = i_L - self.Kff_idc*i_dc - self.Kff_iload*i_load + x_2 = duty_cycle - self.kp_i_L*(x_1 - i_L + self.Kff_idc*i_dc + self.Kff_iload*i_load) + + + self.emt_init = InitialConditionsEMT( + vmag_bus=vmag_bus, + vphase_bus=vphase_bus, + p_bus=p_bus, + q_bus=q_bus, + angle_ref=angle_ref, + pi_cc_d=pi_cc_dq.real, + pi_cc_q=pi_cc_dq.imag, + i_vsc_d=i_vsc_dq.real, + i_vsc_q=i_vsc_dq.imag, + i_bus_d=i_bus_dq.real, + i_bus_q=i_bus_dq.imag, + v_lcl_sh_d=v_lcl_sh_dq.real, + v_lcl_sh_q=v_lcl_sh_dq.imag, + i_bus_D=i_bus_DQ.real, + i_bus_Q=i_bus_DQ.imag, + v_bus_D=v_bus_DQ.real, + v_bus_Q=v_bus_DQ.imag, + v_vsc_mag = abs(v_vsc_DQ), + v_vsc_DQ_phase = np.angle(v_vsc_DQ, deg=True), + v_vsc_d=v_vsc_dq.real, + v_vsc_q=v_vsc_dq.imag, + v_dc=v_dc, + p_vsc=p_vsc, + i_dc=i_dc, + i_load_ref=i_load, + d = duty_cycle, + i_L = i_L, + x_1 = x_1, + x_2 = x_2 + ) + + def define_variables_emt(self): + # States + # ------ + + # Initial conditions + angle_ref = self.emt_init.angle_ref + pi_cc_d, pi_cc_q = self.emt_init.pi_cc_d, self.emt_init.pi_cc_q + + # these quantities are already in the converter ref frame (defined by angle_ref) + i_bus_d, i_bus_q = self.emt_init.i_bus_d, self.emt_init.i_bus_q + i_vsc_d, i_vsc_q = self.emt_init.i_vsc_d, self.emt_init.i_vsc_q + v_sh_d, v_sh_q = self.emt_init.v_lcl_sh_d, self.emt_init.v_lcl_sh_q + + # convert to abc + i_bus_a, i_bus_b, i_bus_c = dq02abc(i_bus_d, i_bus_q, 0, angle_ref*np.pi/180) + i_vsc_a, i_vsc_b, i_vsc_c = dq02abc(i_vsc_d, i_vsc_q, 0, angle_ref*np.pi/180) + v_sh_a, v_sh_b, v_sh_c = dq02abc(v_sh_d, v_sh_q, 0, angle_ref*np.pi/180) + + # DC side + v_dc, i_dc, i_L, x1, x2, i_load = self.emt_init.v_dc, self.emt_init.i_dc, self.emt_init.i_L, self.emt_init.x_1, self.emt_init.x_2, self.emt_init.i_load_ref + + + x = DynamicalVariables( + name = ['pi_cc_d', 'pi_cc_q', 'theta_pll', 'gamma_pll', "i_vsc_a", "i_vsc_b", "i_vsc_c", "v_sh_a", "v_sh_b","v_sh_c", "i_bus_a", "i_bus_b", "i_bus_c",'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load'], + component = f"{self.type_}_{self.id}", + init = [pi_cc_d, pi_cc_q, angle_ref * np.pi/180, 0, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c,i_L, v_dc, i_dc, i_load, x1, x2, i_L, v_dc, i_load] + ) + + # Inputs + # ------ + + # Initial conditions + v_bus_D, v_bus_Q = self.emt_init.v_bus_D, self.emt_init.v_bus_Q + v_bus_a, v_bus_b, v_bus_c = dq02abc(v_bus_D, v_bus_Q, 0, 0) + + u = DynamicalVariables( + name=["i_bus_d_ref", "i_bus_q_ref", "v_dc_ref", "v_s", "i_load_ref", "v_bus_a", "v_bus_b", "v_bus_c"], + component=f"{self.type_}_{self.id}", + type=["device", "device", "device", "device", "device", "grid", "grid", "grid"], + init=[i_bus_d, i_bus_q, v_dc, self.v_s, i_load, v_bus_a, v_bus_b, v_bus_c] + ) + + # Outputs + # ------- + + y = DynamicalVariables( + name=["i_bus_a", "i_bus_b", "i_bus_c"], + component=f"{self.type_}_{self.id}", + init=[i_bus_a, i_bus_b, i_bus_c] + ) + + self.variables_emt = VariablesEMT(x=x,u=u,y=y) + + def get_derivative_state_emt(self): + """ + It returns a vector with the differential equations that describe the dynamics of the GFLI. + This model includes: pi controller, pll, and LCL filter. + """ + # Get state values # here in progress + pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load = self.variables_emt.x.value + + # Get input values (external inputs) + i_bus_d_ref, i_bus_q_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value + + # convert relevant quantities to dq + v_bus_d, v_bus_q, _ = abc2dq0(v_bus_a, v_bus_b, v_bus_c, theta_pll) + i_bus_d, i_bus_q, _ = abc2dq0(i_bus_a, i_bus_b, i_bus_c, theta_pll) + + # Update algebraic states + e_d = pi_cc_d + self.kp_cc_pu * (i_bus_d_ref - i_bus_d) + e_q = pi_cc_q + self.kp_cc_pu * (i_bus_q_ref - i_bus_q) + + v_vsc_d = e_d + self.beta * v_bus_d - (self.xf1_pu + self.xf2_pu) * i_bus_q + v_vsc_q = e_q + self.beta * v_bus_q + (self.xf1_pu + self.xf2_pu) * i_bus_d + + # convert to abc to feed into filter dynamics + v_vsc_a, v_vsc_b, v_vsc_c = dq02abc(v_vsc_d, v_vsc_q, 0, theta_pll) + + # DC/AC power balance + i_vsc_d, i_vsc_q, _ = abc2dq0(i_vsc_a, i_vsc_b, i_vsc_c, theta_pll) + i_dc = (v_vsc_d*i_vsc_d + v_vsc_q*i_vsc_q)/v_dc + + def current_controller_dynamics(y, internal_inputs): + """ + It returns the differential equations that describe the dynamics of the current controller. + The current controller has: virtual inertia, filter for active and reactive power. + """ + + # Definition of states for the ODEs of the current controller + pi_cc_d, pi_cc_q = y[0], y[1] + + # Extract the list of parameters + kp_cc = self.kp_cc_pu # proportional gain of current controller + ki_cc = self.ki_cc_puHz # integral gain of current controller + + # Define internal inputs for the current controller at timepoint "t" + i_bus_d_ref, i_bus_q_ref, i_bus_d, i_bus_q = internal_inputs + + # Define ODEs that describe the dynamics of the current controller + d_pi_cc_d = ki_cc * (i_bus_d_ref - i_bus_d) + d_pi_cc_q = ki_cc * (i_bus_q_ref - i_bus_q) + + return [d_pi_cc_d, d_pi_cc_q] + + def pll_dynamics(y, internal_inputs): + """ + It returns the differential equations that describe the dynamics of the PLL. + The PLL tracks the phase of the grid voltage. + """ + # Definition of states for the ODEs of the pll + theta_pll, gamma_pll = y[0], y[1] + + # Extract the list of parameters + kp_pll = self.kp_pll_pu # proportional gain of PLL + ki_pll = self.ki_pll_puHz # integral gain of PLL + w_base = self.wbase # base frequency of the system + + # Define voltage at bus + v_bus_q = internal_inputs + + # Define ODEs that describe the dynamics of the PLL + d_theta_pll = kp_pll * v_bus_q + gamma_pll + w_base + d_gamma_pll = ki_pll * v_bus_q + + return [d_theta_pll, d_gamma_pll] + + def lcl_filter_dynamics(y, internal_inputs): + """ + It returns the differential equations that describe the dynamics of the LCL filter. + The LCL filter connects the VSC to the grid. It has three branches: the first branch (RL) connects + the VSC to the shunt element, the second branch is the shunt element (RC), and the third branch (RL) + connects the shunt element to the grid. + """ + + # Definition of states for the ODEs of the LCL filter + i_vsc_a , i_vsc_b, i_vsc_c = y[0], y[1], y[2] # currents flowing out of VSC + v_sh_a, v_sh_b, v_sh_c = y[3], y[4], y[5] # currents flowing through paralell RC shunt + i_bus_a, i_bus_b, i_bus_c = y[6], y[7], y[8] # currents flowing to bus + + # Extract the list of parameters + r1 = self.rf1_pu # resistance [p.u.] of first branch of filter + l1 = self.xf1_pu # inductance [p.u.] of first branch of filter + r2 = self.rf2_pu # resistance [p.u.] of second branch of filter + l2 = self.xf2_pu # inductance [p.u.] of second branch of filter + rsh = self.rsh_pu # resistance [p.u.] of series RC shunt + csh = self.csh_pu # capacitance [p.u.] of series RC shunt + wb = self.wbase # nominal frequency of the system + + # Define voltage at vsc at timepoint "t" + v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c = internal_inputs + + # Define ODEs that describe the dynamics of the LCL filter + di_vsc_a = wb/l1 *(v_vsc_a - v_sh_a - r1 * i_vsc_a) + di_vsc_b = wb/l1 *(v_vsc_b - v_sh_b - r1 * i_vsc_b) + di_vsc_c = wb/l1 *(v_vsc_c - v_sh_c - r1 * i_vsc_c) + + dv_sh_a = wb/csh * (-v_sh_a/rsh + i_vsc_a - i_bus_a) + dv_sh_b = wb/csh * (-v_sh_b/rsh + i_vsc_b - i_bus_b) + dv_sh_c = wb/csh * (-v_sh_c/rsh + i_vsc_c - i_bus_c) + + di_bus_a = wb/l2 *(v_sh_a - v_bus_a - r2 * i_bus_a) + di_bus_b = wb/l2 *(v_sh_b - v_bus_b - r2 * i_bus_b) + di_bus_c = wb/l2 *(v_sh_c - v_bus_c - r2 * i_bus_c) + + return [di_vsc_a, di_vsc_b, di_vsc_c, dv_sh_a, dv_sh_b, dv_sh_c, di_bus_a, di_bus_b, di_bus_c] + + def dc_side(y, internal_inputs): + """ + DC-DC controller + circuit + load control + """ + # Define states + i_Lf, v_dcf, i_dcf, i_loadf, x_1, x_2, i_L, v_dc, i_load = y[0], y[1], y[2], y[3], y[4], y[5], y[6], y[7], y[8] + + # Inputs + i_dc, i_load_ref, v_dc_ref, v_s = internal_inputs + + # Parameters + l_dc, c_dc, TiL, Tvdc, Tidc, Kp_vdc, Ki_vdc, Kp_iL, Ki_iL, Kff_idc, Kff_iload, Ti_load, Tload = self.l_dc, self.c_dc, self.Ti_L, self.Tv_dc, self.Ti_dc, self.kp_v_dc, self.ki_v_dc, self.kp_i_L, self.ki_i_L, self.Kff_idc, self.Kff_iload, self.Ti_load, self.Tload + wb = self.wbase + + # ODEs + + # DC-DC controller + d_i_Lf = (1/TiL)*(i_L - i_Lf) + d_v_dcf = (1/Tvdc)*(v_dc - v_dcf) + d_i_dcf = (1/Tidc)*(i_dc - i_dcf) + d_i_load_f = (1/Ti_load)*(i_load - i_loadf) + d_x_1 = Ki_vdc*(v_dc_ref - v_dcf) + d_x_2 = Ki_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) + duty_cycle = Kp_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) + x_2 + + # Circuit equations + d_v_dc = (wb/c_dc)*(-i_dc - i_load + (1-duty_cycle)*i_L) + d_i_L = (wb/l_dc)*(v_s - (1-duty_cycle)*v_dc) + + # Load control + d_i_load = (1/Tload)*(i_load_ref - i_load) + + return [d_i_Lf, d_v_dcf, d_i_dcf, d_i_load_f, d_x_1, d_x_2, d_i_L, d_v_dc, d_i_load] + + d_cc= current_controller_dynamics([pi_cc_d, pi_cc_q], [i_bus_d_ref, i_bus_q_ref, i_bus_d, i_bus_q]) + + d_pll = pll_dynamics([theta_pll, gamma_pll], v_bus_q) + + d_lcl = lcl_filter_dynamics([i_vsc_a , i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c], [v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c]) + + d_dc = dc_side([i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load], [i_dc, i_load_ref, v_dc_ref, v_s]) + + return np.hstack([d_cc, d_pll, d_lcl, d_dc]) + + def get_output_emt(self): + + pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load = self.variables_emt.x.value + + return [i_bus_a, i_bus_b, i_bus_c] + + def plot_results_emt(self): + """ + Plot EMT simulation results + """ + + pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load = self.variables_emt.x.value + tps = self.variables_emt.x.time + + # Transform abc to dq0 + i_vsc_d, i_vsc_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_vsc_a, i_vsc_b, i_vsc_c, theta_pll)]) + v_sh_d, v_sh_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, theta_pll)]) + i_bus_d, i_bus_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, theta_pll)]) + + results = DynamicalVariables( + name=['pi_cc_d', 'pi_cc_q', 'theta_pll', 'gamma_pll', 'i_vsc_d', 'i_vsc_q', 'v_sh_d', 'v_sh_q', 'i_bus_d', 'i_bus_q', 'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load'], + component=f"{self.type_}_{self.id}", + value=[pi_cc_d, pi_cc_q, theta_pll, gamma_pll, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load], + time=tps + ) + return results \ No newline at end of file diff --git a/sting/generator/gfli_e.py b/sting/generator/gfli_e.py index 3ca7f2f..7e7f8ea 100644 --- a/sting/generator/gfli_e.py +++ b/sting/generator/gfli_e.py @@ -144,12 +144,12 @@ def _build_small_signal_model(self): [0 , 0 , 0 , -1/xf2 , -i_bus_d], [0 , 0 , 0 , 0 , v_lcl_sh_q], [0 , 0 , 0 , 0 , -v_lcl_sh_d]]), - C = np.eye(6), - D = np.zeros((6,5)), + C = np.hstack((np.eye(4,4), np.zeros((4,2)))), + D = np.zeros((4,5)), x = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q", "v_lcl_sh_d", "v_lcl_sh_q"], init=[i_vsc_d, i_vsc_q, i_bus_d, i_bus_q, v_lcl_sh_d, v_lcl_sh_q]), u = DynamicalVariables(name=['v_vsc_d', 'v_vsc_q', 'v_bus_d', 'v_bus_q', 'w']), - y = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q", "v_lcl_sh_d", "v_lcl_sh_q"])) + y = DynamicalVariables(name=["i_vsc_d", "i_vsc_q", "i_bus_d", "i_bus_q"])) # Phase-locked loop kp_pll, ki_pll = self.kp_pll_pu, self.ki_pll_puHz @@ -199,8 +199,8 @@ def _build_small_signal_model(self): # Construction of CCM matrices # ustack = F*ystack + G*u - # ustack = i2dq_c, v1dq_c, v2dq_c, w, v2dq, vdcref, iload_ref, idc (12) - # y_stack = e1d, e1q, i1d_c, i1q_c, i2d_c, i2q_c, v3d_c, v3q_c, theta, w, i2d_ref, vdc (12) + # ustack = i2dq_ref, i2dq_c, v1dq_c, v2dq_c, w, v2dq, vdcref, iload_ref, idc (14) + # y_stack = e1d, e1q, i1d_c, i1q_c, i2d_c, i2q_c, theta, w, i2d_ref, vdc (12) # u = vdc_ref, iload_ref, i2q_ref, v2d, v2q # y = i2dq @@ -217,17 +217,17 @@ def _build_small_signal_model(self): b8 = -(self.emt_init.i_vsc_q/v_dc)*self.beta*vmag_bus # theta # Construction of CCM matrices - Fccm = np.vstack( ( np.hstack((np.zeros((10, )), 1, 0)) ,# i2d_ref - np.zeros((12,)), # i2q_ref - np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,6)))), # i2dq_c - [1, 0, 0, 0, 0, -(xf1+xf2), 0, 0, 0, 0, 0, 0], # v1d_c - [0, 1, 0, 0, (xf1+xf2), 0, 0, 0, -beta*vmag_bus, 0, 0, 0], # v1q_c - np.zeros((12, )) , # v2d_c - np.append( np.zeros((8,)) , [-vmag_bus, 0, 0, 0] ), # v2q_c - np.append( np.zeros((9,)) , [1, 0, 0] ), # w - np.zeros((2, 12)), # v2_dq - np.zeros((2, 12)), #vdc_ref, iload_ref - [b2, b5, b1, b4, b6, b3, 0, 0, b8, 0, 0, b7] #idc + Fccm = np.vstack( ( np.hstack((np.zeros((8, )), 1, 0)) ,# i2d_ref + np.zeros((10,)), # i2q_ref + np.hstack((np.zeros((2,4)), np.eye(2) ,np.zeros((2,4)))), # i2dq_c + [1, 0, 0, 0, 0, -(xf1+xf2), 0, 0, 0, 0], # v1d_c + [0, 1, 0, 0, (xf1+xf2), 0, -beta*vmag_bus, 0, 0, 0], # v1q_c + np.zeros((10, )) , # v2d_c + np.append( np.zeros((6,)) , [-vmag_bus, 0, 0, 0] ), # v2q_c + np.append( np.zeros((7,)) , [1, 0, 0] ), # w + np.zeros((2, 10)), # v2_dq + np.zeros((2, 10)), #vdc_ref, iload_ref + [b2, b5, b1, b4, b6, b3, b8, 0, 0, b7] #idc ) ) @@ -247,8 +247,8 @@ def _build_small_signal_model(self): ) ) - Hccm = np.vstack(( [ 0, 0 , 0, 0, cosphi , -sinphi, 0, 0, -sinphi*i_bus_d-cosphi*i_bus_q, 0, 0, 0], - [0, 0, 0, 0, sinphi , cosphi , 0, 0, cosphi*i_bus_d-sinphi*i_bus_q, 0, 0, 0] )) + Hccm = np.vstack(( [ 0, 0 , 0, 0, cosphi , -sinphi, -sinphi*i_bus_d-cosphi*i_bus_q, 0, 0, 0], + [0, 0, 0, 0, sinphi , cosphi , cosphi*i_bus_d-sinphi*i_bus_q, 0, 0, 0] )) Lccm = np.zeros((2, 5)) diff --git a/sting/generator/gfmi_e.py b/sting/generator/gfmi_e.py index 7a06f9f..5c14fa6 100644 --- a/sting/generator/gfmi_e.py +++ b/sting/generator/gfmi_e.py @@ -8,6 +8,7 @@ - Voltage magnitude controller - DC-side DC-DC converter circuit and controller - DC-side load with controller +- does not model any battery resistance / lossless DC-DC converter """ # ---------------------- @@ -402,11 +403,11 @@ def _calculate_emt_initial_conditions(self): v_lcl_sh_dq = v_lcl_sh_DQ * np.exp(-angle_ref * np.pi / 180 * 1j) # DC-side initial conditions - v_dc = self.v_dc_ref - duty_cycle = (v_dc - self.v_s)/v_dc + v_dc = self.v_dc_ref p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real # power at converter terminals i_dc = p_vsc/v_dc i_load = self.i_load_ref + duty_cycle = (v_dc - self.v_s)/v_dc i_L = (i_load+i_dc)/(1-duty_cycle) x_1 = i_L - self.Kff_idc*i_dc - self.Kff_iload*i_load x_2 = duty_cycle - self.kp_i_L*(x_1 - i_L + self.Kff_idc*i_dc + self.Kff_iload*i_load) @@ -635,11 +636,7 @@ def dc_side(y, internal_inputs): # Parameters l_dc, c_dc, TiL, Tvdc, Tidc, Kp_vdc, Ki_vdc, Kp_iL, Ki_iL, Kff_idc, Kff_iload, Ti_load, Tload = self.l_dc, self.c_dc, self.Ti_L, self.Tv_dc, self.Ti_dc, self.kp_v_dc, self.ki_v_dc, self.kp_i_L, self.ki_i_L, self.Kff_idc, self.Kff_iload, self.Ti_load, self.Tload wb = self.wbase - - # # DC side limits - # # P_battery = i_L*v_s - # if i_L > 1.0: - # i_L = 1.0 + # ODEs @@ -652,20 +649,16 @@ def dc_side(y, internal_inputs): d_x_2 = Ki_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) duty_cycle = Kp_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) + x_2 - # # limit duty cycle - # if duty_cycle > 0.9: - # duty_cycle = 0.9 - d_soc = i_L*v_s # soc controller d_x3 = (soc - self.SOC_init_pu) - # Circuit equations + # Circuit equations d_v_dc = (wb/c_dc)*(-i_dc - i_load + (1-duty_cycle)*i_L) d_i_L = (wb/l_dc)*(v_s - (1-duty_cycle)*v_dc) # Load control - d_i_load = (1/Tload)*(i_load_ref - i_load) + d_i_load = (1/Tload)*(i_load_ref - i_load) return [d_i_Lf, d_v_dcf, d_i_dcf, d_i_load_f, d_x_1, d_x_2, d_i_L, d_v_dc, d_i_load, d_soc, d_x3] @@ -685,7 +678,7 @@ def get_output_emt(self): return [i_bus_a, i_bus_b, i_bus_c] - def plot_results_emt(self, output_dir): + def plot_results_emt(self) -> DynamicalVariables: angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value @@ -698,187 +691,223 @@ def plot_results_emt(self, output_dir): v_sh_d, v_sh_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(v_sh_a, v_sh_b, v_sh_c, angle_pc)]) i_bus_d, i_bus_q, _ = zip(*[abc2dq0(a, b, c, ang) for a, b, c, ang in zip(i_bus_a, i_bus_b, i_bus_c, angle_pc)]) - # calculate v_vsc v_sh_dq = v_sh_d + np.multiply(v_sh_q, 1j) i_vsc_dq = i_vsc_d + np.multiply(i_vsc_q, 1j) + i_bus_dq = i_bus_d + np.multiply(i_bus_q, 1j) v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.xf1_pu * 1j), i_vsc_dq) + p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real + + p_cap = (v_sh_dq*np.conjugate(i_bus_dq)).real - fig = make_subplots( - rows=14, cols=2 + p_load = i_load*v_dc + + results_emt = DynamicalVariables( + name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_d", "i_vsc_q", "v_sh_d", "v_sh_q", "i_bus_d", "i_bus_q", "i_Lf", "v_dcf", "i_dcf", "i_loadf", "x1", "x2", "i_L", "v_dc", "i_load", "soc", "x3", 'p_vsc', 'p_cap', 'p_load'], + component = f"{self.type_}_{self.id}", + value=[angle_pc*np.pi/180, w_pc, p_pc, q_pc, gamma, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3, p_vsc, p_cap, p_load], + time=tps ) + + # custom plot + # x = tps + # y = [x1, x2, x3] + # labels = .... + # (..., ) + # custom_plot(...) + + + + + return results_emt, custom_plot + + + + + # fig = make_subplots( + # rows=14, cols=2 + # ) - fig.add_trace(go.Scatter(x=tps, y=w_pc, mode='lines', line=dict(color='red', dash='solid')), - row=1, col=1) - fig.update_xaxes(title_text='Time [s]', row=1, col=1) - fig.update_yaxes(title_text='Frequency pc [p.u.]', row=1, col=1) - - fig.add_trace(go.Scatter(x=tps, y=angle_pc * 180 / np.pi, mode='lines', line=dict(color='red', dash='solid')), - row=1, col=2) - fig.update_xaxes(title_text='Time [s]', row=1, col=2) - fig.update_yaxes(title_text='Angle pc [deg]', row=1, col=2) - - fig.add_trace(go.Scatter(x=tps, y=p_pc, name="p_pc", mode='lines', line=dict(color='red', dash='solid')), - row=2, col=1) - fig.update_xaxes(title_text='Time [s]', row=2, col=1) - fig.update_yaxes(title_text='Active Power pc [p.u.]', row=2, col=1) - - fig.add_trace(go.Scatter(x=tps, y=q_pc, mode='lines', line=dict(color='red', dash='solid')), - row=2, col=2) - fig.update_xaxes(title_text='Time [s]', row=2, col=2) - fig.update_yaxes(title_text='Reactive Power pc [p.u.]', row=2, col=2) - - fig.add_trace(go.Scatter(x=tps, y=gamma, mode='lines', line=dict(color='red', dash='solid')), - row=3, col=1) - fig.update_xaxes(title_text='Time [s]', row=3, col=1) - fig.update_yaxes(title_text='Gamma [p.u.]', row=3, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_loadf, mode='lines', line=dict(color='red', dash='solid')), - row=3, col=2) - fig.update_xaxes(title_text='Time [s]', row=3, col=2) - fig.update_yaxes(title_text='iload_f [p.u.]', row=3, col=2) - - fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, mode='lines', line=dict(color='red', dash='solid')), - row=4, col=1) - fig.update_xaxes(title_text='Time [s]', row=4, col=1) - fig.update_yaxes(title_text='i_vsc_d [p.u.]', row=4, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_vsc_q, mode='lines', line=dict(color='red', dash='solid')), - row=4, col=2) - fig.update_xaxes(title_text='Time [s]', row=4, col=2) - fig.update_yaxes(title_text='i_vsc_q [p.u.]', row=4, col=2) - - fig.add_trace(go.Scatter(x=tps, y=v_sh_d, mode='lines', line=dict(color='red', dash='solid')), - row=5, col=1) - fig.update_xaxes(title_text='Time [s]', row=5, col=1) - fig.update_yaxes(title_text='v_sh_d [p.u.]', row=5, col=1) - - fig.add_trace(go.Scatter(x=tps, y=v_sh_q, mode='lines', line=dict(color='red', dash='solid')), - row=5, col=2) - fig.update_xaxes(title_text='Time [s]', row=5, col=2) - fig.update_yaxes(title_text='v_sh_q [p.u.]', row=5, col=2) - - fig.add_trace(go.Scatter(x=tps, y=i_bus_d, mode='lines', line=dict(color='red', dash='solid')), - row=6, col=1) - fig.update_xaxes(title_text='Time [s]', row=6, col=1) - fig.update_yaxes(title_text='i_bus_d [p.u.]', row=6, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_bus_q, mode='lines', line=dict(color='red', dash='solid')), - row=6, col=2) - fig.update_xaxes(title_text='Time [s]', row=6, col=2) - fig.update_yaxes(title_text='i_bus_q [p.u.]', row=6, col=2) - - fig.add_trace(go.Scatter(x=tps, y=v_dc, mode='lines', line=dict(color='red', dash='solid')), - row=7, col=1) - fig.update_xaxes(title_text='Time [s]', row=7, col=1) - fig.update_yaxes(title_text='v_dc [p.u.]', row=7, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_L, mode='lines', line=dict(color='red', dash='solid')), - row=7, col=2) - fig.update_xaxes(title_text='Time [s]', row=7, col=2) - fig.update_yaxes(title_text='i_L [p.u.]', row=7, col=2) - - - fig.add_trace(go.Scatter(x=tps, y=v_dcf, mode='lines', line=dict(color='red', dash='solid')), - row=8, col=1) - fig.update_xaxes(title_text='Time [s]', row=8, col=1) - fig.update_yaxes(title_text='v_dcf [p.u.]', row=8, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_Lf, mode='lines', line=dict(color='red', dash='solid')), - row=8, col=2) - fig.update_xaxes(title_text='Time [s]', row=8, col=2) - fig.update_yaxes(title_text='i_Lf [p.u.]', row=8, col=2) - - - fig.add_trace(go.Scatter(x=tps, y=i_dcf, mode='lines', line=dict(color='red', dash='solid')), - row=9, col=1) - fig.update_xaxes(title_text='Time [s]', row=9, col=1) - fig.update_yaxes(title_text='i_dcf [p.u.]', row=9, col=1) - - fig.add_trace(go.Scatter(x=tps, y=x1, mode='lines', line=dict(color='red', dash='solid')), - row=9, col=2) - fig.update_xaxes(title_text='Time [s]', row=9, col=2) - fig.update_yaxes(title_text='x1 [p.u.]', row=9, col=2) - - fig.add_trace(go.Scatter(x=tps, y=x2, mode='lines', line=dict(color='red', dash='solid')), - row=10, col=1) - fig.update_xaxes(title_text='Time [s]', row=10, col=1) - fig.update_yaxes(title_text='x2 [p.u.]', row=10, col=1) - - fig.add_trace(go.Scatter(x=tps, y=i_load, mode='lines', line=dict(color='red', dash='solid')), - row=10, col=2) - fig.update_xaxes(title_text='Time [s]', row=10, col=2) - fig.update_yaxes(title_text='i_load [p.u.]', row=10, col=2) - - # power comparisons (calculated) - p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real - p_load = i_load*v_dc - p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value - p_bat = i_L*v_s - p_capacitor = p_vsc + p_load - p_bat - fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid'), legendgroup='1'), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid'), legendgroup='1'), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=p_capacitor, name="p_cap", mode='lines', line=dict(color='pink', dash='solid'), legendgroup='1'), - row=11, col=1) - fig.add_trace(go.Scatter(x=tps, y=np.ones_like(p_bat)*self.Pbat_max_pu, name="p_bat_lim", mode='lines', line=dict(color='green', dash='dot'), legendgroup='1'), - row=11, col=1) - - fig.update_xaxes(title_text='Time [s]', row=11, col=1) - fig.update_yaxes(title_text='power [p.u.]', row=11, col=1) - - # v_vsc (calculated) - fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.real, mode='lines', line=dict(color='red', dash='solid')), - row=12, col=1) - fig.update_xaxes(title_text='Time [s]', row=12, col=1) - fig.update_yaxes(title_text='v_vsc_d [p.u.]', row=12, col=1) - - fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.imag, mode='lines', line=dict(color='red', dash='solid')), - row=12, col=2) - fig.update_xaxes(title_text='Time [s]', row=12, col=2) - fig.update_yaxes(title_text='v_vsc_q [p.u.]', row=12, col=2) - - e_cap = 0.5*self.c_dc*(v_dc**2) - e_ind = 0.5*self.l_dc*(i_L**2) - fig.add_trace(go.Scatter(x=tps, y=soc, name='battery soc', mode='lines', line=dict(color='red', dash='solid')), - row=13, col=1) - fig.add_trace(go.Scatter(x=tps, y=e_cap, name='cap energy', mode='lines', line=dict(color='blue', dash='dot')), - row=13, col=1) - fig.add_trace(go.Scatter(x=tps, y=e_ind, name='ind energy', mode='lines', line=dict(color='pink', dash='dot')), - row=13, col=1) - - fig.add_trace(go.Scatter(x=tps, y=np.ones_like(soc)*self.SOC_max_pu, name="soc lim", mode='lines', line=dict(color='green', dash='dot')), - row=13, col=1) - fig.update_xaxes(title_text='Time [s]', row=13, col=1) - fig.update_yaxes(title_text='energy [p.u.]', row=13, col=1) - - duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 - duty_cycle = np.clip(duty_cycle, 0.0, 1.0) - fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='red', dash='solid')), - row=13, col=2) - fig.update_xaxes(title_text='Time [s]', row=13, col=2) - fig.update_yaxes(title_text='duty cycle [p.u.]', row=13, col=2) - - fig.add_trace(go.Scatter(x=tps, y=x3, mode='lines', line=dict(color='red', dash='solid')), - row=14, col=1) - fig.update_xaxes(title_text='Time [s]', row=14, col=1) - fig.update_yaxes(title_text='x3 [p.u.]', row=14, col=1) - - - name = f"{self.type_}_{self.id}" - fig.update_layout( title_text = name, - title_x=0.5, - showlegend = False, - ) + # fig.add_trace(go.Scatter(x=tps, y=w_pc, mode='lines', line=dict(color='red', dash='solid')), + # row=1, col=1) + # fig.update_xaxes(title_text='Time [s]', row=1, col=1) + # fig.update_yaxes(title_text='Frequency pc [p.u.]', row=1, col=1) - fig.update_layout(height=1200*3, - width=800*2, - showlegend=True, - margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) - - fig.write_html(os.path.join(output_dir, name + ".html")) + # fig.add_trace(go.Scatter(x=tps, y=angle_pc * 180 / np.pi, mode='lines', line=dict(color='red', dash='solid')), + # row=1, col=2) + # fig.update_xaxes(title_text='Time [s]', row=1, col=2) + # fig.update_yaxes(title_text='Angle pc [deg]', row=1, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=p_pc, name="p_pc", mode='lines', line=dict(color='red', dash='solid')), + # row=2, col=1) + # fig.update_xaxes(title_text='Time [s]', row=2, col=1) + # fig.update_yaxes(title_text='Active Power pc [p.u.]', row=2, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=q_pc, mode='lines', line=dict(color='red', dash='solid')), + # row=2, col=2) + # fig.update_xaxes(title_text='Time [s]', row=2, col=2) + # fig.update_yaxes(title_text='Reactive Power pc [p.u.]', row=2, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=gamma, mode='lines', line=dict(color='red', dash='solid')), + # row=3, col=1) + # fig.update_xaxes(title_text='Time [s]', row=3, col=1) + # fig.update_yaxes(title_text='Gamma [p.u.]', row=3, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=i_loadf, mode='lines', line=dict(color='red', dash='solid')), + # row=3, col=2) + # fig.update_xaxes(title_text='Time [s]', row=3, col=2) + # fig.update_yaxes(title_text='iload_f [p.u.]', row=3, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, mode='lines', line=dict(color='red', dash='solid')), + # row=4, col=1) + # fig.update_xaxes(title_text='Time [s]', row=4, col=1) + # fig.update_yaxes(title_text='i_vsc_d [p.u.]', row=4, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=i_vsc_q, mode='lines', line=dict(color='red', dash='solid')), + # row=4, col=2) + # fig.update_xaxes(title_text='Time [s]', row=4, col=2) + # fig.update_yaxes(title_text='i_vsc_q [p.u.]', row=4, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=v_sh_d, mode='lines', line=dict(color='red', dash='solid')), + # row=5, col=1) + # fig.update_xaxes(title_text='Time [s]', row=5, col=1) + # fig.update_yaxes(title_text='v_sh_d [p.u.]', row=5, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=v_sh_q, mode='lines', line=dict(color='red', dash='solid')), + # row=5, col=2) + # fig.update_xaxes(title_text='Time [s]', row=5, col=2) + # fig.update_yaxes(title_text='v_sh_q [p.u.]', row=5, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=i_bus_d, mode='lines', line=dict(color='red', dash='solid')), + # row=6, col=1) + # fig.update_xaxes(title_text='Time [s]', row=6, col=1) + # fig.update_yaxes(title_text='i_bus_d [p.u.]', row=6, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=i_bus_q, mode='lines', line=dict(color='red', dash='solid')), + # row=6, col=2) + # fig.update_xaxes(title_text='Time [s]', row=6, col=2) + # fig.update_yaxes(title_text='i_bus_q [p.u.]', row=6, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=v_dc, mode='lines', line=dict(color='red', dash='solid')), + # row=7, col=1) + # fig.update_xaxes(title_text='Time [s]', row=7, col=1) + # fig.update_yaxes(title_text='v_dc [p.u.]', row=7, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=i_L, mode='lines', line=dict(color='red', dash='solid')), + # row=7, col=2) + # fig.update_xaxes(title_text='Time [s]', row=7, col=2) + # fig.update_yaxes(title_text='i_L [p.u.]', row=7, col=2) + + + # fig.add_trace(go.Scatter(x=tps, y=v_dcf, mode='lines', line=dict(color='red', dash='solid')), + # row=8, col=1) + # fig.update_xaxes(title_text='Time [s]', row=8, col=1) + # fig.update_yaxes(title_text='v_dcf [p.u.]', row=8, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=i_Lf, mode='lines', line=dict(color='red', dash='solid')), + # row=8, col=2) + # fig.update_xaxes(title_text='Time [s]', row=8, col=2) + # fig.update_yaxes(title_text='i_Lf [p.u.]', row=8, col=2) + + + # fig.add_trace(go.Scatter(x=tps, y=i_dcf, mode='lines', line=dict(color='red', dash='solid')), + # row=9, col=1) + # fig.update_xaxes(title_text='Time [s]', row=9, col=1) + # fig.update_yaxes(title_text='i_dcf [p.u.]', row=9, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=x1, mode='lines', line=dict(color='red', dash='solid')), + # row=9, col=2) + # fig.update_xaxes(title_text='Time [s]', row=9, col=2) + # fig.update_yaxes(title_text='x1 [p.u.]', row=9, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=x2, mode='lines', line=dict(color='red', dash='solid')), + # row=10, col=1) + # fig.update_xaxes(title_text='Time [s]', row=10, col=1) + # fig.update_yaxes(title_text='x2 [p.u.]', row=10, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=i_load, mode='lines', line=dict(color='red', dash='solid')), + # row=10, col=2) + # fig.update_xaxes(title_text='Time [s]', row=10, col=2) + # fig.update_yaxes(title_text='i_load [p.u.]', row=10, col=2) + + # # power comparisons (calculated) + # p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real + # p_load = i_load*v_dc + # p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value + # p_bat = i_L*v_s + # p_capacitor = p_vsc + p_load - p_bat + # fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), + # row=11, col=1) + # fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid'), legendgroup='1'), + # row=11, col=1) + # fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid'), legendgroup='1'), + # row=11, col=1) + # fig.add_trace(go.Scatter(x=tps, y=p_capacitor, name="p_cap", mode='lines', line=dict(color='pink', dash='solid'), legendgroup='1'), + # row=11, col=1) + # fig.add_trace(go.Scatter(x=tps, y=np.ones_like(p_bat)*self.Pbat_max_pu, name="p_bat_lim", mode='lines', line=dict(color='green', dash='dot'), legendgroup='1'), + # row=11, col=1) + + # fig.update_xaxes(title_text='Time [s]', row=11, col=1) + # fig.update_yaxes(title_text='power [p.u.]', row=11, col=1) + + # # v_vsc (calculated) + # fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.real, mode='lines', line=dict(color='red', dash='solid')), + # row=12, col=1) + # fig.update_xaxes(title_text='Time [s]', row=12, col=1) + # fig.update_yaxes(title_text='v_vsc_d [p.u.]', row=12, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.imag, mode='lines', line=dict(color='red', dash='solid')), + # row=12, col=2) + # fig.update_xaxes(title_text='Time [s]', row=12, col=2) + # fig.update_yaxes(title_text='v_vsc_q [p.u.]', row=12, col=2) + + # e_cap = 0.5*self.c_dc*(v_dc**2) + # e_ind = 0.5*self.l_dc*(i_L**2) + # fig.add_trace(go.Scatter(x=tps, y=soc, name='battery soc', mode='lines', line=dict(color='red', dash='solid')), + # row=13, col=1) + # fig.add_trace(go.Scatter(x=tps, y=e_cap, name='cap energy', mode='lines', line=dict(color='blue', dash='dot')), + # row=13, col=1) + # fig.add_trace(go.Scatter(x=tps, y=e_ind, name='ind energy', mode='lines', line=dict(color='pink', dash='dot')), + # row=13, col=1) + + # fig.add_trace(go.Scatter(x=tps, y=np.ones_like(soc)*self.SOC_max_pu, name="soc lim", mode='lines', line=dict(color='green', dash='dot')), + # row=13, col=1) + # fig.update_xaxes(title_text='Time [s]', row=13, col=1) + # fig.update_yaxes(title_text='energy [p.u.]', row=13, col=1) + + # duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 + # duty_cycle = np.clip(duty_cycle, 0.0, 1.0) + # fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='red', dash='solid')), + # row=13, col=2) + # fig.update_xaxes(title_text='Time [s]', row=13, col=2) + # fig.update_yaxes(title_text='duty cycle [p.u.]', row=13, col=2) + + # fig.add_trace(go.Scatter(x=tps, y=x3, mode='lines', line=dict(color='red', dash='solid')), + # row=14, col=1) + # fig.update_xaxes(title_text='Time [s]', row=14, col=1) + # fig.update_yaxes(title_text='x3 [p.u.]', row=14, col=1) + + # # Vmag + # vmag = np.sqrt(np.square(v_sh_d)+np.square(v_sh_q)) + # fig.add_trace(go.Scatter(x=tps, y=vmag, mode='lines', line=dict(color='red', dash='solid')), + # row=14, col=2) + # fig.update_xaxes(title_text='Time [s]', row=14, col=2) + # fig.update_yaxes(title_text='Vmag [p.u.]', row=14, col=2) + + + + + # name = f"{self.type_}_{self.id}" + # fig.update_layout( title_text = name, + # title_x=0.5, + # showlegend = False, + # ) + + # fig.update_layout(height=1200*3, + # width=800*2, + # showlegend=True, + # margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) + + # fig.write_html(os.path.join(output_dir, name + ".html")) diff --git a/sting/system/core.py b/sting/system/core.py index 976f144..931c477 100644 --- a/sting/system/core.py +++ b/sting/system/core.py @@ -29,6 +29,7 @@ from sting.generator.gfmi_d import GFMId from sting.generator.gfmi_e import GFMIe from sting.generator.gfli_e import GFLIe +from sting.generator.gfli_d import GFLId from sting.reduced_order_model.linear_subsystem import LinearSubsystem from sting.line.pi_model import LinePiModel from sting.branch.series_rl import BranchSeriesRL @@ -61,6 +62,7 @@ class System: gfmi_e: list[GFMIe] = None gfli_a: list[GFLIa] = None gfli_e: list[GFLIe] = None + gfli_d: list[GFLId] = None linear_subsystems: list[LinearSubsystem] = None buses: list[Bus] = None loads: list[Load] = None From b75afd8ab8132a70ab9232764542fa04fec553a1 Mon Sep 17 00:00:00 2001 From: rkravis Date: Thu, 14 May 2026 10:25:55 -0700 Subject: [PATCH 6/6] clean up gfli_e and gfli_d examples --- .../active_power_balance_by_bus.csv | 3 - .../outputs/ac_power_flow/bus_voltage.csv | 3 - .../outputs/ac_power_flow/costs_summary.csv | 2 - .../ac_power_flow/generator_dispatch.csv | 3 - .../outputs/ac_power_flow/line_flows.csv | 2 - .../outputs/ac_power_flow/load_shedding.csv | 3 - .../reactive_power_balance_by_bus.csv | 3 - .../outputs/ac_power_flow/solver_status.csv | 5 - .../component_connection_matrices/F.csv | 18 -- .../component_connection_matrices/G.csv | 18 -- .../component_connection_matrices/H.csv | 11 - .../component_connection_matrices/L.csv | 11 - .../2-bus-src-gfli_e/run.py | 42 +-- .../2-bus_src-gfli_d/inputs/gfli_d.csv | 2 +- .../active_power_balance_by_bus.csv | 3 - .../outputs/ac_power_flow/bus_voltage.csv | 3 - .../outputs/ac_power_flow/costs_summary.csv | 2 - .../ac_power_flow/generator_dispatch.csv | 3 - .../outputs/ac_power_flow/line_flows.csv | 2 - .../outputs/ac_power_flow/load_shedding.csv | 3 - .../reactive_power_balance_by_bus.csv | 3 - .../outputs/ac_power_flow/solver_status.csv | 5 - .../component_connection_matrices/F.csv | 20 -- .../component_connection_matrices/G.csv | 20 -- .../component_connection_matrices/H.csv | 11 - .../component_connection_matrices/L.csv | 11 - .../2-bus_src-gfli_d/run.py | 42 +-- sting/generator/gfmi_e.py | 244 ++---------------- 28 files changed, 26 insertions(+), 472 deletions(-) delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv delete mode 100644 examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv delete mode 100644 examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv deleted file mode 100644 index 867ad1b..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,t_1,90.0582035350247,-9.97494096631705e-9,0.0,90.05820352504989 -santiago,t_1,-80.0,-9.974940966510371e-9,0.0,-80.00000000997504 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv deleted file mode 100644 index bbf6498..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/bus_voltage.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg -0,lima,t_1,1.0,0.0 -1,santiago,t_1,0.9985756950410833,-2.439253738537986 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv deleted file mode 100644 index 5521476..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/costs_summary.csv +++ /dev/null @@ -1,2 +0,0 @@ -component,cost -total_cost_USD,-800.0000398997639 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv deleted file mode 100644 index 7b8db7e..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/generator_dispatch.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,t_1,90.0582035350247,2.2903854129179724 -0,gfli_e,solar,t_1,-80.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv deleted file mode 100644 index a69015f..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/line_flows.csv +++ /dev/null @@ -1,2 +0,0 @@ -line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,90.05820352504956,2.2903854029422632,-80.00000000997518,-9.975292198719353e-9,10.058203515074382,2.290385392966971 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv deleted file mode 100644 index 35eb28c..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/load_shedding.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,t_1,-9.97494096631705e-9,-9.974940964406027e-9 -santiago,t_1,-9.974940966510371e-9,-9.974940967337148e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv deleted file mode 100644 index 675272d..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,t_1,2.2903854129179724,-9.974940964406027e-9,0.0,2.2903854029429738 -santiago,t_1,0.0,-9.974940967337148e-9,0.0,-9.974937433366244e-9 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv deleted file mode 100644 index 79f46b3..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/ac_power_flow/solver_status.csv +++ /dev/null @@ -1,5 +0,0 @@ -attribute,value -solver_name,ipopt -solver_status,ok -termination_condition,optimal -time_spent_seconds,0.06361007690429688 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv deleted file mode 100644 index 25ec4a0..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/F.csv +++ /dev/null @@ -1,18 +0,0 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_e_0', 'i_bus_D')","('gfli_e_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'v_ref_d')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'v_dc_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'i_bus_q_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('gfli_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 -"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 -"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv deleted file mode 100644 index c46e286..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/G.csv +++ /dev/null @@ -1,18 +0,0 @@ -Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'v_dc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'i_bus_q_ref')" -"('infinite_sources_0', 'v_ref_d')",1.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_ref_q')",0.0,1.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'v_dc_ref')",0.0,0.0,1.0,0.0,0.0 -"('gfli_e_0', 'i_load_ref')",0.0,0.0,0.0,1.0,0.0 -"('gfli_e_0', 'i_bus_q_ref')",0.0,0.0,0.0,0.0,1.0 -"('gfli_e_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv deleted file mode 100644 index d2f1694..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/H.csv +++ /dev/null @@ -1,11 +0,0 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_e_0', 'i_bus_D')","('gfli_e_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv deleted file mode 100644 index 95c81c6..0000000 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/outputs/component_connection_matrices/L.csv +++ /dev/null @@ -1,11 +0,0 @@ -Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_e_0', 'v_dc_ref')","('gfli_e_0', 'i_load_ref')","('gfli_e_0', 'i_bus_q_ref')" -"('infinite_sources_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('gfli_e_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py index 48b9f58..b3d0bdf 100644 --- a/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py @@ -1,19 +1,12 @@ +""" +Simulates an infinite source and GFLI_e connected via a transmission line. +""" # Import Python standard and third-party packages from pathlib import Path -import numpy as np -import pandas as pd -from scipy.linalg import eig, inv -import seaborn as sns -import matplotlib.pyplot as plt - -import sys -sys.path.append('/Users/ruthkravis/Documents/STING') - # Import sting package from sting import main -from sting.utils.transformations import dq02abc, abc2dq0 # Step-change input to applied to the system def step1(t): @@ -22,12 +15,6 @@ def step1(t): def step2(t): return 0.0 -def step3(t): - return 0.1 if t >= 0.5 else 0.0 - -def oscillating_load(t): - return 0.01*np.square(2*np.pi*0.5*t) if t >= 0.1 else 0.0 - inputs = { 'infinite_sources_0': { 'v_ref_d': step1 @@ -45,28 +32,7 @@ def oscillating_load(t): # Construct system and small-signal model _, ssm = main.run_ssm(case_directory=case_dir) ssm.simulate_ssm(t_max=t_max, inputs=inputs) +# Run EMT simulation main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) -# def participation_factor_plot(ssm): -# w,vr = eig(ssm.model.A) -# vl = inv(vr) # ensures normalization - -# p = np.zeros_like(vl) -# for i in range(len(w)): -# for k in range(len(w)): -# # use correct formula for complex eigenvalues -# p[k, i] = (vl[i,k].real)**2/(np.dot(vl[i,:].real,vl[i,:].real)) # participation of k-th state (row) in i-th mode (column) - -# pf_df = pd.DataFrame(p.real, index=ssm.model.x.name) -# fig = plt.figure(figsize=(12,8)) -# ax = sns.heatmap(pf_df, yticklabels=pf_df.index, xticklabels=np.round(w.real,3), linewidths=1, linecolor='white') -# ax.set_yticklabels(ax.get_yticklabels(), fontsize=8) -# ax.set_xticklabels(ax.get_xticklabels(), fontsize=8) -# plt.show() -# plt.savefig("ssm.png") - - -# participation_factor_plot(ssm) - - print('\nok') \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv index e0dcc29..8fc3fec 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/inputs/gfli_d.csv @@ -1,2 +1,2 @@ name,bus,minimum_active_power_MW,maximum_active_power_MW,minimum_reactive_power_MVAR,maximum_reactive_power_MVAR,base_power_MVA,base_voltage_kV,base_frequency_Hz,cost_variable_USDperMWh,rf1_pu,xf1_pu,rsh_pu,csh_pu,txr_power_MVA,txr_voltage1_kV,txr_voltage2_kV,txr_r1_pu,txr_x1_pu,txr_r2_pu,txr_x2_pu,beta,kp_pll_pu,ki_pll_puHz,kp_cc_pu,ki_cc_puHz,kp_v_dc,ki_v_dc,kp_i_L,ki_i_L,l_dc,c_dc,v_dc_ref,v_s,Ti_L,Tv_dc,Ti_dc,Kff_idc,Kff_iload,Ti_load,Tload -solar,santiago,-80,-80,0,0,1.00E+02,4.80E-01,60,10,0.001,0.05,1,0.1,1.00E+02,4.80E-01,2.30E+02,0.002,0.05,0.002,0.05,0.75,100,2500,0.45,6,5,50,1,10,0.1,20,1.05,0.5,0.01,0.01,0.01,1,1,0.01,0.0001 \ No newline at end of file +solar,santiago,-80,-80,0,0,1.00E+02,4.80E-01,60,10,0.001,0.05,1,0.1,1.00E+02,4.80E-01,2.30E+02,0.002,0.05,0.002,0.05,0.75,100,2500,0.45,6,1.2,20,1,10,0.1,20,1,0.5,0.01,0.01,0.01,1,1,0.01,0.001 \ No newline at end of file diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv deleted file mode 100644 index 867ad1b..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/active_power_balance_by_bus.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,generator_dispatch_MW,load_shedding_MW,load_MW,net_line_leaving_flow_MW -lima,t_1,90.0582035350247,-9.97494096631705e-9,0.0,90.05820352504989 -santiago,t_1,-80.0,-9.974940966510371e-9,0.0,-80.00000000997504 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv deleted file mode 100644 index bbf6498..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/bus_voltage.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,bus,timepoint,voltage_magnitude_pu,voltage_angle_deg -0,lima,t_1,1.0,0.0 -1,santiago,t_1,0.9985756950410833,-2.439253738537986 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv deleted file mode 100644 index 5521476..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/costs_summary.csv +++ /dev/null @@ -1,2 +0,0 @@ -component,cost -total_cost_USD,-800.0000398997639 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv deleted file mode 100644 index 645bbc7..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/generator_dispatch.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,type,generator,timepoint,active_power_MW,reactive_power_MVAR -0,infinite_sources,gen1,t_1,90.0582035350247,2.2903854129179724 -0,gfli_d,solar,t_1,-80.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv deleted file mode 100644 index a69015f..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/line_flows.csv +++ /dev/null @@ -1,2 +0,0 @@ -line,from_bus,to_bus,existing_capacity_MW,active_power_from_bus_MW,reactive_power_from_bus_MVAR,active_power_to_bus_MW,reactive_power_to_bus_MVAR,active_power_loss_MW,reactive_power_loss_MVAR -tx_1,lima,santiago,inf,90.05820352504956,2.2903854029422632,-80.00000000997518,-9.975292198719353e-9,10.058203515074382,2.290385392966971 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv deleted file mode 100644 index 35eb28c..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/load_shedding.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,active_load_shedding_MW,reactive_load_shedding_MVAR -lima,t_1,-9.97494096631705e-9,-9.974940964406027e-9 -santiago,t_1,-9.974940966510371e-9,-9.974940967337148e-9 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv deleted file mode 100644 index 675272d..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/reactive_power_balance_by_bus.csv +++ /dev/null @@ -1,3 +0,0 @@ -bus,timepoint,generator_dispatch_MVAR,load_shedding_MVAR,load_MVAR,net_line_leaving_flow_MVAR -lima,t_1,2.2903854129179724,-9.974940964406027e-9,0.0,2.2903854029429738 -santiago,t_1,0.0,-9.974940967337148e-9,0.0,-9.974937433366244e-9 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv deleted file mode 100644 index 34b0df2..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/ac_power_flow/solver_status.csv +++ /dev/null @@ -1,5 +0,0 @@ -attribute,value -solver_name,ipopt -solver_status,ok -termination_condition,optimal -time_spent_seconds,0.06843376159667969 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv deleted file mode 100644 index 6e099b6..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/F.csv +++ /dev/null @@ -1,20 +0,0 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_d_0', 'i_bus_D')","('gfli_d_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'v_ref_d')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_ref_q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_d_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_q_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'v_dc_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'v_s')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('gfli_d_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0 -"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0 -"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv deleted file mode 100644 index 21a08d2..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/G.csv +++ /dev/null @@ -1,20 +0,0 @@ -Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_d_0', 'i_bus_d_ref')","('gfli_d_0', 'i_bus_q_ref')","('gfli_d_0', 'v_dc_ref')","('gfli_d_0', 'v_s')","('gfli_d_0', 'i_load_ref')" -"('infinite_sources_0', 'v_ref_d')",1.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_ref_q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_d_ref')",0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_q_ref')",0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('gfli_d_0', 'v_dc_ref')",0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('gfli_d_0', 'v_s')",0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('gfli_d_0', 'i_load_ref')",0.0,0.0,0.0,0.0,0.0,0.0,1.0 -"('gfli_d_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_from_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_from_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'v_to_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv deleted file mode 100644 index 4bd0f1d..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/H.csv +++ /dev/null @@ -1,11 +0,0 @@ -Index,"('infinite_sources_0', 'i_bus_D')","('infinite_sources_0', 'i_bus_Q')","('gfli_d_0', 'i_bus_D')","('gfli_d_0', 'i_bus_Q')","('shunt_parallel_rc_0', 'v_bus_D')","('shunt_parallel_rc_0', 'v_bus_Q')","('shunt_parallel_rc_1', 'v_bus_D')","('shunt_parallel_rc_1', 'v_bus_Q')","('branch_series_rl_0', 'i_br_D')","('branch_series_rl_0', 'i_br_Q')" -"('infinite_sources_0', 'i_bus_D')",1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_Q')",0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_D')",0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_Q')",0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0 -"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0 -"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv b/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv deleted file mode 100644 index 0bb906b..0000000 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/outputs/component_connection_matrices/L.csv +++ /dev/null @@ -1,11 +0,0 @@ -Index,"('infinite_sources_0', 'v_ref_d')","('infinite_sources_0', 'v_ref_q')","('gfli_d_0', 'i_bus_d_ref')","('gfli_d_0', 'i_bus_q_ref')","('gfli_d_0', 'v_dc_ref')","('gfli_d_0', 'v_s')","('gfli_d_0', 'i_load_ref')" -"('infinite_sources_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('infinite_sources_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('gfli_d_0', 'i_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_0', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('shunt_parallel_rc_1', 'v_bus_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'i_br_D')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 -"('branch_series_rl_0', 'i_br_Q')",0.0,0.0,0.0,0.0,0.0,0.0,0.0 diff --git a/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py b/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py index 8ab2abe..ad5a469 100644 --- a/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py @@ -4,67 +4,33 @@ """ # Import Python standard and third-party packages from pathlib import Path -import sys -sys.path.append("/Users/ruthkravis/Documents/STING") - -import numpy as np -import pandas as pd -from scipy.linalg import eig, inv -import seaborn as sns -import matplotlib.pyplot as plt # Import sting package from sting import main -from sting.system.core import System # Specify path of the case study directory case_dir = Path(__file__).resolve().parent -# Construct system and small-signal model -sys = System.from_csv(case_directory=case_dir) - # Step function to simulate def step1(t): - return 0.1 if t >= 0.5 else 0.0 + return 0.3 if t >= 0.1 else 0.0 def step2(t): return 0.0 inputs = { 'infinite_sources_0': { - 'v_ref_d': step2 + 'v_ref_d': step1 }, 'gfli_d_0': { - 'i_bus_d_ref': step1} + 'i_load_ref': step2} } t_max = 2.0 # Simulation length # Construct system and small-signal model _, ssm = main.run_ssm(case_directory=case_dir) ssm.simulate_ssm(t_max=t_max, inputs=inputs) -# Run EMT simulation (not implemented yet for gfli_a) +# Run EMT simulation main.run_emt(case_directory=case_dir, inputs=inputs, t_max=t_max) -# def participation_factor_plot(ssm): -# w,vr = eig(ssm.model.A) -# vl = inv(vr) # ensures normalization - -# p = np.zeros_like(vl) -# for i in range(len(w)): -# for k in range(len(w)): -# # use correct formula for complex eigenvalues -# p[k, i] = (vl[i,k].real)**2/(np.dot(vl[i,:].real,vl[i,:].real)) # participation of k-th state (row) in i-th mode (column) - -# pf_df = pd.DataFrame(p.real, index=ssm.model.x.name) -# fig = plt.figure(figsize=(12,8)) -# ax = sns.heatmap(pf_df, yticklabels=pf_df.index, xticklabels=np.round(w.real,3), linewidths=1, linecolor='white') -# ax.set_yticklabels(ax.get_yticklabels(), fontsize=8) -# ax.set_xticklabels(ax.get_xticklabels(), fontsize=8) -# plt.show() -# plt.savefig("ssm.png") - - -# participation_factor_plot(ssm) - - print('\nok') \ No newline at end of file diff --git a/sting/generator/gfmi_e.py b/sting/generator/gfmi_e.py index 5c14fa6..9212d55 100644 --- a/sting/generator/gfmi_e.py +++ b/sting/generator/gfmi_e.py @@ -293,7 +293,7 @@ def _build_small_signal_model(self): # DC circuit a1 = v_vsc_d/v_dc a2 = i_vsc_d/v_dc - a3 = self.emt_init.v_vsc_q/v_dc # is this just 0 ? + a3 = self.emt_init.v_vsc_q/v_dc a4 = i_vsc_q/v_dc a5 = -i_dc/v_dc @@ -423,7 +423,7 @@ def _calculate_emt_initial_conditions(self): v_ref=v_ref, angle_ref=angle_ref, v_vsc_d=v_vsc_dq.real, - v_vsc_q=v_vsc_dq.imag, # added + v_vsc_q=v_vsc_dq.imag, i_vsc_d=i_vsc_dq.real, i_vsc_q=i_vsc_dq.imag, i_bus_d=i_bus_dq.real, @@ -471,9 +471,9 @@ def define_variables_emt(self): v_dc, i_dc, i_L, x1, x2, i_load = self.emt_init.v_dc, self.emt_init.i_dc, self.emt_init.i_L, self.emt_init.x_1, self.emt_init.x_2, self.emt_init.i_load x = DynamicalVariables( - name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_a", "i_vsc_b","i_vsc_c", "v_sh_a", "v_sh_b","v_sh_c", "i_bus_a", "i_bus_b", "i_bus_c", 'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load', 'soc', 'x3'], + name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_a", "i_vsc_b","i_vsc_c", "v_sh_a", "v_sh_b","v_sh_c", "i_bus_a", "i_bus_b", "i_bus_c", 'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load', 'soc'], component = f"{self.type_}_{self.id}", - init=[angle_ref*np.pi/180, 1.0, p_ref, q_ref, v_vsc_d, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_L, v_dc, i_dc, i_load, x1, x2, i_L, v_dc, i_load, self.SOC_init_pu, 0.0] + init=[angle_ref*np.pi/180, 1.0, p_ref, q_ref, v_vsc_d, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_L, v_dc, i_dc, i_load, x1, x2, i_L, v_dc, i_load, self.SOC_init_pu] ) # Inputs @@ -505,7 +505,7 @@ def define_variables_emt(self): def get_derivative_state_emt(self): # Get state values - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc = self.variables_emt.x.value # Get input values (external inputs) p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value @@ -518,15 +518,10 @@ def get_derivative_state_emt(self): # Do Q-V droop v_sh_mag_ref = v_ref - self.droop_q_pu*(q_pc - q_ref) # adjusting voltage reference based on reactive power error (measured at capacitor) - # NB updating algebraic states! # Updating converter terminal voltages v_vsc_d = gamma + self.kp_vc_pu*(v_sh_mag_ref - (v_sh_d**2 + v_sh_q**2)**0.5) # update v_vsc_q = 0.0 # update - # # check magnitude wrt v_dc and limit if necessary - # if v_vsc_d > v_dc: # can check only d component because v_vsc_q= 0 - # v_vsc_d = v_dc - # convert to abc to feed into filter dynamics v_vsc_a, v_vsc_b, v_vsc_c = dq02abc(v_vsc_d, v_vsc_q, 0, angle_pc) # correct to use this angle? @@ -534,7 +529,6 @@ def get_derivative_state_emt(self): i_dc = (v_vsc_d*i_vsc_d + v_vsc_q*i_vsc_q)/v_dc - # Differential equations # ---------------------- @@ -636,7 +630,6 @@ def dc_side(y, internal_inputs): # Parameters l_dc, c_dc, TiL, Tvdc, Tidc, Kp_vdc, Ki_vdc, Kp_iL, Ki_iL, Kff_idc, Kff_iload, Ti_load, Tload = self.l_dc, self.c_dc, self.Ti_L, self.Tv_dc, self.Ti_dc, self.kp_v_dc, self.ki_v_dc, self.kp_i_L, self.ki_i_L, self.Kff_idc, self.Kff_iload, self.Ti_load, self.Tload wb = self.wbase - # ODEs @@ -649,10 +642,6 @@ def dc_side(y, internal_inputs): d_x_2 = Ki_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) duty_cycle = Kp_iL*(Kp_vdc*(v_dc_ref - v_dcf) + x_1 - i_Lf + Kff_idc*i_dcf + Kff_iload*i_loadf) + x_2 - d_soc = i_L*v_s - # soc controller - d_x3 = (soc - self.SOC_init_pu) - # Circuit equations d_v_dc = (wb/c_dc)*(-i_dc - i_load + (1-duty_cycle)*i_L) d_i_L = (wb/l_dc)*(v_s - (1-duty_cycle)*v_dc) @@ -660,13 +649,16 @@ def dc_side(y, internal_inputs): # Load control d_i_load = (1/Tload)*(i_load_ref - i_load) - return [d_i_Lf, d_v_dcf, d_i_dcf, d_i_load_f, d_x_1, d_x_2, d_i_L, d_v_dc, d_i_load, d_soc, d_x3] + #SOC + d_soc = i_L*v_s + + return [d_i_Lf, d_v_dcf, d_i_dcf, d_i_load_f, d_x_1, d_x_2, d_i_L, d_v_dc, d_i_load, d_soc] dy_pc = power_controller_dynamics([angle_pc, w_pc, p_pc, q_pc], [v_sh_d, v_sh_q, i_bus_d, i_bus_q, p_ref]) dy_vc = voltage_controller_dynamics([gamma], [v_sh_mag_ref, v_sh_d, v_sh_q]) dy_lcl = lcl_filter_dynamics([i_vsc_a , i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c], [v_vsc_a, v_vsc_b, v_vsc_c, v_bus_a, v_bus_b, v_bus_c]) - dy_dc_side = dc_side([i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3], [i_dc, i_load_ref, v_dc_ref, v_s]) + dy_dc_side = dc_side([i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc], [i_dc, i_load_ref, v_dc_ref, v_s]) return np.hstack([dy_pc, dy_vc, dy_lcl, dy_dc_side]) @@ -674,15 +666,13 @@ def dc_side(y, internal_inputs): def get_output_emt(self): # Output is i_bus_abc - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc = self.variables_emt.x.value return [i_bus_a, i_bus_b, i_bus_c] def plot_results_emt(self) -> DynamicalVariables: - angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3 = self.variables_emt.x.value - - #i_L = np.clip(i_L, -1000.0, 1.0) + angle_pc, w_pc, p_pc, q_pc, gamma, i_vsc_a, i_vsc_b, i_vsc_c, v_sh_a, v_sh_b, v_sh_c, i_bus_a, i_bus_b, i_bus_c, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc = self.variables_emt.x.value tps = self.variables_emt.x.time @@ -697,218 +687,18 @@ def plot_results_emt(self) -> DynamicalVariables: v_vsc_dq = v_sh_dq + np.multiply((self.rf1_pu + self.xf1_pu * 1j), i_vsc_dq) p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real - p_cap = (v_sh_dq*np.conjugate(i_bus_dq)).real + p_sh = (v_sh_dq*np.conjugate(i_bus_dq)).real p_load = i_load*v_dc results_emt = DynamicalVariables( - name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_d", "i_vsc_q", "v_sh_d", "v_sh_q", "i_bus_d", "i_bus_q", "i_Lf", "v_dcf", "i_dcf", "i_loadf", "x1", "x2", "i_L", "v_dc", "i_load", "soc", "x3", 'p_vsc', 'p_cap', 'p_load'], + name = ['angle_pc', 'w_pc', 'p_pc', 'q_pc', 'gamma',"i_vsc_d", "i_vsc_q", "v_sh_d", "v_sh_q", "i_bus_d", "i_bus_q", "i_Lf", "v_dcf", "i_dcf", "i_loadf", "x1", "x2", "i_L", "v_dc", "i_load", "soc", 'p_vsc', 'p_sh', 'p_load'], component = f"{self.type_}_{self.id}", - value=[angle_pc*np.pi/180, w_pc, p_pc, q_pc, gamma, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, x3, p_vsc, p_cap, p_load], + value=[angle_pc*np.pi/180, w_pc, p_pc, q_pc, gamma, i_vsc_d, i_vsc_q, v_sh_d, v_sh_q, i_bus_d, i_bus_q, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, p_vsc, p_sh, p_load], time=tps ) - # custom plot - # x = tps - # y = [x1, x2, x3] - # labels = .... - # (..., ) - # custom_plot(...) - - + return results_emt - return results_emt, custom_plot - - - - - # fig = make_subplots( - # rows=14, cols=2 - # ) - - # fig.add_trace(go.Scatter(x=tps, y=w_pc, mode='lines', line=dict(color='red', dash='solid')), - # row=1, col=1) - # fig.update_xaxes(title_text='Time [s]', row=1, col=1) - # fig.update_yaxes(title_text='Frequency pc [p.u.]', row=1, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=angle_pc * 180 / np.pi, mode='lines', line=dict(color='red', dash='solid')), - # row=1, col=2) - # fig.update_xaxes(title_text='Time [s]', row=1, col=2) - # fig.update_yaxes(title_text='Angle pc [deg]', row=1, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=p_pc, name="p_pc", mode='lines', line=dict(color='red', dash='solid')), - # row=2, col=1) - # fig.update_xaxes(title_text='Time [s]', row=2, col=1) - # fig.update_yaxes(title_text='Active Power pc [p.u.]', row=2, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=q_pc, mode='lines', line=dict(color='red', dash='solid')), - # row=2, col=2) - # fig.update_xaxes(title_text='Time [s]', row=2, col=2) - # fig.update_yaxes(title_text='Reactive Power pc [p.u.]', row=2, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=gamma, mode='lines', line=dict(color='red', dash='solid')), - # row=3, col=1) - # fig.update_xaxes(title_text='Time [s]', row=3, col=1) - # fig.update_yaxes(title_text='Gamma [p.u.]', row=3, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=i_loadf, mode='lines', line=dict(color='red', dash='solid')), - # row=3, col=2) - # fig.update_xaxes(title_text='Time [s]', row=3, col=2) - # fig.update_yaxes(title_text='iload_f [p.u.]', row=3, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=i_vsc_d, mode='lines', line=dict(color='red', dash='solid')), - # row=4, col=1) - # fig.update_xaxes(title_text='Time [s]', row=4, col=1) - # fig.update_yaxes(title_text='i_vsc_d [p.u.]', row=4, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=i_vsc_q, mode='lines', line=dict(color='red', dash='solid')), - # row=4, col=2) - # fig.update_xaxes(title_text='Time [s]', row=4, col=2) - # fig.update_yaxes(title_text='i_vsc_q [p.u.]', row=4, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=v_sh_d, mode='lines', line=dict(color='red', dash='solid')), - # row=5, col=1) - # fig.update_xaxes(title_text='Time [s]', row=5, col=1) - # fig.update_yaxes(title_text='v_sh_d [p.u.]', row=5, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=v_sh_q, mode='lines', line=dict(color='red', dash='solid')), - # row=5, col=2) - # fig.update_xaxes(title_text='Time [s]', row=5, col=2) - # fig.update_yaxes(title_text='v_sh_q [p.u.]', row=5, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=i_bus_d, mode='lines', line=dict(color='red', dash='solid')), - # row=6, col=1) - # fig.update_xaxes(title_text='Time [s]', row=6, col=1) - # fig.update_yaxes(title_text='i_bus_d [p.u.]', row=6, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=i_bus_q, mode='lines', line=dict(color='red', dash='solid')), - # row=6, col=2) - # fig.update_xaxes(title_text='Time [s]', row=6, col=2) - # fig.update_yaxes(title_text='i_bus_q [p.u.]', row=6, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=v_dc, mode='lines', line=dict(color='red', dash='solid')), - # row=7, col=1) - # fig.update_xaxes(title_text='Time [s]', row=7, col=1) - # fig.update_yaxes(title_text='v_dc [p.u.]', row=7, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=i_L, mode='lines', line=dict(color='red', dash='solid')), - # row=7, col=2) - # fig.update_xaxes(title_text='Time [s]', row=7, col=2) - # fig.update_yaxes(title_text='i_L [p.u.]', row=7, col=2) - - - # fig.add_trace(go.Scatter(x=tps, y=v_dcf, mode='lines', line=dict(color='red', dash='solid')), - # row=8, col=1) - # fig.update_xaxes(title_text='Time [s]', row=8, col=1) - # fig.update_yaxes(title_text='v_dcf [p.u.]', row=8, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=i_Lf, mode='lines', line=dict(color='red', dash='solid')), - # row=8, col=2) - # fig.update_xaxes(title_text='Time [s]', row=8, col=2) - # fig.update_yaxes(title_text='i_Lf [p.u.]', row=8, col=2) - - - # fig.add_trace(go.Scatter(x=tps, y=i_dcf, mode='lines', line=dict(color='red', dash='solid')), - # row=9, col=1) - # fig.update_xaxes(title_text='Time [s]', row=9, col=1) - # fig.update_yaxes(title_text='i_dcf [p.u.]', row=9, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=x1, mode='lines', line=dict(color='red', dash='solid')), - # row=9, col=2) - # fig.update_xaxes(title_text='Time [s]', row=9, col=2) - # fig.update_yaxes(title_text='x1 [p.u.]', row=9, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=x2, mode='lines', line=dict(color='red', dash='solid')), - # row=10, col=1) - # fig.update_xaxes(title_text='Time [s]', row=10, col=1) - # fig.update_yaxes(title_text='x2 [p.u.]', row=10, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=i_load, mode='lines', line=dict(color='red', dash='solid')), - # row=10, col=2) - # fig.update_xaxes(title_text='Time [s]', row=10, col=2) - # fig.update_yaxes(title_text='i_load [p.u.]', row=10, col=2) - - # # power comparisons (calculated) - # p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real - # p_load = i_load*v_dc - # p_ref, q_ref, v_ref, v_dc_ref, v_s, i_load_ref, v_bus_a, v_bus_b, v_bus_c = self.variables_emt.u.value - # p_bat = i_L*v_s - # p_capacitor = p_vsc + p_load - p_bat - # fig.add_trace(go.Scatter(x=tps, y=p_vsc, name="p_vsc", mode='lines', line=dict(color='red', dash='solid'), legendgroup='1'), - # row=11, col=1) - # fig.add_trace(go.Scatter(x=tps, y=p_load, name="p_load", mode='lines', line=dict(color='blue', dash='solid'), legendgroup='1'), - # row=11, col=1) - # fig.add_trace(go.Scatter(x=tps, y=p_bat, name="p_bat", mode='lines', line=dict(color='green', dash='solid'), legendgroup='1'), - # row=11, col=1) - # fig.add_trace(go.Scatter(x=tps, y=p_capacitor, name="p_cap", mode='lines', line=dict(color='pink', dash='solid'), legendgroup='1'), - # row=11, col=1) - # fig.add_trace(go.Scatter(x=tps, y=np.ones_like(p_bat)*self.Pbat_max_pu, name="p_bat_lim", mode='lines', line=dict(color='green', dash='dot'), legendgroup='1'), - # row=11, col=1) - - # fig.update_xaxes(title_text='Time [s]', row=11, col=1) - # fig.update_yaxes(title_text='power [p.u.]', row=11, col=1) - - # # v_vsc (calculated) - # fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.real, mode='lines', line=dict(color='red', dash='solid')), - # row=12, col=1) - # fig.update_xaxes(title_text='Time [s]', row=12, col=1) - # fig.update_yaxes(title_text='v_vsc_d [p.u.]', row=12, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=v_vsc_dq.imag, mode='lines', line=dict(color='red', dash='solid')), - # row=12, col=2) - # fig.update_xaxes(title_text='Time [s]', row=12, col=2) - # fig.update_yaxes(title_text='v_vsc_q [p.u.]', row=12, col=2) - - # e_cap = 0.5*self.c_dc*(v_dc**2) - # e_ind = 0.5*self.l_dc*(i_L**2) - # fig.add_trace(go.Scatter(x=tps, y=soc, name='battery soc', mode='lines', line=dict(color='red', dash='solid')), - # row=13, col=1) - # fig.add_trace(go.Scatter(x=tps, y=e_cap, name='cap energy', mode='lines', line=dict(color='blue', dash='dot')), - # row=13, col=1) - # fig.add_trace(go.Scatter(x=tps, y=e_ind, name='ind energy', mode='lines', line=dict(color='pink', dash='dot')), - # row=13, col=1) - - # fig.add_trace(go.Scatter(x=tps, y=np.ones_like(soc)*self.SOC_max_pu, name="soc lim", mode='lines', line=dict(color='green', dash='dot')), - # row=13, col=1) - # fig.update_xaxes(title_text='Time [s]', row=13, col=1) - # fig.update_yaxes(title_text='energy [p.u.]', row=13, col=1) - - # duty_cycle = self.kp_i_L*(self.kp_v_dc*(v_dc_ref - v_dcf) + x1 - i_Lf + self.Kff_idc*i_dcf + self.Kff_iload*i_loadf) + x2 - # duty_cycle = np.clip(duty_cycle, 0.0, 1.0) - # fig.add_trace(go.Scatter(x=tps, y=duty_cycle, name="duty cycle", mode='lines', line=dict(color='red', dash='solid')), - # row=13, col=2) - # fig.update_xaxes(title_text='Time [s]', row=13, col=2) - # fig.update_yaxes(title_text='duty cycle [p.u.]', row=13, col=2) - - # fig.add_trace(go.Scatter(x=tps, y=x3, mode='lines', line=dict(color='red', dash='solid')), - # row=14, col=1) - # fig.update_xaxes(title_text='Time [s]', row=14, col=1) - # fig.update_yaxes(title_text='x3 [p.u.]', row=14, col=1) - - # # Vmag - # vmag = np.sqrt(np.square(v_sh_d)+np.square(v_sh_q)) - # fig.add_trace(go.Scatter(x=tps, y=vmag, mode='lines', line=dict(color='red', dash='solid')), - # row=14, col=2) - # fig.update_xaxes(title_text='Time [s]', row=14, col=2) - # fig.update_yaxes(title_text='Vmag [p.u.]', row=14, col=2) - - - - - # name = f"{self.type_}_{self.id}" - # fig.update_layout( title_text = name, - # title_x=0.5, - # showlegend = False, - # ) - - # fig.update_layout(height=1200*3, - # width=800*2, - # showlegend=True, - # margin={'t': 0, 'l': 0, 'b': 0, 'r': 0}) - - # fig.write_html(os.path.join(output_dir, name + ".html")) - - - - + \ No newline at end of file