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..b50acd6 --- /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,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 new file mode 100644 index 0000000..f033174 --- /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,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 new file mode 100644 index 0000000..b13b274 --- /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.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 new file mode 100644 index 0000000..3b76ba4 --- /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.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 new file mode 100644 index 0000000..ed9aa45 --- /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 +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 new file mode 100644 index 0000000..fb3202e --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/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_e/run.py b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py new file mode 100644 index 0000000..b3d0bdf --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfli_e/run.py @@ -0,0 +1,38 @@ +""" +Simulates an infinite source and GFLI_e connected via a transmission line. + +""" +# Import Python standard and third-party packages +from pathlib import Path + +# Import sting package +from sting import main + +# Step-change input to applied to the system +def step1(t): + return 0.3 if t >= 0.1 else 0.0 + +def step2(t): + return 0.0 + +inputs = { + 'infinite_sources_0': { + 'v_ref_d': step1 + }, + 'gfli_e_0': { + 'i_load_ref': 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 +_, 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) + +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 0000000..3855df0 Binary files /dev/null and b/examples/small_signal_and_emt/2-bus-src-gfmi_e/emt_heatmaps.png differ 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..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 -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.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/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..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.9590749740600586 +time_spent_seconds,0.06470894813537598 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..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 @@ -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,-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 -"('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..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 @@ -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,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 -"('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..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 @@ -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.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 8fe7525..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 @@ -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.11862845497804296 +v_dc_f,gfmi_e_0,"",1.05 +i_dc_f,gfmi_e_0,"",-0.35648974046573473 +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.11862845497804296 +v_dc,gfmi_e_0,"",1.05 +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 +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..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 @@ -1,21 +1,32 @@ + + # 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 sys +sys.path.append("/Users/ruthkravis/Documents/STING") # Import sting package + +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 + + +# 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.3 if t >= 0.2 else 0.0 def step2(t): return 0.0 @@ -23,48 +34,101 @@ def step2(t): def step3(t): 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.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': step1}, +inputs = {'infinite_sources_0': {'v_ref_d': step2}, 'gfmi_e_0': {'p_ref': step2, 'q_ref': step2, 'v_ref': step2, 'v_dc_ref': step2, 'v_s': step2, - 'i_load_ref': step2}} + 'i_load_ref': sin_oscillation}} + +t_max = 1.0 + +# 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) -t_max = 4.0 +#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 = 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 +# # Load system from CSV files +# sys = System.from_csv(case_directory=case_dir) -# 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 +# # 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 -# Run power flow -pf = ACPowerFlow(system=sys, model_settings=None, solver_settings=None) -pf.solve() +# # 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 + +# # 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 + + +# # 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..5ea78ad --- /dev/null +++ b/examples/small_signal_and_emt/2-bus-src-gfmi_e/run_compare_ssm_emt.py @@ -0,0 +1,459 @@ +""" +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 +from scipy import signal + +# 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 + + +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': no_step, + 'q_ref': no_step, + 'v_ref': no_step, + 'v_dc_ref': no_step, + 'v_s': no_step, + 'i_load_ref': square_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 0000000..1df2492 Binary files /dev/null and b/examples/small_signal_and_emt/2-bus-src-gfmi_e/ssm_heatmaps.png differ 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..4602392 --- /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,i_load_ref +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,0.6 \ 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/run.py b/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py new file mode 100644 index 0000000..ad5a469 --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfli_d/run.py @@ -0,0 +1,36 @@ +""" +Simulates an infinite source and GFLI_d connected via a transmission line. + +""" +# Import Python standard and third-party packages +from pathlib import Path + +# Import sting package +from sting import main + +# Specify path of the case study directory +case_dir = Path(__file__).resolve().parent + +# Step function to simulate +def step1(t): + return 0.3 if t >= 0.1 else 0.0 + +def step2(t): + return 0.0 + +inputs = { + 'infinite_sources_0': { + 'v_ref_d': step1 + }, + 'gfli_d_0': { + '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 +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-gfm/emt_analysis.py b/examples/small_signal_and_emt/2-bus_src-gfm/emt_analysis.py new file mode 100644 index 0000000..c3a806c --- /dev/null +++ b/examples/small_signal_and_emt/2-bus_src-gfm/emt_analysis.py @@ -0,0 +1,201 @@ +""" + +Analysis of EMT response of gfmi_c 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_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 0000000..72c694a Binary files /dev/null and b/examples/small_signal_and_emt/2-bus_src-gfm/emt_heatmaps.png differ 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..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.016715049743652344 +time_spent_seconds,0.0744011402130127 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 0000000..1df2492 Binary files /dev/null and b/examples/small_signal_and_emt/2-bus_src-gfm/ssm_heatmaps.png differ 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_d.py b/sting/generator/gfli_d.py new file mode 100644 index 0000000..c703690 --- /dev/null +++ b/sting/generator/gfli_d.py @@ -0,0 +1,635 @@ +""" +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: 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 + i_load_ref: float + SOC_init_pu: float # initial battery state of charge (pu) + 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 + + + # 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, 0], + [(1-duty_cycle)/c_dc, 0, -1/c_dc, 0], + [0, 0, -1/(wb*Tload), 0], + [1, 0, 0, 0]]), + 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)], + [0, 0, 0, 0]]), + C = np.hstack((np.eye(3),np.zeros((3,1)))), + 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', 'soc'], + init = [i_L, v_dc, i_load, self.SOC_init_pu] + ) + ) + + # 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 = 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) + + + 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=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 + + + 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', 'soc'], + 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, self.SOC_init_pu] + ) + + # 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, soc = 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, 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 + + # 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) + + # SOC + d_soc = i_L + + 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_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, soc], [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,soc = 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,soc = 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)]) + + # 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.xf1_pu * 1j), i_vsc_dq) + p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real + + 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', 'i_l_f', 'v_dc_f', 'i_dc_f', 'i_load_f','x_1', 'x_2', 'i_L', 'v_dc', 'i_load', 'soc', '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, i_Lf, v_dcf, i_dcf, i_loadf, x1, x2, i_L, v_dc, i_load, soc, p_vsc, p_sh, p_load, q_sh], + time=tps + ) + return results \ No newline at end of file diff --git a/sting/generator/gfli_e.py b/sting/generator/gfli_e.py new file mode 100644 index 0000000..7e7f8ea --- /dev/null +++ b/sting/generator/gfli_e.py @@ -0,0 +1,577 @@ +""" +This module implements a GFLI that incorporates: +- LCL filter: Two Series RL branches (one branch is the transformer) and one Parallel RC shunt. +- 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 + +""" +# ---------------------- +# Import python packages +# ---------------------- +import numpy as np +from dataclasses import dataclass, field +from typing import NamedTuple, Optional + +# ------------------ +# 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 + i_dc: float + i_load_ref: 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 + 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_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 + v_dc_ref: float # added + c_dc: 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 + 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 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, 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/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/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.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"])) + + # 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] ) ) + + # 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, 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, 0, -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 = 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 + + # dc power balance linearization + 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 # theta + + # Construction of CCM matrices + 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 + ) + ) + + + 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 + [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, -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)) + + 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=["v_dc_ref", "i_load_ref", "i_bus_q_ref", "v_bus_D", "v_bus_Q"], + type=["device", "device", "device", "grid", "grid"], + 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( + 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.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 current controller + pi_cc_dq = ( + v_vsc_dq - self.beta * v_bus_dq - 1j * (self.xf1_pu + self.xf2_pu) * i_bus_dq + ) + + # 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 = -v_dc/self.r_dc - i_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, + 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 = ['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 = [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=["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", "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 + # ------- + + 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, int_vdc, v_dc, i_load = self.variables_emt.x.value + + # Get input values (external inputs) + v_dc_ref, i_load_ref, i_bus_q_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 + + # outer loop Vdc control + i_bus_d_ref = self.kp_oc_pu*(-v_dc_ref + v_dc) + int_vdc + + # 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) + + 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) + i_dc = (v_vsc_d*i_vsc_d + v_vsc_q*i_vsc_q)/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, theta_pll) + + 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 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 + + 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] + + 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) + + 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): + + 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) -> 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, 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)]) + + # 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.xf1_pu * 1j), i_vsc_dq) + p_vsc = (v_vsc_dq*np.conjugate(i_vsc_dq)).real + + 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_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_sh, p_load, q_sh], + time=tps + ) + return results \ No newline at end of file diff --git a/sting/generator/gfmi_e.py b/sting/generator/gfmi_e.py index 99cb9db..9212d55 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 """ # ---------------------- @@ -113,12 +114,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): @@ -292,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 @@ -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) @@ -422,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, @@ -470,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'], + 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] + 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 @@ -504,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 = 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 @@ -515,21 +516,19 @@ 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 # 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 +622,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 @@ -643,21 +642,23 @@ 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 - # 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] - #return [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + #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], [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]) @@ -665,13 +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 = 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, 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 = 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 tps = self.variables_emt.x.time @@ -680,162 +681,24 @@ 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) - - fig = make_subplots( - rows=12, 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_sh = (v_sh_dq*np.conjugate(i_bus_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 - 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.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) - - - 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")) - - - - + 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", '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, p_vsc, p_sh, p_load], + time=tps + ) + + return results_emt + + + \ No newline at end of file diff --git a/sting/system/core.py b/sting/system/core.py index aaddfa3..931c477 100644 --- a/sting/system/core.py +++ b/sting/system/core.py @@ -28,6 +28,8 @@ 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.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 @@ -59,6 +61,8 @@ class System: gfmi_d: list[GFMId] = None 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