From c78fb9e962fb75a0b7775c9d61885dc26feadd7b Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 17 Feb 2025 15:25:19 -0500 Subject: [PATCH 01/20] Added README.md that describes 3Bus equations. --- examples/Bus3Phasor/README.md | 147 ++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 examples/Bus3Phasor/README.md diff --git a/examples/Bus3Phasor/README.md b/examples/Bus3Phasor/README.md new file mode 100644 index 00000000..c6acdac5 --- /dev/null +++ b/examples/Bus3Phasor/README.md @@ -0,0 +1,147 @@ +Notation: $I_{a,b}$ represents the current flowing into Bus b from the branch connecting Bus a and Bus b, $I_{G,a}$ the current from generator a, $I_{L,a}$ the current from load a, and $V_a$ the voltage at Bus a. +Each component is sub-index by the components ID. The 3Bus model is composed of 3 buses, 3 branches, 2 loads and 2 generators. For simplicity in some equation, $I = \left[\begin{array}{c} I_r \\ I_i \end{array}\right]$ to represent complex variables. + +## Bus equations: +```math +I_{G,1}+I_{2,1}+I_{3,1}+I_{L,1} = 0 +``` +```math +I_{L,2}+I_{1,2}+I_{3,2} = 0 +``` +```math +I_{G,3}+I_{1,3}+I_{2,3} = 0 +``` + +## Branch equations: + +(branch connecting Bus 1 and Bus 2) +```math +\left[ \begin{array}{c} + I_{2,1} \\ + I_{1,2} + \end{array}\right] = + Y^{[1]}\left[ \begin{array}{c} + V_1 \\ + V_2 +\end{array}\right] +``` + +(branch connecting Bus 2 and Bus 3) +```math +\left[ \begin{array}{c} + I_{3,2} \\ + I_{2,3} + \end{array}\right] = + Y^{[2]}\left[ \begin{array}{c} + V_2 \\ + V_3 + \end{array}\right] +``` + +(branch connecting Bus 1 and Bus 3) +```math +\left[ \begin{array}{c} + I_{3,1} \\ + I_{1,3} + \end{array}\right] = + Y^{[3]}\left[ \begin{array}{c} + V_1 \\ + V_3 + \end{array}\right] +``` + + ## Load equations: + Loads connected to bus 1: +```math + I_{L,1} = \frac{V_1}{R_1} +``` +Loads connected to bus 2: +```math + I_{L,2} = \frac{V_2}{R_2} +``` + +## Generator 1 +Generator connected to bus 1:
+Differential equations: +```math +\dot{\delta}_1 = \omega_1 \cdot \omega_0 +``` +```math +\dot{\omega}_1 = \frac{1}{2H_1}\left( \frac{P_{mech,1} - D_1\omega_1}{1+\omega_1} - T_{elec,1}\right) +``` +```math +\dot{E}_{qp,1} = \frac{1}{T_{dop,1}}\left( E_{fd,1} - \left( E_{qp,1} + X_{d1,1}\left( I_{d,1} + X_{d3,1}\left( E_{qp,1} - \psi_{dp,1} - X_{d2,1}I_{d,1} \right) \right) + \psi_{dpp,1}\cdot k_{sat,1} \right) \right) +``` +```math +\dot{\psi}_{dp,1} = \frac{1}{T_{dopp,1}} \left( E_{qp,1} - \psi_{dp,1} - X_{d2,1} I_{d, 1} \right) +``` +```math +\dot{\psi}_{qp,1} = \frac{1}{T_{qopp,1}} \left( E_{dp,1} - \psi_{qp,1} + X_{q2,1}I_{q,1}\right) +``` +```math +\dot{E}_{dp,1} = \frac{1}{T_{qop,1}}\left(-E_{dp,1} + X_{qd,1}\psi_{qpp,1}\cdot k_{sat,1} + X_{q1,1} \left(I_{q,1} - X_{q3,1} \left(E_{dp,1} + I_{q,1}X_{q2,1} - \psi_{qp,1}\right)\right)\right) +``` +Algebraic equations: +```math +V_{d,1} = -\psi_{qpp,1}(\psi_{qp^{\prime},1}E_{dp,1})(1+\omega_1) +``` +```math +V_{q,1} = \psi_{dpp,1}(\psi_{dp^{\prime},1}E_{qp,1})(1+\omega_1) +``` +```math +I_{d,1} = I_{G,1, r}\sin(\delta_1) - I_{G,1,i}\cos(\delta_1) +``` +```math +I_{q,1} = I_{G,1, r}\cos(\delta_1) + I_{G,1,i}\sin(\delta_1) +``` + +Network interfaces: +```math +V_{d,1} = V_{1,r}\sin(\delta_1) - V_{1,i}\cos(\delta_1) + I_{d,1} R_{a,1} -I_{q,1} X_{qpp,1} +``` +```math +V_{q,1} = V_{1,r}\cos(\delta_1) + V_{1,i}\sin(\delta_1) + I_{d,1}X_{qpp,1} + I_{q,1}R_{a,1} +``` +## Generator 3 +Generator connected to bus 3:
+Differential equations: +```math +\dot{\delta}_3 = \omega_3 \cdot \omega_0 +``` +```math +\dot{\omega}_3 = \frac{1}{2H_3}\left( \frac{P_{mech,3} - D_3\omega_3}{1+\omega_3} - T_{elec,3}\right) +``` +```math +\dot{E_{qp,3}} = \frac{1}{T_{dop,3}}\left( E_{fd,3} - \left( E_{qp,3} + X_{d1,3}\left( I_{d,3} + X_{d3,3}\left( E_{qp,3} - \psi_{dp,3} - X_{d2,3}I_{d,3} \right) \right) + \psi_{dpp,3}\cdot k_{sat,3} \right) \right) +``` +```math +\dot{\psi}_{dp,3} = \frac{1}{T_{dopp,3}} \left( E_{qp,3} - \psi_{dp,3} - X_{d2,3} I_{d,3} \right) +``` +```math +\dot{\psi}_{qp,3} = \frac{1}{T_{qopp,3}} \left( E_{dp,3} - \psi_{qp,3} + X_{q2,3}I_{q,3}\right) +``` +```math +\dot{E}_{dp,3} = \frac{1}{T_{qop,3}}\left(-E_{dp,3} + X_{qd,3}\psi_{qpp,3}\cdot k_{sat,3} + X_{q1,3} \left(I_{q,3} - X_{q3,3} \left(E_{dp,3} + I_{q,3}X_{q2,3} - \psi_{qp,3}\right)\right)\right) +``` +Algebraic equations: +```math +V_{d,3} = -\psi_{qpp,3}(\psi_{qp^{\prime},3}E_{dp,3})(1+\omega_3) +``` +```math +V_{q,3} = \psi_{dpp,3}(\psi_{dp^{\prime},3}E_{qp,3})(1+\omega_3) +``` +```math +I_{d,3} = I_{G,3, r}\sin(\delta_3) - I_{G,3,i}\cos(\delta_3) +``` +```math +I_{q,3} = I_{G,3, r}\cos(\delta_3) + I_{G,3,i}\sin(\delta_3) +``` + +Network interfaces: +```math +V_{d,3} = V_{3,r}\sin(\delta_3) - V_{3,i}\cos(\delta_3) + I_{d,3} R_{a,3} -I_{q,3} X_{qpp,3} +``` +```math +V_{q,3} = V_{3,r}\cos(\delta_3) + V_{3,i} \sin(\delta_3) + I_{d,3}X_{qpp,3} + I_{q,3}R_{a,3} +``` + From 290ebd0ba3472e1cade0f3d167cef0bad4f00959 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry <153353767+abdourahmanbarry@users.noreply.github.com> Date: Mon, 24 Feb 2025 01:58:05 -0500 Subject: [PATCH 02/20] Update examples/Bus3Phasor/README.md "indices r and i written before numerical indices" Co-authored-by: pelesh --- examples/Bus3Phasor/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/Bus3Phasor/README.md b/examples/Bus3Phasor/README.md index c6acdac5..49aa127b 100644 --- a/examples/Bus3Phasor/README.md +++ b/examples/Bus3Phasor/README.md @@ -97,8 +97,7 @@ I_{q,1} = I_{G,1, r}\cos(\delta_1) + I_{G,1,i}\sin(\delta_1) Network interfaces: ```math -V_{d,1} = V_{1,r}\sin(\delta_1) - V_{1,i}\cos(\delta_1) + I_{d,1} R_{a,1} -I_{q,1} X_{qpp,1} -``` +V_{d,1} = V_{r,1}\sin(\delta_1) - V_{i,1}\cos(\delta_1) + I_{d,1} R_{a,1} -I_{q,1} X_{qpp,1} ```math V_{q,1} = V_{1,r}\cos(\delta_1) + V_{1,i}\sin(\delta_1) + I_{d,1}X_{qpp,1} + I_{q,1}R_{a,1} ``` From 86b48ebe237c89df6a5cc17ff2e98c54bec0df2e Mon Sep 17 00:00:00 2001 From: abdourahmanbarry <153353767+abdourahmanbarry@users.noreply.github.com> Date: Mon, 24 Feb 2025 01:59:55 -0500 Subject: [PATCH 03/20] Update examples/Bus3Phasor/README.md "Indices r and i written before numerical indices" Co-authored-by: pelesh --- examples/Bus3Phasor/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/Bus3Phasor/README.md b/examples/Bus3Phasor/README.md index 49aa127b..41b9ae7b 100644 --- a/examples/Bus3Phasor/README.md +++ b/examples/Bus3Phasor/README.md @@ -130,8 +130,7 @@ V_{d,3} = -\psi_{qpp,3}(\psi_{qp^{\prime},3}E_{dp,3})(1+\omega_3) V_{q,3} = \psi_{dpp,3}(\psi_{dp^{\prime},3}E_{qp,3})(1+\omega_3) ``` ```math -I_{d,3} = I_{G,3, r}\sin(\delta_3) - I_{G,3,i}\cos(\delta_3) -``` +I_{d,3} = I_{G,r,3}\sin(\delta_3) - I_{G,i,3}\cos(\delta_3) ```math I_{q,3} = I_{G,3, r}\cos(\delta_3) + I_{G,3,i}\sin(\delta_3) ``` From 4888ce92fe512f5281f70fc56a3a7db3562e31e5 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 24 Feb 2025 13:24:00 -0500 Subject: [PATCH 04/20] Added 3Bus diagram to README.md in Bus3Phasor folder --- examples/Bus3Phasor/genrou.png | Bin 0 -> 108343 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/Bus3Phasor/genrou.png diff --git a/examples/Bus3Phasor/genrou.png b/examples/Bus3Phasor/genrou.png new file mode 100644 index 0000000000000000000000000000000000000000..75e948f202e3650c7b208a872d077e452c12a23d GIT binary patch literal 108343 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

6frW{<7sr(fy7 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 R`y1dT`T2`zsj$A^{{n|p3^D)! literal 0 HcmV?d00001 From a4c1d21a7bf58d5e1d80ea21b78015b5d75cc718 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 24 Feb 2025 13:31:55 -0500 Subject: [PATCH 05/20] Indices r and i written before numerical indices --- examples/Bus3Phasor/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/Bus3Phasor/README.md b/examples/Bus3Phasor/README.md index 41b9ae7b..46fff90d 100644 --- a/examples/Bus3Phasor/README.md +++ b/examples/Bus3Phasor/README.md @@ -1,3 +1,6 @@ + + + Notation: $I_{a,b}$ represents the current flowing into Bus b from the branch connecting Bus a and Bus b, $I_{G,a}$ the current from generator a, $I_{L,a}$ the current from load a, and $V_a$ the voltage at Bus a. Each component is sub-index by the components ID. The 3Bus model is composed of 3 buses, 3 branches, 2 loads and 2 generators. For simplicity in some equation, $I = \left[\begin{array}{c} I_r \\ I_i \end{array}\right]$ to represent complex variables. From 745d6557cf022f64b82f9e5395d8de1b2985497c Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 24 Feb 2025 13:37:09 -0500 Subject: [PATCH 06/20] Added the diagram of the 3Bus example. --- examples/Bus3Phasor/README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/Bus3Phasor/README.md b/examples/Bus3Phasor/README.md index 46fff90d..798f2368 100644 --- a/examples/Bus3Phasor/README.md +++ b/examples/Bus3Phasor/README.md @@ -1,5 +1,5 @@ - + Notation: $I_{a,b}$ represents the current flowing into Bus b from the branch connecting Bus a and Bus b, $I_{G,a}$ the current from generator a, $I_{L,a}$ the current from load a, and $V_a$ the voltage at Bus a. Each component is sub-index by the components ID. The 3Bus model is composed of 3 buses, 3 branches, 2 loads and 2 generators. For simplicity in some equation, $I = \left[\begin{array}{c} I_r \\ I_i \end{array}\right]$ to represent complex variables. @@ -92,17 +92,18 @@ V_{d,1} = -\psi_{qpp,1}(\psi_{qp^{\prime},1}E_{dp,1})(1+\omega_1) V_{q,1} = \psi_{dpp,1}(\psi_{dp^{\prime},1}E_{qp,1})(1+\omega_1) ``` ```math -I_{d,1} = I_{G,1, r}\sin(\delta_1) - I_{G,1,i}\cos(\delta_1) +I_{d,1} = I_{G,r,1}\sin(\delta_1) - I_{G,i,1}\cos(\delta_1) ``` ```math -I_{q,1} = I_{G,1, r}\cos(\delta_1) + I_{G,1,i}\sin(\delta_1) +I_{q,1} = I_{G,r,1}\cos(\delta_1) + I_{G,i,1}\sin(\delta_1) ``` Network interfaces: ```math V_{d,1} = V_{r,1}\sin(\delta_1) - V_{i,1}\cos(\delta_1) + I_{d,1} R_{a,1} -I_{q,1} X_{qpp,1} +``` ```math -V_{q,1} = V_{1,r}\cos(\delta_1) + V_{1,i}\sin(\delta_1) + I_{d,1}X_{qpp,1} + I_{q,1}R_{a,1} +V_{q,1} = V_{r, 1}\cos(\delta_1) + V_{i,1}\sin(\delta_1) + I_{d,1}X_{qpp,1} + I_{q,1}R_{a,1} ``` ## Generator 3 Generator connected to bus 3:
@@ -134,15 +135,16 @@ V_{q,3} = \psi_{dpp,3}(\psi_{dp^{\prime},3}E_{qp,3})(1+\omega_3) ``` ```math I_{d,3} = I_{G,r,3}\sin(\delta_3) - I_{G,i,3}\cos(\delta_3) +``` ```math -I_{q,3} = I_{G,3, r}\cos(\delta_3) + I_{G,3,i}\sin(\delta_3) +I_{q,3} = I_{G,r, 3}\cos(\delta_3) + I_{G,i,3}\sin(\delta_3) ``` Network interfaces: ```math -V_{d,3} = V_{3,r}\sin(\delta_3) - V_{3,i}\cos(\delta_3) + I_{d,3} R_{a,3} -I_{q,3} X_{qpp,3} +V_{d,3} = V_{r,3}\sin(\delta_3) - V_{i,3}\cos(\delta_3) + I_{d,3} R_{a,3} -I_{q,3} X_{qpp,3} ``` ```math -V_{q,3} = V_{3,r}\cos(\delta_3) + V_{3,i} \sin(\delta_3) + I_{d,3}X_{qpp,3} + I_{q,3}R_{a,3} +V_{q,3} = V_{r,3}\cos(\delta_3) + V_{i,3} \sin(\delta_3) + I_{d,3}X_{qpp,3} + I_{q,3}R_{a,3} ``` From 5a4619d9d89f89f609d0e1a95318cc91656d611d Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Fri, 7 Mar 2025 07:43:13 -0500 Subject: [PATCH 07/20] Added equations in the form f(t, y, y')=0 for the 3bus example --- examples/Bus3Phasor/bus3phasor.cpp | 280 +++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 examples/Bus3Phasor/bus3phasor.cpp diff --git a/examples/Bus3Phasor/bus3phasor.cpp b/examples/Bus3Phasor/bus3phasor.cpp new file mode 100644 index 00000000..397e6e39 --- /dev/null +++ b/examples/Bus3Phasor/bus3phasor.cpp @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include + +#include +#include + + +struct generator_params +{ + double w_0; + double H; + double D; + double T_dop; + double T_dopp; + double T_qopp; + double T_qop; + double X_d1; + double X_d2; + double X_d3; + double X_d4; + double X_d5; + double X_q1; + double X_q2; + double X_q3; + double X_q4; + double X_q5; + double X_d; + double X_dp; + double X_dpp; + double X_q; + double X_qp; + double X_qpp; + double X_L; + double X_qd; + double R_a; + double S_A; + double S_B; +}; + +int get_psi_qpp(double &psi_qpp,double psi_qp, double X_q4, double E_dp, double X_q5) +{ + psi_qpp = -psi_qp*X_q4 - E_dp*X_q5; + return 0; +} + +int get_psi_dpp(double &psi_dpp, double psi_dp, double X_d4, double E_qp, double X_d5) +{ + psi_dpp = psi_dp*X_d4 + E_qp*X_d5; + return 0; +} + + +int diff_gen(std::vector Y, std::vector &Yp, std::vector Z, generator_params P, double P_mech, double E_fd) +{ + double delta = Y[0]; + double w = Y[1]; + double E_qp = Y[2]; + double psi_dp = Y[3]; + double psi_qp = Y[4]; + double E_dp = Y[5]; + double I_d = Z[0]; + double I_q = Z[1]; + + double psi_qpp; + double psi_dpp; + double psi_pp; + double k_sat; + double T_elec; + + get_psi_qpp(&psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); + get_psi_dpp(&psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); + psi_pp = sqrt(psi_qpp*psi_qpp + psi_dpp*psi_dpp); + k_sat = P.S_B*(psi_pp - P.S_A)*(psi_pp - P.S_A); + T_elec = (psi_dpp - I_d*P.X_dpp)*I_q - (psi_qpp - I_q*P.X_dpp)*I_d; + + Yp[0] = w*P.w_0; + Yp[1] = (1/(2*P.H))*((P_mech - P.D*w)/(1+w) - T_elec); + Yp[2] = (1/P.T_dop)*(E_fd - (E_qp + P.X_d1*(I_d + P.X_d3*(E_qp - psi_dp - P.X_d2*I_d)) + psi_dpp*k_sat)); + Yp[3] = (1/P.T_dopp)*(E_qp - psi_dp - P.X_d2*I_d); + Yp[4] = (1/P.T_qopp)*(E_dp - psi_qp + X_q2*I_q); + Yp[5] = (1/P.T_qop)*(-E_dp + X_qd*psi_qpp*k_sat + P.X_q1*(I_q - P.X_q3*(E_dp + I_q*P.X_q2 - psi_qp))) + + return 0; + +} + +int get_algebraic_res(sdt::vector &res, std::vector Y, std::vector Z, std::vector V, std::vector I, double R_a, double X_qpp) +{ + double delta = Y[0]; + double w = Y[1]; + double E_qp = Y[2]; + double psi_dp = Y[3]; + double psi_qp = Y[4]; + double E_dp = Y[5]; + + double I_d = Z[0]; + double I_q = Z[1]; + + double I_r = I[0]; + double I_i = I[1]; + + double V_r = V[0]; + double V_i = V[1]; + + double psi_qpp; + double psi_dpp; + + get_psi_qpp(&psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); + get_psi_dpp(&psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); + + res[0] = I_d - I_r*sin(delta) + I_i*cos(delta); + res[1] = I_q - I_r*cos(delta) - I_i*sin(delta); + res[2] = -psi_qpp*(1 + w) - V_r*sin(delta) + V_i*cos(delta) - I_d*R_a + I_q*X_qpp; + res[3] = psi_dpp*(1 + w) - V_r*cos(delta) - V_i*sin(delta) - I_d*X_qpp - I_q*R_a; +} + + +int buseq(std::vector &I, std::vector &r) +{ + r[0] = 0.0; + r[1] = 0.0; + + for (size_t i = 0; i < I.size(); i++) + { + if(i%2 == 0){ + r[0] += I[i]; + } + else{ + r[1] += I[i]; + } + } + +} + +/** + * @brief Calculates the current given the voltages + * I[0]: I_r1 + * I[1]: I_i1 + * I[2]: I_r2 + * I[3]: I_i2 + * + * V[0]: V_r1 + * V[1]: V_i1 + * V[2]: V_r2 + * V[3]: V_i2 + */ +int brancheq(std::vector &res, std::vector &I, std::vector &V, double g, double b, double B, double G) +{ + res[0] = -I[0] -(g + G/2)*V[0] + (b + B/2)*V[1] + g*V[2] - b*V[3]; + res[1] = -I[1] -(b + B/2)*V[0] - (g + G/2)*V[1] + b*V[2] + g*V[3]; + res[2] = -I[2] = g*V[0] - b*V[1] - (g + G/2)*V[2] + (b + B/2)*V[3]; + res[3] = -I[3] = b*V[0] + g*V[1] - (b + B/2)*V[2] - (g + G/2)*V[3]; + + return 0; +} + +/** + * @brief calculates the current for the load given the voltages and Resistances + * I[0]: I_r1 + * I[1]: I_i1 + * + * V[0]: V_r1 + * V[1]: V_i1 + */ +int loadeq(std::vector &I, std::vector &V, std::vector &res, double R1, double R2) +{ + res[0] = I[0] - (V[0]*R1 + V[1]*R2)/(R1*R1 + R2*R2); + res[1] = I[1] - (V[1]*R1 + V[0]*R2)/(R1*R1 + R2*R2); + + return 0; +} + +int loadeq_res() + +std::vector diff_res(double t, std::vector &Y, std::vector &Yp) +{ + std::vector gen_res; + + std::vector sub_Y = (Y.begin(), Y.begin() + 6); + std::vector sub_Yp = (Yp.begin(), Yp.begin() + 6); + std::vector Z = (Y.begin() + 32, Yp.begin() + 34); + std::vector diff_Yp(6); + generator_params params; + diff_gen(sub_Y, &diff_Yp, Z, params, 1, 1); + + for(int i=0; i < sub_Yp.size(); i++) + { + gen_res.push_back(sub_Yp[i] - Yp[i] ); + } + + std::vector algeb_res(4); + std::vector V = {Y[34], Y[35]}; + std::vector I = {Y[6], Y[7]}; + get_algebraic_res(&algeb_res, sub_Y, Z, V, I, 1, 1); + + for(int i=0; i < algeb_res.size(); i++) + { + gen_res.push_back(algeb_res[i]); + } + + //Calculate residuals from bus equations and + //append to the global residual vector gen_res + int buseq(std::vector &I, std::vector &r) + + std::vector bus_res(2); + + std::vector I_bus_eqn = (Y.begin() + 6, Y.begin() + 14); + buseq(I_bus_eqn, &bus_res); + gen_res.insert(gen_res.end(), bus_res.begin(), bus_res.end()); + + I_bus_eqn = (Y.begin() + 14, Y.begin() + 20); + buseq(I_bus_eqn, &bus_res); + gen_res.insert(gen_res.end(), bus_res.begin(), bus_res.end()); + + I_bus_eqn = (Y.begin() + 20, Y.begin() + 26); + buseq(I_bus_eqn, &bus_res); + gen_res.insert(gen_res.end(), bus_res.begin(), bus_res.end()); + + //Calculate residuals from branch equations and + //append to the global residual vector gen_res + std::vector branch_res(4); + + std::vector I_branch = {Y[8], Y[9], Y[16], Y[17]}; + std::vector V_branch = {Y[26], Y[27], Y[28], Y[29]}; + brancheq(&branch_res, I_branch, V_branch, 1, 1, 1, 1); + gen_res.insert(gen_res.end(), branch_res.begin(), branch_res.end()); + + I_branch = {Y[18], Y[19], Y[24], Y[25]}; + V_branch = {Y[28], Y[29], Y[30], Y[31]}; + brancheq(&branch_res, I_branch, V_branch, 1, 1, 1, 1); + gen_res.insert(gen_res.end(), branch_res.begin(), branch_res.end()); + + + I_branch = {Y[10], Y[11], Y[22], Y[23]}; + V_branch = {Y[26], Y[27], Y[30], Y[31]}; + brancheq(&branch_res, I_branch, V_branch, 1, 1, 1, 1); + gen_res.insert(gen_res.end(), branch_res.begin(), branch_res.end()); + + //Calculate residuals from load equations and + //append to the global residual vector gen_res + int loadeq(std::vector &I, std::vector &V, std::vector &res, double R1, double R2) + + std::vector load_res(2); + std::vector I_load = {Y[12], Y[13]}; + std::vector V_load = {Y[26], Y[27]}; + loadeq(I_load, V_load, load_res, 1, 1); + gen_res.insert(gen_res.end(), load_res.begin(), load_res.end()); + + return gen_res; + +} + +int get_params() + + +int main(int argc, char const *argv[]) +{ + //These are for branch 1 + double b1 = 0.0; + double g1 = 0.0; + double B1 = 0.0; + double G1 = 0.0; + + //These are for branch 2 + double b2 = 0.0; + double g2 = 0.0; + double B2 = 0.0; + double G2 = 0.0; + + //These are for branch 3 + double b3 = 0.0; + double g3 = 0.0; + double B3 = 0.0; + double G3 = 0.0; + + return 0; +} From 83a64a690b802ffabbc0297edd07ff1c0cb82824 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Mon, 10 Mar 2025 10:21:01 -0400 Subject: [PATCH 08/20] Added code for the residual of the DAE for the 3Bus example. --- examples/Bus3Phasor/README.md | 43 ++- examples/Bus3Phasor/bus3phasor.cpp | 430 ++++++++++++++++++++++++----- 2 files changed, 395 insertions(+), 78 deletions(-) diff --git a/examples/Bus3Phasor/README.md b/examples/Bus3Phasor/README.md index 798f2368..e9d6e5aa 100644 --- a/examples/Bus3Phasor/README.md +++ b/examples/Bus3Phasor/README.md @@ -86,10 +86,10 @@ Differential equations: ``` Algebraic equations: ```math -V_{d,1} = -\psi_{qpp,1}(\psi_{qp^{\prime},1}E_{dp,1})(1+\omega_1) +V_{d,1} = -\psi_{qpp,1}(\psi_{qp,1},E_{dp,1})(1+\omega_1) ``` ```math -V_{q,1} = \psi_{dpp,1}(\psi_{dp^{\prime},1}E_{qp,1})(1+\omega_1) +V_{q,1} = \psi_{dpp,1}(\psi_{dp,1},E_{qp,1})(1+\omega_1) ``` ```math I_{d,1} = I_{G,r,1}\sin(\delta_1) - I_{G,i,1}\cos(\delta_1) @@ -105,6 +105,45 @@ V_{d,1} = V_{r,1}\sin(\delta_1) - V_{i,1}\cos(\delta_1) + I_{d,1} R_{a,1} -I_{q ```math V_{q,1} = V_{r, 1}\cos(\delta_1) + V_{i,1}\sin(\delta_1) + I_{d,1}X_{qpp,1} + I_{q,1}R_{a,1} ``` + +We can lump together the algebraic and network interface equations to get the following equivalent constraint equations: +```math +\left[\begin{array}{c} +I_{d} \\ +I_{q} +\end{array}\right] = +\left[\begin{array}{cr} +\sin(\delta_1) & -\cos(\delta_1)\\ +\cos(\delta_1) & \sin(\delta_1) +\end{array}\right] +\left[\begin{array}{c} +I_{G,r,1} \\ +I_{G,i,1} +\end{array}\right] +``` +```math +\left[\begin{array}{c} +-\psi_{qpp,1}(\psi_{qp,1},E_{dp,1})(1+\omega_1) \\ +\psi_{dpp,1}(\psi_{dp,1},E_{qp,1})(1+\omega_1) +\end{array}\right] +- \left[\begin{array}{cr} +\sin(\delta_1) & -\cos(\delta_1)\\ +\cos(\delta_1) & \sin(\delta_1) +\end{array}\right] +\left[\begin{array}{c} +V_{r,1} \\ +V_{i,1} +\end{array}\right] += +\left[\begin{array}{cr} +R_a & -X_{qpp}\\ +X_{qpp} & R_a +\end{array}\right] +\left[\begin{array}{c} +I_{d} \\ +I_{q} +\end{array}\right] +``` ## Generator 3 Generator connected to bus 3:
Differential equations: diff --git a/examples/Bus3Phasor/bus3phasor.cpp b/examples/Bus3Phasor/bus3phasor.cpp index 397e6e39..731a4e5b 100644 --- a/examples/Bus3Phasor/bus3phasor.cpp +++ b/examples/Bus3Phasor/bus3phasor.cpp @@ -3,9 +3,53 @@ #include #include #include - -#include -#include +#include +using namespace std; +//#include +//#include + +#define _DEL1 0 +#define _W1 1 +#define _E_qp1 2 +#define _PSI_dp1 3 +#define _PSI_qp1 4 +#define _E_dp1 5 +#define _I_Gr1 6 +#define _I_Gi1 7 +#define _I_r21 8 +#define _I_i21 9 +#define _I_r31 10 +#define _I_i31 11 +#define _I_Lr1 12 +#define _I_Li1 13 +#define _I_Lr2 14 +#define _I_Li2 15 +#define _I_r12 16 +#define _I_i12 17 +#define _I_r32 18 +#define _I_i32 19 +#define _I_Gr3 20 +#define _I_Gi3 21 +#define _I_r13 22 +#define _I_i13 23 +#define _I_r23 24 +#define _I_i23 25 +#define _V_r1 26 +#define _V_i1 27 +#define _V_r2 28 +#define _V_i2 29 +#define _V_r3 30 +#define _V_i3 31 +#define _I_d1 32 +#define _I_q1 33 +#define _DEL2 34 +#define _W2 35 +#define _E_qp2 36 +#define _PSI_dp2 37 +#define _PSI_qp2 38 +#define _E_dp2 39 +#define _I_d2 40 +#define _I_q2 41 struct generator_params @@ -55,12 +99,13 @@ int get_psi_dpp(double &psi_dpp, double psi_dp, double X_d4, double E_qp, double int diff_gen(std::vector Y, std::vector &Yp, std::vector Z, generator_params P, double P_mech, double E_fd) { - double delta = Y[0]; - double w = Y[1]; - double E_qp = Y[2]; - double psi_dp = Y[3]; - double psi_qp = Y[4]; - double E_dp = Y[5]; + double delta = Y[_DEL1]; + double w = Y[_W1]; + double E_qp = Y[_E_qp1]; + double psi_dp = Y[_PSI_dp1]; + double psi_qp = Y[_PSI_qp1]; + double E_dp = Y[_E_dp1]; + double I_d = Z[0]; double I_q = Z[1]; @@ -70,31 +115,31 @@ int diff_gen(std::vector Y, std::vector &Yp, std::vector double k_sat; double T_elec; - get_psi_qpp(&psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); - get_psi_dpp(&psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); + get_psi_qpp(psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); + get_psi_dpp(psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); psi_pp = sqrt(psi_qpp*psi_qpp + psi_dpp*psi_dpp); k_sat = P.S_B*(psi_pp - P.S_A)*(psi_pp - P.S_A); T_elec = (psi_dpp - I_d*P.X_dpp)*I_q - (psi_qpp - I_q*P.X_dpp)*I_d; - + Yp[0] = w*P.w_0; Yp[1] = (1/(2*P.H))*((P_mech - P.D*w)/(1+w) - T_elec); Yp[2] = (1/P.T_dop)*(E_fd - (E_qp + P.X_d1*(I_d + P.X_d3*(E_qp - psi_dp - P.X_d2*I_d)) + psi_dpp*k_sat)); Yp[3] = (1/P.T_dopp)*(E_qp - psi_dp - P.X_d2*I_d); - Yp[4] = (1/P.T_qopp)*(E_dp - psi_qp + X_q2*I_q); - Yp[5] = (1/P.T_qop)*(-E_dp + X_qd*psi_qpp*k_sat + P.X_q1*(I_q - P.X_q3*(E_dp + I_q*P.X_q2 - psi_qp))) + Yp[4] = (1/P.T_qopp)*(E_dp - psi_qp + P.X_q2*I_q); + Yp[5] = (1/P.T_qop)*(-E_dp + P.X_qd*psi_qpp*k_sat + P.X_q1*(I_q - P.X_q3*(E_dp + I_q*P.X_q2 - psi_qp))); return 0; } -int get_algebraic_res(sdt::vector &res, std::vector Y, std::vector Z, std::vector V, std::vector I, double R_a, double X_qpp) +int get_algebraic_res(std::vector &res, std::vector Y, std::vector Z, std::vector V, std::vector I, generator_params P, double R_a) { - double delta = Y[0]; - double w = Y[1]; - double E_qp = Y[2]; - double psi_dp = Y[3]; - double psi_qp = Y[4]; - double E_dp = Y[5]; + double delta = Y[_DEL1]; + double w = Y[_W1]; + double E_qp = Y[_E_qp1]; + double psi_dp = Y[_PSI_dp1]; + double psi_qp = Y[_PSI_qp1]; + double E_dp = Y[_E_dp1]; double I_d = Z[0]; double I_q = Z[1]; @@ -108,13 +153,15 @@ int get_algebraic_res(sdt::vector &res, std::vector Y, std::vect double psi_qpp; double psi_dpp; - get_psi_qpp(&psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); - get_psi_dpp(&psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); + get_psi_qpp(psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); + get_psi_dpp(psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); res[0] = I_d - I_r*sin(delta) + I_i*cos(delta); res[1] = I_q - I_r*cos(delta) - I_i*sin(delta); - res[2] = -psi_qpp*(1 + w) - V_r*sin(delta) + V_i*cos(delta) - I_d*R_a + I_q*X_qpp; - res[3] = psi_dpp*(1 + w) - V_r*cos(delta) - V_i*sin(delta) - I_d*X_qpp - I_q*R_a; + res[2] = -psi_qpp*(1 + w) - V_r*sin(delta) + V_i*cos(delta) - I_d*R_a + I_q*P.X_qpp; + res[3] = psi_dpp*(1 + w) - V_r*cos(delta) - V_i*sin(delta) - I_d*P.X_qpp - I_q*R_a; + + return 0; } @@ -132,6 +179,8 @@ int buseq(std::vector &I, std::vector &r) r[1] += I[i]; } } + + return 0; } @@ -147,12 +196,12 @@ int buseq(std::vector &I, std::vector &r) * V[2]: V_r2 * V[3]: V_i2 */ -int brancheq(std::vector &res, std::vector &I, std::vector &V, double g, double b, double B, double G) +int brancheq(std::vector &res, std::vector &I, std::vector &V, double g, double b, double G, double B) { res[0] = -I[0] -(g + G/2)*V[0] + (b + B/2)*V[1] + g*V[2] - b*V[3]; res[1] = -I[1] -(b + B/2)*V[0] - (g + G/2)*V[1] + b*V[2] + g*V[3]; - res[2] = -I[2] = g*V[0] - b*V[1] - (g + G/2)*V[2] + (b + B/2)*V[3]; - res[3] = -I[3] = b*V[0] + g*V[1] - (b + B/2)*V[2] - (g + G/2)*V[3]; + res[2] = -I[2] + g*V[0] - b*V[1] - (g + G/2)*V[2] + (b + B/2)*V[3]; + res[3] = -I[3] + b*V[0] + g*V[1] - (b + B/2)*V[2] - (g + G/2)*V[3]; return 0; } @@ -168,92 +217,158 @@ int brancheq(std::vector &res, std::vector &I, std::vector &I, std::vector &V, std::vector &res, double R1, double R2) { res[0] = I[0] - (V[0]*R1 + V[1]*R2)/(R1*R1 + R2*R2); - res[1] = I[1] - (V[1]*R1 + V[0]*R2)/(R1*R1 + R2*R2); + res[1] = I[1] - (V[1]*R1 - V[0]*R2)/(R1*R1 + R2*R2); return 0; } -int loadeq_res() +int copy_to_res_vector(std::vector &gen_res, std::vector &towrite, int &at) +{ + for(int i=0; i < towrite.size(); i++){ + gen_res[at] = towrite[i]; + at++; + } + return 0; +} + -std::vector diff_res(double t, std::vector &Y, std::vector &Yp) +int diff_res(double t, std::vector &Y, std::vector &Yp, generator_params params, std::vector &gen_res ) { - std::vector gen_res; - std::vector sub_Y = (Y.begin(), Y.begin() + 6); - std::vector sub_Yp = (Yp.begin(), Yp.begin() + 6); - std::vector Z = (Y.begin() + 32, Yp.begin() + 34); + int residual_index = 0; + + std::vector sub_Y(Y.begin(), Y.begin() + 6); + std::vector sub_Yp(Yp.begin(), Yp.begin() + 6); + std::vector Z(2); std::vector diff_Yp(6); - generator_params params; - diff_gen(sub_Y, &diff_Yp, Z, params, 1, 1); + Z[0] = Y[_I_d1]; + Z[1] = Y[_I_q1]; + + + double P_mech = 3.0; + double E_fd = 1.0; + + diff_gen(sub_Y, diff_Yp, Z, params, P_mech, E_fd); - for(int i=0; i < sub_Yp.size(); i++) + for(int i=0; i < diff_Yp.size(); i++) { - gen_res.push_back(sub_Yp[i] - Yp[i] ); + gen_res[i] = diff_Yp[i] - Yp[i]; + residual_index++; } + std::vector algeb_res(4); - std::vector V = {Y[34], Y[35]}; - std::vector I = {Y[6], Y[7]}; - get_algebraic_res(&algeb_res, sub_Y, Z, V, I, 1, 1); + std::vector V = {Y[_V_r1], Y[_V_i1]}; + std::vector I = {Y[_I_Gr1], Y[_I_Gi1]}; + double R_a = 2.0; - for(int i=0; i < algeb_res.size(); i++) - { - gen_res.push_back(algeb_res[i]); - } + get_algebraic_res(algeb_res, sub_Y, Z, V, I, params, R_a); + copy_to_res_vector(gen_res, algeb_res, residual_index); + + //Calculate residuals from bus equations and //append to the global residual vector gen_res - int buseq(std::vector &I, std::vector &r) std::vector bus_res(2); - std::vector I_bus_eqn = (Y.begin() + 6, Y.begin() + 14); - buseq(I_bus_eqn, &bus_res); - gen_res.insert(gen_res.end(), bus_res.begin(), bus_res.end()); + std::vector I_bus_eqn(Y.begin() + 6, Y.begin() + 14); + buseq(I_bus_eqn, bus_res); + copy_to_res_vector(gen_res, bus_res, residual_index); + - I_bus_eqn = (Y.begin() + 14, Y.begin() + 20); - buseq(I_bus_eqn, &bus_res); - gen_res.insert(gen_res.end(), bus_res.begin(), bus_res.end()); + I_bus_eqn.assign(Y.begin() + 14, Y.begin() + 20); + buseq(I_bus_eqn, bus_res); + copy_to_res_vector(gen_res, bus_res, residual_index); - I_bus_eqn = (Y.begin() + 20, Y.begin() + 26); - buseq(I_bus_eqn, &bus_res); - gen_res.insert(gen_res.end(), bus_res.begin(), bus_res.end()); + + I_bus_eqn.assign(Y.begin() + 20, Y.begin() + 26); + buseq(I_bus_eqn, bus_res); + copy_to_res_vector(gen_res, bus_res, residual_index); + + //Calculate residuals from branch equations and //append to the global residual vector gen_res std::vector branch_res(4); + double b = 1.0; + double g = 1.0; + double B = 2.0; + double G = 2.0; + + std::vector I_branch = {Y[_I_r21], Y[_I_i21], Y[_I_r12], Y[_I_i12]}; + std::vector V_branch(Y.begin()+26, Y.begin() + 30); + brancheq(branch_res, I_branch, V_branch, g, b, G, B); + copy_to_res_vector(gen_res, branch_res, residual_index); - std::vector I_branch = {Y[8], Y[9], Y[16], Y[17]}; - std::vector V_branch = {Y[26], Y[27], Y[28], Y[29]}; - brancheq(&branch_res, I_branch, V_branch, 1, 1, 1, 1); - gen_res.insert(gen_res.end(), branch_res.begin(), branch_res.end()); - I_branch = {Y[18], Y[19], Y[24], Y[25]}; - V_branch = {Y[28], Y[29], Y[30], Y[31]}; - brancheq(&branch_res, I_branch, V_branch, 1, 1, 1, 1); - gen_res.insert(gen_res.end(), branch_res.begin(), branch_res.end()); + I_branch = {Y[_I_r32], Y[_I_i32], Y[_I_r23], Y[_I_i23]}; + V_branch.assign(Y.begin() + 28, Y.begin() + 32); + brancheq(branch_res, I_branch, V_branch, g, b, G, B); + copy_to_res_vector(gen_res, branch_res, residual_index); - I_branch = {Y[10], Y[11], Y[22], Y[23]}; - V_branch = {Y[26], Y[27], Y[30], Y[31]}; - brancheq(&branch_res, I_branch, V_branch, 1, 1, 1, 1); - gen_res.insert(gen_res.end(), branch_res.begin(), branch_res.end()); + I_branch = {Y[_I_r31], Y[_I_i31], Y[_I_r13], Y[_I_i13]}; + V_branch = {Y[_V_r1], Y[_V_i1], Y[_V_r3], Y[_V_i3]}; + brancheq(branch_res, I_branch, V_branch, g, b, G, B); + copy_to_res_vector(gen_res, branch_res, residual_index); + //Calculate residuals from load equations and //append to the global residual vector gen_res - int loadeq(std::vector &I, std::vector &V, std::vector &res, double R1, double R2) - std::vector load_res(2); - std::vector I_load = {Y[12], Y[13]}; - std::vector V_load = {Y[26], Y[27]}; - loadeq(I_load, V_load, load_res, 1, 1); - gen_res.insert(gen_res.end(), load_res.begin(), load_res.end()); + double R1 = 1.0; + double R2 = 1.0; + + std::vector I_load = {Y[_I_Lr1], Y[_I_Li1]}; + std::vector V_load = {Y[_V_r1], Y[_V_i1]}; + loadeq(I_load, V_load, load_res, R1, R2); + copy_to_res_vector(gen_res, load_res, residual_index); + + + I_load = {Y[_I_Lr2], Y[_I_Li2]}; + V_load = {Y[_V_r2], Y[_V_i2]}; + loadeq(I_load, V_load, load_res, R1, R2); + copy_to_res_vector(gen_res, load_res, residual_index); + - return gen_res; + sub_Y.assign(Y.begin() + 34, Y.begin() + 40); + sub_Yp.assign(Yp.begin() + 34, Yp.begin() + 40); + Z[0] = Y[_I_d2]; + Z[1] = Y[_I_q2]; + + + P_mech = 3.0; + E_fd = 1.0; + + diff_gen(sub_Y, diff_Yp, Z, params, P_mech, E_fd); + + for(int i=0; i < diff_Yp.size(); i++) + { + gen_res[i] = diff_Yp[i] - Yp[i]; + residual_index++; + } + + + V = {Y[_V_r3], Y[_V_i3]}; + I = {Y[_I_Gr3], Y[_I_Gi3]}; + R_a = 2.0; + + get_algebraic_res(algeb_res, sub_Y, Z, V, I, params, R_a); + copy_to_res_vector(gen_res, algeb_res, residual_index); + + + + for(int i=0; i < gen_res.size(); i++) + { + std::cout << "index: " << i << " - value: " << gen_res[i] << std::endl; + } + + return 0; } -int get_params() + int main(int argc, char const *argv[]) @@ -276,5 +391,168 @@ int main(int argc, char const *argv[]) double B3 = 0.0; double G3 = 0.0; + generator_params params; + + params.w_0 = 1; + params.H = 1; + params.D = 1; + params.T_dop = 1; + params.T_dopp = 1; + params.T_qopp = 1; + params.T_qop = 1; + params.X_d1 = 1; + params.X_d2 = 1; + params.X_d3 = 1; + params.X_d4 = 1; + params.X_d5 = 1; + params.X_q1 = 1; + params.X_q2 = 1; + params.X_q3 = 1; + params.X_q4 = 1; + params.X_q5 = 1; + params.X_d = 1; + params.X_dp = 1; + params.X_dpp = 1; + params.X_q = 1; + params.X_qp = 1; + params.X_qpp = 1; + params.X_L = 1; + params.X_qd = 1; + params.R_a = 1; + params.S_A = 1; + params.S_B = 1; + + std::vector gen_res(42); + std::vector Y = {1, 1, 2, 2, 2, 1, 1, 2, -6, -5, 4, 1, -2, + -2, 0.5, 4.5, -1, 15, 0, -18, 1, 1, 2, 2, -3, -2, 2, 1, 1, 2, + 3, 4, 1 , 2, 1, 1, 2, 2, 2, 1, 1, 2}; + + std::vector Yp(42); + Yp[0]=1, Yp[1]=-5, Yp[2]=-65, Yp[3]=-1, Yp[4]=1, Yp[5]=-48; + Yp[34]=1, Yp[35]=-5, Yp[36]=-65, Yp[37]=-1, Yp[38]=1, Yp[39]=-48; + + diff_res(1, Y, Yp, params, gen_res ); + + + // vector Y = {1, 1, 2, 2, 2, 1}; + // vector Z = {1, 2}; + // vector Yp(6); + // double P_mech = 3.0; + // double E_fd = 1.0; + + // diff_gen(Y, Yp, Z, params, P_mech, E_fd); + + + // for(auto val: Yp) + // { + // std::cout << val << " "; + // } + + // std::cout << "\n--------------------Algebraics----------------------" << std::endl; + + // std::vector algeb_res(4); + // std::vector V = {2, 1}; + // std::vector I = {1, 2}; + // double R_a = 2; + // get_algebraic_res(algeb_res, Y, Z, V, I, params, R_a); + + // for(auto val: algeb_res) + // { + // std::cout << val << " "; + // } + + // std::cout << "\n--------------------Buses----------------------" << std::endl; + + // std::vector bus_res(2); + // I = {3, 4, -6, -5, 4, 1, -2, -2}; + // buseq(I, bus_res); + // for(auto val: bus_res) + // { + // std::cout << val << " "; + // } + // std::cout < branch_res(4); + // I = {-6, -5, -1, 15}; + // V = {2, 1, 1, 2}; + // brancheq(branch_res, I, V, g, b, B, G); + + // for(auto val: branch_res) + // { + // std::cout << val << " "; + // } + // std::cout < &I, std::vector &V, std::vector &res, double R1, double R2) + + // std::vector load_res(2); + // I = {-2, -2}; + // V = {2, 1}; + // double R1 = 1; + // double R2 = 1; + + // loadeq(I, V, load_res, R1, R2); + + // for(auto val: load_res) + // { + // std::cout << val << " "; + // } + // std::cout < Date: Wed, 12 Mar 2025 16:29:24 -0400 Subject: [PATCH 09/20] added residual function and testing --- examples/Bus3Phasor/bus3phasor.cpp | 947 ++++++++++++++--------------- 1 file changed, 467 insertions(+), 480 deletions(-) diff --git a/examples/Bus3Phasor/bus3phasor.cpp b/examples/Bus3Phasor/bus3phasor.cpp index 731a4e5b..5394796d 100644 --- a/examples/Bus3Phasor/bus3phasor.cpp +++ b/examples/Bus3Phasor/bus3phasor.cpp @@ -1,187 +1,197 @@ -#include -#include #include -#include #include -#include +#include +#include +#include +#include using namespace std; -//#include -//#include -#define _DEL1 0 -#define _W1 1 -#define _E_qp1 2 +#define _DEL1 0 +#define _W1 1 +#define _E_qp1 2 #define _PSI_dp1 3 #define _PSI_qp1 4 -#define _E_dp1 5 -#define _I_Gr1 6 -#define _I_Gi1 7 -#define _I_r21 8 -#define _I_i21 9 -#define _I_r31 10 -#define _I_i31 11 -#define _I_Lr1 12 -#define _I_Li1 13 -#define _I_Lr2 14 -#define _I_Li2 15 -#define _I_r12 16 -#define _I_i12 17 -#define _I_r32 18 -#define _I_i32 19 -#define _I_Gr3 20 -#define _I_Gi3 21 -#define _I_r13 22 -#define _I_i13 23 -#define _I_r23 24 -#define _I_i23 25 -#define _V_r1 26 -#define _V_i1 27 -#define _V_r2 28 -#define _V_i2 29 -#define _V_r3 30 -#define _V_i3 31 -#define _I_d1 32 -#define _I_q1 33 -#define _DEL2 34 -#define _W2 35 -#define _E_qp2 36 +#define _E_dp1 5 +#define _I_Gr1 6 +#define _I_Gi1 7 +#define _I_r21 8 +#define _I_i21 9 +#define _I_r31 10 +#define _I_i31 11 +#define _I_Lr1 12 +#define _I_Li1 13 +#define _I_Lr2 14 +#define _I_Li2 15 +#define _I_r12 16 +#define _I_i12 17 +#define _I_r32 18 +#define _I_i32 19 +#define _I_Gr3 20 +#define _I_Gi3 21 +#define _I_r13 22 +#define _I_i13 23 +#define _I_r23 24 +#define _I_i23 25 +#define _V_r1 26 +#define _V_i1 27 +#define _V_r2 28 +#define _V_i2 29 +#define _V_r3 30 +#define _V_i3 31 +#define _I_d1 32 +#define _I_q1 33 +#define _DEL2 34 +#define _W2 35 +#define _E_qp2 36 #define _PSI_dp2 37 #define _PSI_qp2 38 -#define _E_dp2 39 -#define _I_d2 40 -#define _I_q2 41 - - -struct generator_params -{ - double w_0; - double H; - double D; - double T_dop; - double T_dopp; - double T_qopp; - double T_qop; - double X_d1; - double X_d2; - double X_d3; - double X_d4; - double X_d5; - double X_q1; - double X_q2; - double X_q3; - double X_q4; - double X_q5; - double X_d; - double X_dp; - double X_dpp; - double X_q; - double X_qp; - double X_qpp; - double X_L; - double X_qd; - double R_a; - double S_A; - double S_B; +#define _E_dp2 39 +#define _I_d2 40 +#define _I_q2 41 + +struct generator_params { + double w_0; + double H; + double D; + double T_dop; + double T_dopp; + double T_qopp; + double T_qop; + double X_d1; + double X_d2; + double X_d3; + double X_d4; + double X_d5; + double X_q1; + double X_q2; + double X_q3; + double X_q4; + double X_q5; + double X_d; + double X_dp; + double X_dpp; + double X_q; + double X_qp; + double X_qpp; + double X_L; + double X_qd; + double R_a; + double S_A; + double S_B; }; -int get_psi_qpp(double &psi_qpp,double psi_qp, double X_q4, double E_dp, double X_q5) -{ - psi_qpp = -psi_qp*X_q4 - E_dp*X_q5; - return 0; +/** + * @brief function for calculating the helper variables + */ +int get_psi_qpp(double &psi_qpp, double psi_qp, double X_q4, double E_dp, + double X_q5) { + psi_qpp = -psi_qp * X_q4 - E_dp * X_q5; + return 0; } -int get_psi_dpp(double &psi_dpp, double psi_dp, double X_d4, double E_qp, double X_d5) -{ - psi_dpp = psi_dp*X_d4 + E_qp*X_d5; - return 0; +/** + * @brief function for calculating the helper variables + */ +int get_psi_dpp(double &psi_dpp, double psi_dp, double X_d4, double E_qp, + double X_d5) { + psi_dpp = psi_dp * X_d4 + E_qp * X_d5; + return 0; } - -int diff_gen(std::vector Y, std::vector &Yp, std::vector Z, generator_params P, double P_mech, double E_fd) -{ - double delta = Y[_DEL1]; - double w = Y[_W1]; - double E_qp = Y[_E_qp1]; - double psi_dp = Y[_PSI_dp1]; - double psi_qp = Y[_PSI_qp1]; - double E_dp = Y[_E_dp1]; - - double I_d = Z[0]; - double I_q = Z[1]; - - double psi_qpp; - double psi_dpp; - double psi_pp; - double k_sat; - double T_elec; - - get_psi_qpp(psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); - get_psi_dpp(psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); - psi_pp = sqrt(psi_qpp*psi_qpp + psi_dpp*psi_dpp); - k_sat = P.S_B*(psi_pp - P.S_A)*(psi_pp - P.S_A); - T_elec = (psi_dpp - I_d*P.X_dpp)*I_q - (psi_qpp - I_q*P.X_dpp)*I_d; - - Yp[0] = w*P.w_0; - Yp[1] = (1/(2*P.H))*((P_mech - P.D*w)/(1+w) - T_elec); - Yp[2] = (1/P.T_dop)*(E_fd - (E_qp + P.X_d1*(I_d + P.X_d3*(E_qp - psi_dp - P.X_d2*I_d)) + psi_dpp*k_sat)); - Yp[3] = (1/P.T_dopp)*(E_qp - psi_dp - P.X_d2*I_d); - Yp[4] = (1/P.T_qopp)*(E_dp - psi_qp + P.X_q2*I_q); - Yp[5] = (1/P.T_qop)*(-E_dp + P.X_qd*psi_qpp*k_sat + P.X_q1*(I_q - P.X_q3*(E_dp + I_q*P.X_q2 - psi_qp))); - - return 0; - +int diff_gen(std::vector Y, std::vector &Yp, + std::vector Z, generator_params P, double P_mech, + double E_fd) { + double delta = Y[_DEL1]; + double w = Y[_W1]; + double E_qp = Y[_E_qp1]; + double psi_dp = Y[_PSI_dp1]; + double psi_qp = Y[_PSI_qp1]; + double E_dp = Y[_E_dp1]; + + double I_d = Z[0]; + double I_q = Z[1]; + + double psi_qpp; + double psi_dpp; + double psi_pp; + double k_sat; + double T_elec; + + get_psi_qpp(psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); + get_psi_dpp(psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); + psi_pp = sqrt(psi_qpp * psi_qpp + psi_dpp * psi_dpp); + k_sat = P.S_B * (psi_pp - P.S_A) * (psi_pp - P.S_A); + T_elec = (psi_dpp - I_d * P.X_dpp) * I_q - (psi_qpp - I_q * P.X_dpp) * I_d; + + Yp[0] = w * P.w_0; + Yp[1] = (1 / (2 * P.H)) * ((P_mech - P.D * w) / (1 + w) - T_elec); + Yp[2] = + (1 / P.T_dop) * + (E_fd - (E_qp + P.X_d1 * (I_d + P.X_d3 * (E_qp - psi_dp - P.X_d2 * I_d)) + + psi_dpp * k_sat)); + Yp[3] = (1 / P.T_dopp) * (E_qp - psi_dp - P.X_d2 * I_d); + Yp[4] = (1 / P.T_qopp) * (E_dp - psi_qp + P.X_q2 * I_q); + Yp[5] = (1 / P.T_qop) * + (-E_dp + P.X_qd * psi_qpp * k_sat + + P.X_q1 * (I_q - P.X_q3 * (E_dp + I_q * P.X_q2 - psi_qp))); + + return 0; } -int get_algebraic_res(std::vector &res, std::vector Y, std::vector Z, std::vector V, std::vector I, generator_params P, double R_a) -{ - double delta = Y[_DEL1]; - double w = Y[_W1]; - double E_qp = Y[_E_qp1]; - double psi_dp = Y[_PSI_dp1]; - double psi_qp = Y[_PSI_qp1]; - double E_dp = Y[_E_dp1]; +int get_algebraic_res(std::vector &res, std::vector Y, + std::vector Z, std::vector V, + std::vector I, generator_params P, double R_a) { + double delta = Y[_DEL1]; + double w = Y[_W1]; + double E_qp = Y[_E_qp1]; + double psi_dp = Y[_PSI_dp1]; + double psi_qp = Y[_PSI_qp1]; + double E_dp = Y[_E_dp1]; - double I_d = Z[0]; - double I_q = Z[1]; + double I_d = Z[0]; + double I_q = Z[1]; - double I_r = I[0]; - double I_i = I[1]; + double I_r = I[0]; + double I_i = I[1]; - double V_r = V[0]; - double V_i = V[1]; + double V_r = V[0]; + double V_i = V[1]; - double psi_qpp; - double psi_dpp; + double psi_qpp; + double psi_dpp; - get_psi_qpp(psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); - get_psi_dpp(psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); + get_psi_qpp(psi_qpp, psi_qp, P.X_q4, E_dp, P.X_q5); + get_psi_dpp(psi_dpp, psi_dp, P.X_d4, E_qp, P.X_d5); - res[0] = I_d - I_r*sin(delta) + I_i*cos(delta); - res[1] = I_q - I_r*cos(delta) - I_i*sin(delta); - res[2] = -psi_qpp*(1 + w) - V_r*sin(delta) + V_i*cos(delta) - I_d*R_a + I_q*P.X_qpp; - res[3] = psi_dpp*(1 + w) - V_r*cos(delta) - V_i*sin(delta) - I_d*P.X_qpp - I_q*R_a; + res[0] = I_d - I_r * sin(delta) + I_i * cos(delta); + res[1] = I_q - I_r * cos(delta) - I_i * sin(delta); + res[2] = -psi_qpp * (1 + w) - V_r * sin(delta) + V_i * cos(delta) - + I_d * R_a + I_q * P.X_qpp; + res[3] = psi_dpp * (1 + w) - V_r * cos(delta) - V_i * sin(delta) - + I_d * P.X_qpp - I_q * R_a; - return 0; + return 0; } - -int buseq(std::vector &I, std::vector &r) -{ - r[0] = 0.0; - r[1] = 0.0; - - for (size_t i = 0; i < I.size(); i++) - { - if(i%2 == 0){ - r[0] += I[i]; - } - else{ - r[1] += I[i]; - } +/** + * @brief Takes a vector of currents I and computes the residuals using + * kirchhoff's law + * @param I contains the currents flowing to and from the Bus + * @param r residual output + */ +int buseq(std::vector &I, std::vector &r) { + r[0] = 0.0; + r[1] = 0.0; + + for (size_t i = 0; i < I.size(); i++) { + if (i % 2 == 0) { + r[0] += I[i]; + } else { + r[1] += I[i]; } + } - return 0; - + return 0; } /** @@ -190,369 +200,346 @@ int buseq(std::vector &I, std::vector &r) * I[1]: I_i1 * I[2]: I_r2 * I[3]: I_i2 - * + * * V[0]: V_r1 * V[1]: V_i1 * V[2]: V_r2 * V[3]: V_i2 */ -int brancheq(std::vector &res, std::vector &I, std::vector &V, double g, double b, double G, double B) -{ - res[0] = -I[0] -(g + G/2)*V[0] + (b + B/2)*V[1] + g*V[2] - b*V[3]; - res[1] = -I[1] -(b + B/2)*V[0] - (g + G/2)*V[1] + b*V[2] + g*V[3]; - res[2] = -I[2] + g*V[0] - b*V[1] - (g + G/2)*V[2] + (b + B/2)*V[3]; - res[3] = -I[3] + b*V[0] + g*V[1] - (b + B/2)*V[2] - (g + G/2)*V[3]; - - return 0; +int brancheq(std::vector &res, std::vector &I, + std::vector &V, double g, double b, double G, double B) { + res[0] = + -I[0] - (g + G / 2) * V[0] + (b + B / 2) * V[1] + g * V[2] - b * V[3]; + res[1] = + -I[1] - (b + B / 2) * V[0] - (g + G / 2) * V[1] + b * V[2] + g * V[3]; + res[2] = + -I[2] + g * V[0] - b * V[1] - (g + G / 2) * V[2] + (b + B / 2) * V[3]; + res[3] = + -I[3] + b * V[0] + g * V[1] - (b + B / 2) * V[2] - (g + G / 2) * V[3]; + + return 0; } /** - * @brief calculates the current for the load given the voltages and Resistances + * @brief calculates the current for the load given the voltages and Resistances * I[0]: I_r1 * I[1]: I_i1 - * + * * V[0]: V_r1 * V[1]: V_i1 */ -int loadeq(std::vector &I, std::vector &V, std::vector &res, double R1, double R2) -{ - res[0] = I[0] - (V[0]*R1 + V[1]*R2)/(R1*R1 + R2*R2); - res[1] = I[1] - (V[1]*R1 - V[0]*R2)/(R1*R1 + R2*R2); +int loadeq(std::vector &I, std::vector &V, + std::vector &res, double R1, double R2) { + res[0] = I[0] - (V[0] * R1 + V[1] * R2) / (R1 * R1 + R2 * R2); + res[1] = I[1] - (V[1] * R1 - V[0] * R2) / (R1 * R1 + R2 * R2); - return 0; + return 0; } -int copy_to_res_vector(std::vector &gen_res, std::vector &towrite, int &at) -{ - for(int i=0; i < towrite.size(); i++){ - gen_res[at] = towrite[i]; - at++; - } - return 0; +int copy_to_res_vector(std::vector &gen_res, + std::vector &towrite, int &at) { + for (int i = 0; i < towrite.size(); i++) { + gen_res[at] = towrite[i]; + at++; + } + return 0; } +int diff_res(double t, std::vector &Y, std::vector &Yp, + generator_params params, std::vector &gen_res) { + + int residual_index = 0; -int diff_res(double t, std::vector &Y, std::vector &Yp, generator_params params, std::vector &gen_res ) -{ + std::vector sub_Y(Y.begin(), Y.begin() + 6); + std::vector sub_Yp(Yp.begin(), Yp.begin() + 6); + std::vector Z(2); + std::vector diff_Yp(6); + Z[0] = Y[_I_d1]; + Z[1] = Y[_I_q1]; - int residual_index = 0; + double P_mech = 3.0; + double E_fd = 1.0; - std::vector sub_Y(Y.begin(), Y.begin() + 6); - std::vector sub_Yp(Yp.begin(), Yp.begin() + 6); - std::vector Z(2); - std::vector diff_Yp(6); - Z[0] = Y[_I_d1]; - Z[1] = Y[_I_q1]; + diff_gen(sub_Y, diff_Yp, Z, params, P_mech, E_fd); - - double P_mech = 3.0; - double E_fd = 1.0; - - diff_gen(sub_Y, diff_Yp, Z, params, P_mech, E_fd); + for (int i = 0; i < diff_Yp.size(); i++) { + gen_res[i] = diff_Yp[i] - Yp[i]; + residual_index++; + } - for(int i=0; i < diff_Yp.size(); i++) - { - gen_res[i] = diff_Yp[i] - Yp[i]; - residual_index++; - } - - - std::vector algeb_res(4); - std::vector V = {Y[_V_r1], Y[_V_i1]}; - std::vector I = {Y[_I_Gr1], Y[_I_Gi1]}; - double R_a = 2.0; - - get_algebraic_res(algeb_res, sub_Y, Z, V, I, params, R_a); - copy_to_res_vector(gen_res, algeb_res, residual_index); - - - - //Calculate residuals from bus equations and - //append to the global residual vector gen_res + std::vector algeb_res(4); + std::vector V = {Y[_V_r1], Y[_V_i1]}; + std::vector I = {Y[_I_Gr1], Y[_I_Gi1]}; + double R_a = 2.0; - std::vector bus_res(2); + get_algebraic_res(algeb_res, sub_Y, Z, V, I, params, R_a); + copy_to_res_vector(gen_res, algeb_res, residual_index); - std::vector I_bus_eqn(Y.begin() + 6, Y.begin() + 14); - buseq(I_bus_eqn, bus_res); - copy_to_res_vector(gen_res, bus_res, residual_index); + // Calculate residuals from bus equations and + // append to the global residual vector gen_res + std::vector bus_res(2); - I_bus_eqn.assign(Y.begin() + 14, Y.begin() + 20); - buseq(I_bus_eqn, bus_res); - copy_to_res_vector(gen_res, bus_res, residual_index); + std::vector I_bus_eqn(Y.begin() + 6, Y.begin() + 14); + buseq(I_bus_eqn, bus_res); + copy_to_res_vector(gen_res, bus_res, residual_index); + I_bus_eqn.assign(Y.begin() + 14, Y.begin() + 20); + buseq(I_bus_eqn, bus_res); + copy_to_res_vector(gen_res, bus_res, residual_index); - I_bus_eqn.assign(Y.begin() + 20, Y.begin() + 26); - buseq(I_bus_eqn, bus_res); - copy_to_res_vector(gen_res, bus_res, residual_index); + I_bus_eqn.assign(Y.begin() + 20, Y.begin() + 26); + buseq(I_bus_eqn, bus_res); + copy_to_res_vector(gen_res, bus_res, residual_index); - + // Calculate residuals from branch equations and + // append to the global residual vector gen_res + std::vector branch_res(4); + double b = 1.0; + double g = 1.0; + double B = 2.0; + double G = 2.0; - //Calculate residuals from branch equations and - //append to the global residual vector gen_res - std::vector branch_res(4); - double b = 1.0; - double g = 1.0; - double B = 2.0; - double G = 2.0; + std::vector I_branch = {Y[_I_r21], Y[_I_i21], Y[_I_r12], Y[_I_i12]}; + std::vector V_branch(Y.begin() + 26, Y.begin() + 30); + brancheq(branch_res, I_branch, V_branch, g, b, G, B); + copy_to_res_vector(gen_res, branch_res, residual_index); - std::vector I_branch = {Y[_I_r21], Y[_I_i21], Y[_I_r12], Y[_I_i12]}; - std::vector V_branch(Y.begin()+26, Y.begin() + 30); - brancheq(branch_res, I_branch, V_branch, g, b, G, B); - copy_to_res_vector(gen_res, branch_res, residual_index); + I_branch = {Y[_I_r32], Y[_I_i32], Y[_I_r23], Y[_I_i23]}; + V_branch.assign(Y.begin() + 28, Y.begin() + 32); + brancheq(branch_res, I_branch, V_branch, g, b, G, B); + copy_to_res_vector(gen_res, branch_res, residual_index); - - I_branch = {Y[_I_r32], Y[_I_i32], Y[_I_r23], Y[_I_i23]}; - V_branch.assign(Y.begin() + 28, Y.begin() + 32); - brancheq(branch_res, I_branch, V_branch, g, b, G, B); - copy_to_res_vector(gen_res, branch_res, residual_index); + I_branch = {Y[_I_r31], Y[_I_i31], Y[_I_r13], Y[_I_i13]}; + V_branch = {Y[_V_r1], Y[_V_i1], Y[_V_r3], Y[_V_i3]}; + brancheq(branch_res, I_branch, V_branch, g, b, G, B); + copy_to_res_vector(gen_res, branch_res, residual_index); - - I_branch = {Y[_I_r31], Y[_I_i31], Y[_I_r13], Y[_I_i13]}; - V_branch = {Y[_V_r1], Y[_V_i1], Y[_V_r3], Y[_V_i3]}; - brancheq(branch_res, I_branch, V_branch, g, b, G, B); - copy_to_res_vector(gen_res, branch_res, residual_index); + // Calculate residuals from load equations and + // append to the global residual vector gen_res + std::vector load_res(2); + double R1 = 1.0; + double R2 = 1.0; - - //Calculate residuals from load equations and - //append to the global residual vector gen_res - std::vector load_res(2); - double R1 = 1.0; - double R2 = 1.0; + std::vector I_load = {Y[_I_Lr1], Y[_I_Li1]}; + std::vector V_load = {Y[_V_r1], Y[_V_i1]}; + loadeq(I_load, V_load, load_res, R1, R2); + copy_to_res_vector(gen_res, load_res, residual_index); - std::vector I_load = {Y[_I_Lr1], Y[_I_Li1]}; - std::vector V_load = {Y[_V_r1], Y[_V_i1]}; - loadeq(I_load, V_load, load_res, R1, R2); - copy_to_res_vector(gen_res, load_res, residual_index); + I_load = {Y[_I_Lr2], Y[_I_Li2]}; + V_load = {Y[_V_r2], Y[_V_i2]}; + loadeq(I_load, V_load, load_res, R1, R2); + copy_to_res_vector(gen_res, load_res, residual_index); + sub_Y.assign(Y.begin() + 34, Y.begin() + 40); + sub_Yp.assign(Yp.begin() + 34, Yp.begin() + 40); + Z[0] = Y[_I_d2]; + Z[1] = Y[_I_q2]; - I_load = {Y[_I_Lr2], Y[_I_Li2]}; - V_load = {Y[_V_r2], Y[_V_i2]}; - loadeq(I_load, V_load, load_res, R1, R2); - copy_to_res_vector(gen_res, load_res, residual_index); - - - sub_Y.assign(Y.begin() + 34, Y.begin() + 40); - sub_Yp.assign(Yp.begin() + 34, Yp.begin() + 40); - Z[0] = Y[_I_d2]; - Z[1] = Y[_I_q2]; - - - P_mech = 3.0; - E_fd = 1.0; - - diff_gen(sub_Y, diff_Yp, Z, params, P_mech, E_fd); - - for(int i=0; i < diff_Yp.size(); i++) - { - gen_res[i] = diff_Yp[i] - Yp[i]; - residual_index++; - } + P_mech = 3.0; + E_fd = 1.0; + diff_gen(sub_Y, diff_Yp, Z, params, P_mech, E_fd); - V = {Y[_V_r3], Y[_V_i3]}; - I = {Y[_I_Gr3], Y[_I_Gi3]}; - R_a = 2.0; - - get_algebraic_res(algeb_res, sub_Y, Z, V, I, params, R_a); - copy_to_res_vector(gen_res, algeb_res, residual_index); - - - - for(int i=0; i < gen_res.size(); i++) - { - std::cout << "index: " << i << " - value: " << gen_res[i] << std::endl; - } - - return 0; - + for (int i = 0; i < diff_Yp.size(); i++) { + gen_res[i] = diff_Yp[i] - Yp[i]; + residual_index++; + } + + V = {Y[_V_r3], Y[_V_i3]}; + I = {Y[_I_Gr3], Y[_I_Gi3]}; + R_a = 2.0; + + get_algebraic_res(algeb_res, sub_Y, Z, V, I, params, R_a); + copy_to_res_vector(gen_res, algeb_res, residual_index); + + return 0; } - - - -int main(int argc, char const *argv[]) -{ - //These are for branch 1 - double b1 = 0.0; - double g1 = 0.0; - double B1 = 0.0; - double G1 = 0.0; - - //These are for branch 2 - double b2 = 0.0; - double g2 = 0.0; - double B2 = 0.0; - double G2 = 0.0; - - //These are for branch 3 - double b3 = 0.0; - double g3 = 0.0; - double B3 = 0.0; - double G3 = 0.0; - - generator_params params; - - params.w_0 = 1; - params.H = 1; - params.D = 1; - params.T_dop = 1; - params.T_dopp = 1; - params.T_qopp = 1; - params.T_qop = 1; - params.X_d1 = 1; - params.X_d2 = 1; - params.X_d3 = 1; - params.X_d4 = 1; - params.X_d5 = 1; - params.X_q1 = 1; - params.X_q2 = 1; - params.X_q3 = 1; - params.X_q4 = 1; - params.X_q5 = 1; - params.X_d = 1; - params.X_dp = 1; - params.X_dpp = 1; - params.X_q = 1; - params.X_qp = 1; - params.X_qpp = 1; - params.X_L = 1; - params.X_qd = 1; - params.R_a = 1; - params.S_A = 1; - params.S_B = 1; - - std::vector gen_res(42); - std::vector Y = {1, 1, 2, 2, 2, 1, 1, 2, -6, -5, 4, 1, -2, - -2, 0.5, 4.5, -1, 15, 0, -18, 1, 1, 2, 2, -3, -2, 2, 1, 1, 2, - 3, 4, 1 , 2, 1, 1, 2, 2, 2, 1, 1, 2}; - - std::vector Yp(42); - Yp[0]=1, Yp[1]=-5, Yp[2]=-65, Yp[3]=-1, Yp[4]=1, Yp[5]=-48; - Yp[34]=1, Yp[35]=-5, Yp[36]=-65, Yp[37]=-1, Yp[38]=1, Yp[39]=-48; - - diff_res(1, Y, Yp, params, gen_res ); - - - // vector Y = {1, 1, 2, 2, 2, 1}; - // vector Z = {1, 2}; - // vector Yp(6); - // double P_mech = 3.0; - // double E_fd = 1.0; - - // diff_gen(Y, Yp, Z, params, P_mech, E_fd); - - - // for(auto val: Yp) - // { - // std::cout << val << " "; - // } - - // std::cout << "\n--------------------Algebraics----------------------" << std::endl; - - // std::vector algeb_res(4); - // std::vector V = {2, 1}; - // std::vector I = {1, 2}; - // double R_a = 2; - // get_algebraic_res(algeb_res, Y, Z, V, I, params, R_a); - - // for(auto val: algeb_res) - // { - // std::cout << val << " "; - // } - - // std::cout << "\n--------------------Buses----------------------" << std::endl; - - // std::vector bus_res(2); - // I = {3, 4, -6, -5, 4, 1, -2, -2}; - // buseq(I, bus_res); - // for(auto val: bus_res) - // { - // std::cout << val << " "; - // } - // std::cout < branch_res(4); - // I = {-6, -5, -1, 15}; - // V = {2, 1, 1, 2}; - // brancheq(branch_res, I, V, g, b, B, G); - - // for(auto val: branch_res) - // { - // std::cout << val << " "; - // } - // std::cout < &I, std::vector &V, std::vector &res, double R1, double R2) - - // std::vector load_res(2); - // I = {-2, -2}; - // V = {2, 1}; - // double R1 = 1; - // double R2 = 1; - - // loadeq(I, V, load_res, R1, R2); - - // for(auto val: load_res) - // { - // std::cout << val << " "; - // } - // std::cout < gen_res(42); + std::vector Y = {1, 1, 2, 2, 2, 1, 1, 2, -6, -5, 4, + 1, -2, -2, 0.5, 4.5, -1, 15, 0, -18, 1, 1, + 2, 2, -3, -2, 2, 1, 1, 2, 3, 4, 1, + 2, 1, 1, 2, 2, 2, 1, 1, 2}; + + std::vector Yp(42); + Yp[0] = 1, Yp[1] = -5, Yp[2] = -65, Yp[3] = -1, Yp[4] = 1, Yp[5] = -48; + Yp[34] = 1, Yp[35] = -5, Yp[36] = -65, Yp[37] = -1, Yp[38] = 1, Yp[39] = -48; + + diff_res(1, Y, Yp, params, gen_res); + + for (int i = 0; i < gen_res.size(); i++) { + std::cout << "index: " << i << " - value: " << gen_res[i] << std::endl; + } + + // vector Y = {1, 1, 2, 2, 2, 1}; + // vector Z = {1, 2}; + // vector Yp(6); + // double P_mech = 3.0; + // double E_fd = 1.0; + + // diff_gen(Y, Yp, Z, params, P_mech, E_fd); + + // for(auto val: Yp) + // { + // std::cout << val << " "; + // } + + // std::cout << "\n--------------------Algebraics----------------------" << + // std::endl; + + // std::vector algeb_res(4); + // std::vector V = {2, 1}; + // std::vector I = {1, 2}; + // double R_a = 2; + // get_algebraic_res(algeb_res, Y, Z, V, I, params, R_a); + + // for(auto val: algeb_res) + // { + // std::cout << val << " "; + // } + + // std::cout << "\n--------------------Buses----------------------" << + // std::endl; + + // std::vector bus_res(2); + // I = {3, 4, -6, -5, 4, 1, -2, -2}; + // buseq(I, bus_res); + // for(auto val: bus_res) + // { + // std::cout << val << " "; + // } + // std::cout < branch_res(4); + // I = {-6, -5, -1, 15}; + // V = {2, 1, 1, 2}; + // brancheq(branch_res, I, V, g, b, B, G); + + // for(auto val: branch_res) + // { + // std::cout << val << " "; + // } + // std::cout < &I, std::vector &V, + // std::vector &res, double R1, double R2) + + // std::vector load_res(2); + // I = {-2, -2}; + // V = {2, 1}; + // double R1 = 1; + // double R2 = 1; + + // loadeq(I, V, load_res, R1, R2); + + // for(auto val: load_res) + // { + // std::cout << val << " "; + // } + // std::cout < Date: Tue, 11 Mar 2025 11:38:57 -0500 Subject: [PATCH 10/20] Initial working version of example1 --- examples/CMakeLists.txt | 2 + examples/PhasorDynamics/CMakeLists.txt | 1 + .../PhasorDynamics/Example1/CMakeLists.txt | 9 + examples/PhasorDynamics/Example1/example1.cpp | 60 ++++ examples/PhasorDynamics/Example1/example1.hpp | 30 ++ .../PhasorDynamics/BusFault/BusFault.cpp | 1 + .../PhasorDynamics/BusFault/BusFault.hpp | 113 +++++++ .../PhasorDynamics/BusFault/CMakeLists.txt | 5 + .../SynchronousMachine/CMakeLists.txt | 2 + .../GENROUwS/CMakeLists.txt | 5 + .../SynchronousMachine/GENROUwS/GENROU.cpp | 1 + .../SynchronousMachine/GENROUwS/GENROU.hpp | 317 ++++++++++++++++++ src/Solver/Dynamic/Ida.cpp | 49 ++- src/Solver/Dynamic/Ida.hpp | 15 +- 14 files changed, 608 insertions(+), 2 deletions(-) create mode 100644 examples/PhasorDynamics/CMakeLists.txt create mode 100644 examples/PhasorDynamics/Example1/CMakeLists.txt create mode 100644 examples/PhasorDynamics/Example1/example1.cpp create mode 100644 examples/PhasorDynamics/Example1/example1.hpp create mode 100644 src/Model/PhasorDynamics/BusFault/BusFault.cpp create mode 100644 src/Model/PhasorDynamics/BusFault/BusFault.hpp create mode 100644 src/Model/PhasorDynamics/BusFault/CMakeLists.txt create mode 100644 src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/CMakeLists.txt create mode 100644 src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp create mode 100644 src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 85096b79..6c867ea0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -24,3 +24,5 @@ endif() if(GRIDKIT_ENABLE_ENZYME) add_subdirectory(Enzyme) endif() + +add_subdirectory(PhasorDynamics) \ No newline at end of file diff --git a/examples/PhasorDynamics/CMakeLists.txt b/examples/PhasorDynamics/CMakeLists.txt new file mode 100644 index 00000000..5d441798 --- /dev/null +++ b/examples/PhasorDynamics/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Example1) \ No newline at end of file diff --git a/examples/PhasorDynamics/Example1/CMakeLists.txt b/examples/PhasorDynamics/Example1/CMakeLists.txt new file mode 100644 index 00000000..78d3b018 --- /dev/null +++ b/examples/PhasorDynamics/Example1/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(phasordynamics_example1 example1.cpp) +target_link_libraries(phasordynamics_example1 + GRIDKIT::bus + SUNDIALS::sunlinsolklu + SUNDIALS::core + SUNDIALS::ida + SUNDIALS::idas + SUNDIALS::sunmatrixdense) +install(TARGETS phasordynamics_example1 RUNTIME DESTINATION bin) diff --git a/examples/PhasorDynamics/Example1/example1.cpp b/examples/PhasorDynamics/Example1/example1.cpp new file mode 100644 index 00000000..976f03f3 --- /dev/null +++ b/examples/PhasorDynamics/Example1/example1.cpp @@ -0,0 +1,60 @@ +#include "example1.hpp" + +#define _CRT_SECURE_NO_WARNINGS + +using namespace GridKit::PhasorDynamics; +using namespace AnalysisManager::Sundials; + +int main() +{ + printf("Example 1 version 2\n"); + + /* Create model parts */ + SystemModel sys; + Bus bus1(0.9949877346411762, 0.09999703952427966); + BusInfinite bus2(1.0, 0.0); + Branch branch(&bus1, &bus2, 0, 0.1, 0, 0); + BusFault fault(&bus1, 0, 1e-3, 0); + GENROU gen(&bus1, 1, 1, 0.05013, 3, 0, 0, 7, .04, .05, .75, 2.1, 0.2, 0.18, + 0.5, 0.5, 0.18, 0.15, 0, 0); + + /* Connect everything together */ + sys.addBus(&bus1); + sys.addBus(&bus2); + sys.addComponent(&branch); + sys.addComponent(&fault); + sys.addComponent(&gen); + sys.allocate(); + + double dt = 1.0/4.0/60.0; + + /* Output file header */ + FILE *f = fopen("example1_v2_results.csv", "w"); + if (!f) printf("ERROR writing to output file!\n"); + fprintf(f, "%s,%s", "t", "IDA Return Value"); + for (int i = 0; i < sys.size(); ++i) fprintf(f, ",Y[%d]", i); + for (int i = 0; i < sys.size(); ++i) fprintf(f, ",Yp[%d]", i); + fprintf(f, "\n"); + + /* Set up simulation */ + Ida ida(&sys); + ida.configureSimulation(); + + /* Run simulation */ + double start = (double) clock(); + ida.printOutputF(0, 0, f); + ida.initializeSimulation(0, false); + ida.runSimulationFixed(0, dt, 1, f); + fault.setStatus(1); + ida.initializeSimulation(1, false); + ida.runSimulationFixed(1, dt, 1.1, f); + fault.setStatus(0); + ida.initializeSimulation(1.1, false); + ida.runSimulationFixed(1.1, dt, 30, f); + + printf("Complete in %.4g seconds\n", (clock() - start) / CLOCKS_PER_SEC); + fclose(f); + + return 0; + +} \ No newline at end of file diff --git a/examples/PhasorDynamics/Example1/example1.hpp b/examples/PhasorDynamics/Example1/example1.hpp new file mode 100644 index 00000000..836c729f --- /dev/null +++ b/examples/PhasorDynamics/Example1/example1.hpp @@ -0,0 +1,30 @@ +#include +#define _USE_MATH_DEFINES +#include +#include + +//#include +#include +#include +#include +#include +#include + +#include "Model/PhasorDynamics/SystemModel.hpp" +#include "Model/PhasorDynamics/Bus/Bus.hpp" +#include "Model/PhasorDynamics/Bus/BusInfinite.hpp" +#include "Model/PhasorDynamics/Branch/Branch.hpp" +#include "Model/PhasorDynamics/BusFault/BusFault.hpp" +#include "Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp" + + +#include "Solver/Dynamic/Ida.hpp" + +#include "Model/PhasorDynamics/Bus/Bus.cpp" +#include "Model/PhasorDynamics/Bus/BusInfinite.cpp" +#include "Model/PhasorDynamics/Branch/Branch.cpp" +#include "Solver/Dynamic/Ida.cpp" + + + +int main(); diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.cpp b/src/Model/PhasorDynamics/BusFault/BusFault.cpp new file mode 100644 index 00000000..42b70b45 --- /dev/null +++ b/src/Model/PhasorDynamics/BusFault/BusFault.cpp @@ -0,0 +1 @@ +#include "BusFault.hpp" \ No newline at end of file diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.hpp b/src/Model/PhasorDynamics/BusFault/BusFault.hpp new file mode 100644 index 00000000..9ee2cc98 --- /dev/null +++ b/src/Model/PhasorDynamics/BusFault/BusFault.hpp @@ -0,0 +1,113 @@ +/* Bus Fault Component - Adam Birchfield */ +#pragma once + +#include +#include + +namespace GridKit +{ +namespace PhasorDynamics +{ + using ComponentT = Component; + using BaseBusT = BusBase; + + class BusFault : public ComponentT + { + using ComponentT::size_; + using ComponentT::nnz_; + using ComponentT::time_; + using ComponentT::alpha_; + using ComponentT::y_; + using ComponentT::yp_; + using ComponentT::tag_; + using ComponentT::f_; + using ComponentT::g_; + using ComponentT::yB_; + using ComponentT::ypB_; + using ComponentT::fB_; + using ComponentT::gB_; + using ComponentT::param_; + + + public: + BusFault(BaseBusT* bus) : bus_(bus), R_(0), X_(0.01), + status_(0), busID_(0) + { + size_ = 0; + } + + BusFault(BaseBusT* bus, double R, double X, int status) : + bus_(bus), R_(R), X_(X), status_(status), busID_(0) + { + size_ = 0; + } + + ~BusFault() { } + + int allocate() override { return 0; } + + int initialize() override { return 0; } + + int tagDifferentiable() override { return 0; } + + int evaluateResidual() override + { + if (status_) + { + double B = -X_ / (X_*X_ + R_*R_); + double G = R_ / (X_*X_ + R_*R_); + Ir() += -Vr()*G + Vi()*B; + Ii() += -Vr()*B - Vi()*G; + } + return 0; + } + + int evaluateJacobian() override { return 0; } + + + int evaluateIntegrand() override { return 0; } + int initializeAdjoint() override { return 0; } + int evaluateAdjointResidual() override { return 0; } + int evaluateAdjointIntegrand() override { return 0; } + + void updateTime(double t, double a) override { } + + public: + void setR(double R) { R_ = R; } + + void setX(double X) { X_ = X; } + + void setStatus(int status) { status_ = status; } + + + private: + double& Vr() + { + return bus_->Vr(); + } + + double& Vi() + { + return bus_->Vi(); + } + + double& Ir() + { + return bus_->Ir(); + } + + double& Ii() + { + return bus_->Ii(); + } + + private: + BaseBusT* bus_; + double R_; + double X_; + int status_; + const int busID_; + }; + +} +} diff --git a/src/Model/PhasorDynamics/BusFault/CMakeLists.txt b/src/Model/PhasorDynamics/BusFault/CMakeLists.txt new file mode 100644 index 00000000..c78284ee --- /dev/null +++ b/src/Model/PhasorDynamics/BusFault/CMakeLists.txt @@ -0,0 +1,5 @@ +gridkit_add_library(phasor_dynamics_BusFault + SOURCES + BusFault.cpp + OUTPUT_NAME + gridkit_phasor_dynamics_BusFault) \ No newline at end of file diff --git a/src/Model/PhasorDynamics/SynchronousMachine/CMakeLists.txt b/src/Model/PhasorDynamics/SynchronousMachine/CMakeLists.txt index f8239be4..07667d32 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/CMakeLists.txt +++ b/src/Model/PhasorDynamics/SynchronousMachine/CMakeLists.txt @@ -10,3 +10,5 @@ gridkit_add_library(phasor_dynamics_synchronous_machine SynchronousMachine.cpp OUTPUT_NAME gridkit_phasor_dynamics_synchronous_machine) + +add_subdirectory(GENROUwS) diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/CMakeLists.txt b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/CMakeLists.txt new file mode 100644 index 00000000..fbe3da17 --- /dev/null +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/CMakeLists.txt @@ -0,0 +1,5 @@ +gridkit_add_library(phasor_dynamics_GENROU + SOURCES + GENROU.cpp + OUTPUT_NAME + gridkit_phasor_dynamics_GENROU) diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp new file mode 100644 index 00000000..f4e56bab --- /dev/null +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp @@ -0,0 +1 @@ +#include "GENROU.hpp" \ No newline at end of file diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp new file mode 100644 index 00000000..4dcdc942 --- /dev/null +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp @@ -0,0 +1,317 @@ +/* GENROU Component - Adam Birchfield */ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include + +namespace GridKit +{ +namespace PhasorDynamics +{ + using ComponentT = Component; + using BaseBusT = BusBase; + + class GENROU : public ComponentT + { + using ComponentT::size_; + using ComponentT::nnz_; + using ComponentT::time_; + using ComponentT::alpha_; + using ComponentT::y_; + using ComponentT::yp_; + using ComponentT::tag_; + using ComponentT::f_; + using ComponentT::g_; + using ComponentT::yB_; + using ComponentT::ypB_; + using ComponentT::fB_; + using ComponentT::gB_; + using ComponentT::param_; + + + public: + + GENROU(BaseBusT* bus, int unit_id) : bus_(bus), unit_id_(unit_id), + p0_(0), q0_(0), busID_(0), H_(3), D_(0), Ra_(0), Tdop_(7), + Tdopp_(.04), Tqopp_(.05), Tqop_(.75), Xd_(2.1), Xdp_(0.2), + Xdpp_(0.18), Xq_(.5), Xqp_(.5), Xqpp_(.18), Xl_(.15), + S10_(0), S12_(0) + { + size_ = 21; + set_derived_params(); + } + + GENROU(BaseBusT* bus, int unit_id, double p0, double q0, double H, + double D, double Ra, double Tdop, double Tdopp, double Tqopp, + double Tqop, double Xd, double Xdp, double Xdpp, double Xq, + double Xqp, double Xqpp, double Xl, double S10, double S12) : + bus_(bus), unit_id_(unit_id), p0_(p0), q0_(q0), busID_(0), H_(H), + D_(D), Ra_(Ra), Tdop_(Tdop), Tdopp_(Tdopp), Tqopp_(Tqopp), + Tqop_(Tqop), Xd_(Xd), Xdp_(Xdp), Xdpp_(Xdpp), Xq_(Xq), Xqp_(Xqp), + Xqpp_(Xqpp), Xl_(Xl), S10_(S10), S12_(S12) + { + size_ = 21; + set_derived_params(); + } + + void set_derived_params() + { + SA_ = 0; + SB_ = 0; + if (S12_ != 0) + { + double s112 = sqrt(S10_ / S12_); + SA_ = (1.2*s112 + 1) / (s112 + 1); + SB_ = (1.2*s112 - 1) / (s112 - 1); + if (SB_ < SA_) SA_ = SB_; + SB_ = S12_ / pow(SA_ - 1.2, 2); + } + Xd1_ = Xd_ - Xdp_; + Xd2_ = Xdp_ - Xl_; + Xd3_ = (Xdp_ - Xdpp_) / (Xd2_ * Xd2_); + Xd4_ = (Xdp_ - Xdpp_) / Xd2_; + Xd5_ = (Xdpp_ - Xl_) / Xd2_; + Xq1_ = Xq_ - Xqp_; + Xq2_ = Xqp_ - Xl_; + Xq3_ = (Xqp_ - Xqpp_) / (Xq2_ * Xq2_); + Xq4_ = (Xqp_ - Xqpp_) / Xq2_; + Xq5_ = (Xqpp_ - Xl_) / Xq2_; + Xqd_ = (Xq_ - Xl_) / (Xd_ - Xl_); + G_ = Ra_ / (Ra_*Ra_ + Xqpp_*Xqpp_); + B_ = -Xqpp_ / (Ra_*Ra_ + Xqpp_*Xqpp_); + + } + + ~GENROU() { } + + int allocate() override + { + f_.resize(size_); + y_.resize(size_); + yp_.resize(size_); + tag_.resize(size_); + fB_.resize(size_); + yB_.resize(size_); + ypB_.resize(size_); + return 0; + } + + int initialize() override + { + double delta, omega, Eqp, psidp, psiqp, Edp, psiqpp, psidpp, psipp, + vd, vq, id, iq, ir, ii, Te, g, b, ksat; + + /* Initialization tricks -- assuming NO saturation */ + double vr, vi, p, q, vm2, Er, Ei; + vr = Vr(); + vi = Vi(); + p = p0_; + q = q0_; + vm2 = vr*vr + vi*vi; + Er = vr + (Ra_*p*vr + Ra_*q*vi - Xq_*p*vi + Xq_*q*vr) / vm2; + Ei = vi + (Ra_*p*vi - Ra_*q*vr + Xq_*p*vr + Xq_*q*vi) / vm2; + delta = atan2(Ei, Er); + omega = 0; + ir = (p*vr + q*vi) / vm2; + ii = (p*vi - q*vr) / vm2; + id = ir*sin(delta) -ii*cos(delta); + iq = ir*cos(delta) +ii*sin(delta); + vd = vr*sin(delta) - vi*cos(delta) + id*Ra_ - iq*Xqpp_; + vq = vr*cos(delta) + vi*sin(delta) + id*Xqpp_ - iq*Ra_; + psiqpp = -vd / (1 + omega); + psidpp = vq / (1 + omega); + Te = (psidpp - id*Xdpp_)*iq - (psiqpp - iq*Xdpp_)*id; + psiqp = -(-(Xqp_-Xl_)*iq+psiqpp*(Xqp_-Xl_)/(Xqpp_-Xl_)) + /(1+(Xqp_-Xqpp_)/(Xqpp_-Xl_)); + Edp = psiqp - (Xqp_ - Xl_) * iq; + psidp = -((Xdp_-Xl_)*id-psidpp*(Xdp_-Xl_)/(Xdpp_-Xl_)) + /(1+(Xdp_-Xdpp_)/(Xdpp_-Xl_)); + Eqp = psidp + (Xdp_ - Xl_) * id; + + /* Now we have the state variables, solve for alg. variables */ + + y_[0] = delta; //= 0.55399038; + y_[1] = omega; // = 0; + y_[2] = Eqp; // = 0.995472581; + y_[3] = psidp; // = 0.971299567; + y_[4] = psiqp; // = 0.306880069; + y_[5] = Edp; // = 0; + + y_[6] = psiqpp = -psiqp*Xq4_ - Edp*Xq5_; + y_[7] = psidpp = psidp*Xd4_ + Eqp*Xd5_; + y_[8] = psipp = sqrt(psiqpp*psiqpp + psidpp*psidpp); + y_[9] = ksat = SB_*pow(psipp - SA_, 2); + y_[10] = vd = -psiqpp*(1 + omega); + y_[11] = vq = psidpp*(1 + omega); + y_[12] = Te = (psidpp - id*Xdpp_)*iq - (psiqpp - iq*Xdpp_)*id; + y_[13] = id; + y_[14] = iq; + y_[15] = ir; + y_[16] = ii; + y_[17] = pmech_set_ = Te; + y_[18] = efd_set_ = Eqp + Xd1_*(id + + Xd3_*(Eqp - psidp - Xd2_*id)) + psidpp*ksat; + y_[19] = G_*(vd*sin(delta)+vq*cos(delta)) + - B_*(vd*-cos(delta) + vq*sin(delta)); /* inort, real */; + y_[20] = B_*(vd*sin(delta)+vq*cos(delta)) + + G_*(vd*-cos(delta) + vq*sin(delta)); /* inort, imag */ + + for (int i = 0; i < size_; ++i) yp_[i] = 0.0; + return 0; + } + + int tagDifferentiable() override + { + for (int i = 0; i < size_; ++i) + { + tag_[i] = i < 6; + } + return 0; + } + + int evaluateResidual() override + { + double delta, omega, Eqp, psidp, psiqp, Edp, psiqpp, psidpp, psipp, + ksat, vd, vq, telec, id, iq, ir, ii, pmech, efd, inr, ini, vr, + vi, vr_inf, vi_inf, delta_dot, omega_dot, Eqp_dot, psidp_dot, + psiqp_dot, Edp_dot; + + /* Read variables */ + delta = y_[0]; + omega = y_[1]; + Eqp = y_[2]; + psidp = y_[3]; + psiqp = y_[4]; + Edp = y_[5]; + psiqpp = y_[6]; + psidpp = y_[7]; + psipp = y_[8]; + ksat = y_[9]; + vd = y_[10]; + vq = y_[11]; + telec = y_[12]; + id = y_[13]; + iq = y_[14]; + ir = y_[15]; + ii = y_[16]; + pmech = y_[17]; + efd = y_[18]; + inr = y_[19]; + ini = y_[20]; + vr = Vr(); + vi = Vi(); + + /* Read derivatives */ + delta_dot = yp_[0]; + omega_dot = yp_[1]; + Eqp_dot = yp_[2]; + psidp_dot = yp_[3]; + psiqp_dot = yp_[4]; + Edp_dot = yp_[5]; + + /* 6 GENROU differential equations */ + f_[0] = delta_dot - omega*(2*M_PI*60); + f_[1] = omega_dot - (1/(2*H_)) * ((pmech - D_*omega) / (1 + omega) + - telec); + f_[2] = Eqp_dot - (1/Tdop_) * (efd - (Eqp + Xd1_*(id + Xd3_*(Eqp + - psidp - Xd2_*id)) + psidpp*ksat )); + f_[3] = psidp_dot - (1/Tdopp_) * (Eqp - psidp - Xd2_*id); + f_[4] = psiqp_dot - (1/Tqopp_) * (Edp - psiqp + Xq2_*iq); + f_[5] = Edp_dot - (1/Tqop_) * (-Edp + Xqd_*psiqpp*ksat + + Xq1_*(iq - Xq3_*(Edp + iq*Xq2_ - psiqp))); + + /* 11 GENROU algebraic equations */ + f_[6] = psiqpp - (-psiqp * Xq4_ - Edp * Xq5_); + f_[7] = psidpp - (psidp * Xd4_ + Eqp * Xd5_); + f_[8] = psipp - sqrt(pow(psidpp, 2.0) + pow(psiqpp, 2.0)); + f_[9] = ksat - SB_*pow(psipp - SA_, 2.0); + f_[10] = vd + psiqpp * (1 + omega); + f_[11] = vq - psidpp * (1 + omega); + f_[12] = telec - ((psidpp - id*Xdpp_)*iq - (psiqpp - iq*Xdpp_)*id); + f_[13] = id - (ir*sin(delta) - ii*cos(delta)); + f_[14] = iq - (ir*cos(delta) + ii*sin(delta)); + f_[15] = ir + G_*vr - B_*vi - inr; + f_[16] = ii + B_*vr + G_*vi - ini; + + /* 2 GENROU control inputs are set to constant for this example */ + f_[17] = pmech - pmech_set_; + f_[18] = efd - efd_set_; + + /* 2 GENROU current source definitions */ + f_[19] = inr - (G_*(sin(delta)*vd + cos(delta)*vq) + - B_*(-cos(delta)*vd + sin(delta)*vq)); + f_[20] = ini - (B_*(sin(delta)*vd + cos(delta)*vq) + + G_*(-cos(delta)*vd + sin(delta)*vq)); + + /* Current balance */ + Ir() += inr - Vr()*G_ + Vi()*B_; + Ii() += ini - Vr()*B_ - Vi()*G_; + + //printf("GENROU residual\n"); + //for (int i = 0 ; i < 21; ++i) printf("%d: %g\n", i, f_[i]); + + //printf("GENROU inr %g Vr %g B %g Vi %g G %g\n", inr, Vr(), B_, Vi(), G_); + //printf("GENROU Ii = %g\n", inr - Vr()*B_ - Vi()*G_); + + return 0; + } + + int evaluateJacobian() override + { + /* TODO */ + return 0; + } + + /* Don't know what to do with any of these */ + int evaluateIntegrand() override { return 0; } + int initializeAdjoint() override { return 0; } + int evaluateAdjointResidual() override { return 0; } + int evaluateAdjointIntegrand() override { return 0; } + void updateTime(double t, double a) override { } + + private: + double& Vr() + { + return bus_->Vr(); + } + + double& Vi() + { + return bus_->Vi(); + } + + double& Ir() + { + return bus_->Ir(); + } + + double& Ii() + { + return bus_->Ii(); + } + + private: + + /* Identification */ + BaseBusT* bus_; + const int busID_; + int unit_id_; + + /* Initial terminal conditions */ + double p0_, q0_; + + /* Input parameters */ + double H_, D_, Ra_, Tdop_, Tdopp_, Tqopp_, Tqop_, Xd_, Xdp_, Xdpp_, + Xq_, Xqp_, Xqpp_, Xl_, S10_, S12_; + + /* Derivied parameters */ + double SA_, SB_, Xd1_, Xd2_, Xd3_, Xd4_, Xd5_, Xq1_, Xq2_, Xq3_, Xq4_, + Xq5_, Xqd_, G_, B_; + + /* Setpoints for control variables (determined at initialization) */ + double pmech_set_, efd_set_; + }; + +} +} diff --git a/src/Solver/Dynamic/Ida.cpp b/src/Solver/Dynamic/Ida.cpp index 456c4dcb..7d68b945 100644 --- a/src/Solver/Dynamic/Ida.cpp +++ b/src/Solver/Dynamic/Ida.cpp @@ -75,7 +75,7 @@ namespace AnalysisManager // Create vectors to store restart initial condition yy0_ = N_VClone(yy_); checkAllocation((void*) yy0_, "N_VClone"); - yp0_ = N_VClone(yy_); + yp0_ = N_VClone(yp_); checkAllocation((void*) yp0_, "N_VClone"); // Dummy initial time; will be overridden. @@ -205,6 +205,36 @@ namespace AnalysisManager return retval; } + template + int Ida::runSimulationFixed(real_type t0, real_type dt, + real_type tmax, FILE *f) + { + int retval = 0; + int iout = 0; + real_type t, tret; + + //printOutputF(t0, 0, f); + for (t = t0+dt; t < tmax; t += dt) + { + retval = IDASolve(solver_, t, &tret, yy_, yp_, IDA_NORMAL); + checkOutput(retval, "IDASolve"); + printOutputF(t, retval, f); + + if (retval != IDA_SUCCESS) + { + printf("IDA Failure! %dn", retval); + break; + } + } + + model_->updateTime(t, 0.0); + copyVec(yy_, model_->y()); + copyVec(yp_, model_->yp()); + + return retval; + } + + template int Ida::runSimulation(real_type tf, int nout) { @@ -651,6 +681,23 @@ namespace AnalysisManager } } + void Ida::printOutputF(sunrealtype t, int res, FILE *f) + { + sunrealtype *yval = N_VGetArrayPointer_Serial(yy_); + sunrealtype *ypval = N_VGetArrayPointer_Serial(yp_); + + fprintf(f, "%g,%d", t, res); + for (IdxT i = 0; i < model_->size(); ++i) + { + fprintf(f, ",%g", yval[i]); + } + for (IdxT i = 0; i < model_->size(); ++i) + { + fprintf(f, ",%g", ypval[i]); + } + fprintf(f, "\n"); + } + template void Ida::printOutput(sunrealtype t) { diff --git a/src/Solver/Dynamic/Ida.hpp b/src/Solver/Dynamic/Ida.hpp index 71cc6a40..8ad99b51 100644 --- a/src/Solver/Dynamic/Ida.hpp +++ b/src/Solver/Dynamic/Ida.hpp @@ -36,6 +36,9 @@ namespace AnalysisManager int runSimulation(real_type tf, int nout = 1); int deleteSimulation(); + // TODO: Temporary + int runSimulationFixed(real_type t0, real_type dt, real_type tmax, FILE* f); + int configureQuadrature(); int initializeQuadrature(); int runSimulationQuadrature(real_type tf, int nout = 1); @@ -101,6 +104,7 @@ namespace AnalysisManager void printOutput(sunrealtype t); void printSpecial(sunrealtype t, N_Vector x); void printFinalStats(); + void printOutputF(sunrealtype t, int res, FILE* f); private: static int Residual(sunrealtype t, @@ -109,7 +113,16 @@ namespace AnalysisManager N_Vector rr, void* user_data); - static int Jac(sunrealtype t, sunrealtype cj, N_Vector yy, N_Vector yp, N_Vector resvec, SUNMatrix J, void* user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3); + static int Jac(sunrealtype t, + sunrealtype cj, + N_Vector yy, + N_Vector yp, + N_Vector resvec, + SUNMatrix J, + void* user_data, + N_Vector tmp1, + N_Vector tmp2, + N_Vector tmp3); static int Integrand(sunrealtype t, N_Vector yy, From 313e4c56f4f5e40eb90593194f37c4a6f0c58627 Mon Sep 17 00:00:00 2001 From: Slaven Peles Date: Tue, 11 Mar 2025 19:02:03 -0400 Subject: [PATCH 11/20] Fix minor cherry pick issue. --- src/Solver/Dynamic/Ida.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Solver/Dynamic/Ida.cpp b/src/Solver/Dynamic/Ida.cpp index 7d68b945..41bb5b81 100644 --- a/src/Solver/Dynamic/Ida.cpp +++ b/src/Solver/Dynamic/Ida.cpp @@ -681,6 +681,7 @@ namespace AnalysisManager } } + template void Ida::printOutputF(sunrealtype t, int res, FILE *f) { sunrealtype *yval = N_VGetArrayPointer_Serial(yy_); From b73554624d87ec15305c1668e8529df20805abc2 Mon Sep 17 00:00:00 2001 From: Slaven Peles Date: Tue, 11 Mar 2025 20:01:55 -0400 Subject: [PATCH 12/20] Update code style for GENROU model and example. --- examples/PhasorDynamics/Example1/example1.cpp | 129 ++-- examples/PhasorDynamics/Example1/example1.hpp | 30 - .../SynchronousMachine/GENROUwS/GENROU.hpp | 669 ++++++++++-------- 3 files changed, 454 insertions(+), 374 deletions(-) delete mode 100644 examples/PhasorDynamics/Example1/example1.hpp diff --git a/examples/PhasorDynamics/Example1/example1.cpp b/examples/PhasorDynamics/Example1/example1.cpp index 976f03f3..702ddc59 100644 --- a/examples/PhasorDynamics/Example1/example1.cpp +++ b/examples/PhasorDynamics/Example1/example1.cpp @@ -1,60 +1,85 @@ -#include "example1.hpp" +#include +#define _USE_MATH_DEFINES +#include +#include + +// #include +#include +#include +#include +#include +#include + +#include "Model/PhasorDynamics/Branch/Branch.cpp" +#include "Model/PhasorDynamics/Branch/Branch.hpp" +#include "Model/PhasorDynamics/Bus/Bus.cpp" +#include "Model/PhasorDynamics/Bus/Bus.hpp" +#include "Model/PhasorDynamics/Bus/BusInfinite.cpp" +#include "Model/PhasorDynamics/Bus/BusInfinite.hpp" +#include "Model/PhasorDynamics/BusFault/BusFault.hpp" +#include "Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp" +#include "Model/PhasorDynamics/SystemModel.hpp" +#include "Solver/Dynamic/Ida.cpp" +#include "Solver/Dynamic/Ida.hpp" #define _CRT_SECURE_NO_WARNINGS -using namespace GridKit::PhasorDynamics; -using namespace AnalysisManager::Sundials; int main() { - printf("Example 1 version 2\n"); - - /* Create model parts */ - SystemModel sys; - Bus bus1(0.9949877346411762, 0.09999703952427966); - BusInfinite bus2(1.0, 0.0); - Branch branch(&bus1, &bus2, 0, 0.1, 0, 0); - BusFault fault(&bus1, 0, 1e-3, 0); - GENROU gen(&bus1, 1, 1, 0.05013, 3, 0, 0, 7, .04, .05, .75, 2.1, 0.2, 0.18, - 0.5, 0.5, 0.18, 0.15, 0, 0); - - /* Connect everything together */ - sys.addBus(&bus1); - sys.addBus(&bus2); - sys.addComponent(&branch); - sys.addComponent(&fault); - sys.addComponent(&gen); - sys.allocate(); - - double dt = 1.0/4.0/60.0; - - /* Output file header */ - FILE *f = fopen("example1_v2_results.csv", "w"); - if (!f) printf("ERROR writing to output file!\n"); - fprintf(f, "%s,%s", "t", "IDA Return Value"); - for (int i = 0; i < sys.size(); ++i) fprintf(f, ",Y[%d]", i); - for (int i = 0; i < sys.size(); ++i) fprintf(f, ",Yp[%d]", i); - fprintf(f, "\n"); - - /* Set up simulation */ - Ida ida(&sys); - ida.configureSimulation(); - - /* Run simulation */ - double start = (double) clock(); - ida.printOutputF(0, 0, f); - ida.initializeSimulation(0, false); - ida.runSimulationFixed(0, dt, 1, f); - fault.setStatus(1); - ida.initializeSimulation(1, false); - ida.runSimulationFixed(1, dt, 1.1, f); - fault.setStatus(0); - ida.initializeSimulation(1.1, false); - ida.runSimulationFixed(1.1, dt, 30, f); - - printf("Complete in %.4g seconds\n", (clock() - start) / CLOCKS_PER_SEC); - fclose(f); - - return 0; + using namespace GridKit::PhasorDynamics; + using namespace AnalysisManager::Sundials; + + printf("Example 1 version 2\n"); + + /* Create model parts */ + SystemModel sys; + Bus bus1(0.9949877346411762, 0.09999703952427966); + BusInfinite bus2(1.0, 0.0); + Branch branch(&bus1, &bus2, 0, 0.1, 0, 0); + BusFault fault(&bus1, 0, 1e-3, 0); + + GENROU gen(&bus1, 1, 1, 0.05013, 3, 0, 0, 7, .04, .05, .75, 2.1, 0.2, 0.18, 0.5, 0.5, 0.18, 0.15, 0, 0); + + /* Connect everything together */ + sys.addBus(&bus1); + sys.addBus(&bus2); + sys.addComponent(&branch); + sys.addComponent(&fault); + sys.addComponent(&gen); + sys.allocate(); + + double dt = 1.0 / 4.0 / 60.0; + + /* Output file header */ + FILE* f = fopen("example1_v2_results.csv", "w"); + if (!f) + printf("ERROR writing to output file!\n"); + fprintf(f, "%s,%s", "t", "IDA Return Value"); + for (int i = 0; i < sys.size(); ++i) + fprintf(f, ",Y[%d]", i); + for (int i = 0; i < sys.size(); ++i) + fprintf(f, ",Yp[%d]", i); + fprintf(f, "\n"); + + /* Set up simulation */ + Ida ida(&sys); + ida.configureSimulation(); + + /* Run simulation */ + double start = (double) clock(); + ida.printOutputF(0, 0, f); + ida.initializeSimulation(0.0, false); + ida.runSimulationFixed(0.0, dt, 1.0, f); + fault.setStatus(1); + ida.initializeSimulation(1.0, false); + ida.runSimulationFixed(1.0, dt, 1.1, f); + fault.setStatus(0); + ida.initializeSimulation(1.1, false); + ida.runSimulationFixed(1.1, dt, 10.0, f); + + printf("Complete in %.4g seconds\n", (clock() - start) / CLOCKS_PER_SEC); + fclose(f); + return 0; } \ No newline at end of file diff --git a/examples/PhasorDynamics/Example1/example1.hpp b/examples/PhasorDynamics/Example1/example1.hpp deleted file mode 100644 index 836c729f..00000000 --- a/examples/PhasorDynamics/Example1/example1.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#define _USE_MATH_DEFINES -#include -#include - -//#include -#include -#include -#include -#include -#include - -#include "Model/PhasorDynamics/SystemModel.hpp" -#include "Model/PhasorDynamics/Bus/Bus.hpp" -#include "Model/PhasorDynamics/Bus/BusInfinite.hpp" -#include "Model/PhasorDynamics/Branch/Branch.hpp" -#include "Model/PhasorDynamics/BusFault/BusFault.hpp" -#include "Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp" - - -#include "Solver/Dynamic/Ida.hpp" - -#include "Model/PhasorDynamics/Bus/Bus.cpp" -#include "Model/PhasorDynamics/Bus/BusInfinite.cpp" -#include "Model/PhasorDynamics/Branch/Branch.cpp" -#include "Solver/Dynamic/Ida.cpp" - - - -int main(); diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp index 4dcdc942..5a4550c7 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp @@ -2,316 +2,401 @@ #pragma once #define _USE_MATH_DEFINES -#include #include +#include namespace GridKit { -namespace PhasorDynamics -{ + namespace PhasorDynamics + { using ComponentT = Component; - using BaseBusT = BusBase; + using BaseBusT = BusBase; class GENROU : public ComponentT { - using ComponentT::size_; - using ComponentT::nnz_; - using ComponentT::time_; - using ComponentT::alpha_; - using ComponentT::y_; - using ComponentT::yp_; - using ComponentT::tag_; - using ComponentT::f_; - using ComponentT::g_; - using ComponentT::yB_; - using ComponentT::ypB_; - using ComponentT::fB_; - using ComponentT::gB_; - using ComponentT::param_; - + using ComponentT::alpha_; + using ComponentT::f_; + using ComponentT::fB_; + using ComponentT::g_; + using ComponentT::gB_; + using ComponentT::nnz_; + using ComponentT::param_; + using ComponentT::size_; + using ComponentT::tag_; + using ComponentT::time_; + using ComponentT::y_; + using ComponentT::yB_; + using ComponentT::yp_; + using ComponentT::ypB_; public: - - GENROU(BaseBusT* bus, int unit_id) : bus_(bus), unit_id_(unit_id), - p0_(0), q0_(0), busID_(0), H_(3), D_(0), Ra_(0), Tdop_(7), - Tdopp_(.04), Tqopp_(.05), Tqop_(.75), Xd_(2.1), Xdp_(0.2), - Xdpp_(0.18), Xq_(.5), Xqp_(.5), Xqpp_(.18), Xl_(.15), - S10_(0), S12_(0) + GENROU(BaseBusT* bus, int unit_id) + : bus_(bus), + unit_id_(unit_id), + p0_(0), + q0_(0), + busID_(0), + H_(3), + D_(0), + Ra_(0), + Tdop_(7), + Tdopp_(.04), + Tqopp_(.05), + Tqop_(.75), + Xd_(2.1), + Xdp_(0.2), + Xdpp_(0.18), + Xq_(.5), + Xqp_(.5), + Xqpp_(.18), + Xl_(.15), + S10_(0), + S12_(0) + { + size_ = 21; + set_derived_params(); + } + + GENROU(BaseBusT* bus, + int unit_id, + double p0, + double q0, + double H, + double D, + double Ra, + double Tdop, + double Tdopp, + double Tqopp, + double Tqop, + double Xd, + double Xdp, + double Xdpp, + double Xq, + double Xqp, + double Xqpp, + double Xl, + double S10, + double S12) + : bus_(bus), + unit_id_(unit_id), + p0_(p0), + q0_(q0), + busID_(0), + H_(H), + D_(D), + Ra_(Ra), + Tdop_(Tdop), + Tdopp_(Tdopp), + Tqopp_(Tqopp), + Tqop_(Tqop), + Xd_(Xd), + Xdp_(Xdp), + Xdpp_(Xdpp), + Xq_(Xq), + Xqp_(Xqp), + Xqpp_(Xqpp), + Xl_(Xl), + S10_(S10), + S12_(S12) + { + size_ = 21; + set_derived_params(); + } + + void set_derived_params() + { + SA_ = 0; + SB_ = 0; + if (S12_ != 0) { - size_ = 21; - set_derived_params(); - } + double s112 = sqrt(S10_ / S12_); - GENROU(BaseBusT* bus, int unit_id, double p0, double q0, double H, - double D, double Ra, double Tdop, double Tdopp, double Tqopp, - double Tqop, double Xd, double Xdp, double Xdpp, double Xq, - double Xqp, double Xqpp, double Xl, double S10, double S12) : - bus_(bus), unit_id_(unit_id), p0_(p0), q0_(q0), busID_(0), H_(H), - D_(D), Ra_(Ra), Tdop_(Tdop), Tdopp_(Tdopp), Tqopp_(Tqopp), - Tqop_(Tqop), Xd_(Xd), Xdp_(Xdp), Xdpp_(Xdpp), Xq_(Xq), Xqp_(Xqp), - Xqpp_(Xqpp), Xl_(Xl), S10_(S10), S12_(S12) - { - size_ = 21; - set_derived_params(); + SA_ = (1.2 * s112 + 1) / (s112 + 1); + SB_ = (1.2 * s112 - 1) / (s112 - 1); + if (SB_ < SA_) + SA_ = SB_; + SB_ = S12_ / pow(SA_ - 1.2, 2); } - - void set_derived_params() + Xd1_ = Xd_ - Xdp_; + Xd2_ = Xdp_ - Xl_; + Xd3_ = (Xdp_ - Xdpp_) / (Xd2_ * Xd2_); + Xd4_ = (Xdp_ - Xdpp_) / Xd2_; + Xd5_ = (Xdpp_ - Xl_) / Xd2_; + Xq1_ = Xq_ - Xqp_; + Xq2_ = Xqp_ - Xl_; + Xq3_ = (Xqp_ - Xqpp_) / (Xq2_ * Xq2_); + Xq4_ = (Xqp_ - Xqpp_) / Xq2_; + Xq5_ = (Xqpp_ - Xl_) / Xq2_; + Xqd_ = (Xq_ - Xl_) / (Xd_ - Xl_); + G_ = Ra_ / (Ra_ * Ra_ + Xqpp_ * Xqpp_); + B_ = -Xqpp_ / (Ra_ * Ra_ + Xqpp_ * Xqpp_); + } + + ~GENROU() + { + } + + int allocate() override + { + f_.resize(size_); + y_.resize(size_); + yp_.resize(size_); + tag_.resize(size_); + fB_.resize(size_); + yB_.resize(size_); + ypB_.resize(size_); + return 0; + } + + int initialize() override + { + /* Initialization tricks -- assuming NO saturation */ + double vr = Vr(); + double vi = Vi(); + double p = p0_; + double q = q0_; + double vm2 = vr * vr + vi * vi; + double Er = vr + (Ra_ * p * vr + Ra_ * q * vi - Xq_ * p * vi + Xq_ * q * vr) / vm2; + double Ei = vi + (Ra_ * p * vi - Ra_ * q * vr + Xq_ * p * vr + Xq_ * q * vi) / vm2; + double delta = atan2(Ei, Er); + double omega = 0; + double ir = (p * vr + q * vi) / vm2; + double ii = (p * vi - q * vr) / vm2; + double id = ir * sin(delta) - ii * cos(delta); + double iq = ir * cos(delta) + ii * sin(delta); + double vd = vr * sin(delta) - vi * cos(delta) + id * Ra_ - iq * Xqpp_; + double vq = vr * cos(delta) + vi * sin(delta) + id * Xqpp_ - iq * Ra_; + double psiqpp = -vd / (1 + omega); + double psidpp = vq / (1 + omega); + double Te = (psidpp - id * Xdpp_) * iq - (psiqpp - iq * Xdpp_) * id; + double psiqp = -(-(Xqp_ - Xl_) * iq + psiqpp * (Xqp_ - Xl_) / (Xqpp_ - Xl_)) + / (1 + (Xqp_ - Xqpp_) / (Xqpp_ - Xl_)); + double Edp = psiqp - (Xqp_ - Xl_) * iq; + double psidp = -((Xdp_ - Xl_) * id - psidpp * (Xdp_ - Xl_) / (Xdpp_ - Xl_)) + / (1 + (Xdp_ - Xdpp_) / (Xdpp_ - Xl_)); + double Eqp = psidp + (Xdp_ - Xl_) * id; + + /* Now we have the state variables, solve for alg. variables */ + double ksat; + double psipp; + + y_[0] = delta; //= 0.55399038; + y_[1] = omega; // = 0; + y_[2] = Eqp; // = 0.995472581; + y_[3] = psidp; // = 0.971299567; + y_[4] = psiqp; // = 0.306880069; + y_[5] = Edp; // = 0; + + y_[6] = psiqpp = -psiqp * Xq4_ - Edp * Xq5_; + y_[7] = psidpp = psidp * Xd4_ + Eqp * Xd5_; + y_[8] = psipp = sqrt(psiqpp * psiqpp + psidpp * psidpp); + y_[9] = ksat = SB_ * pow(psipp - SA_, 2); + y_[10] = vd = -psiqpp * (1 + omega); + y_[11] = vq = psidpp * (1 + omega); + y_[12] = Te = (psidpp - id * Xdpp_) * iq - (psiqpp - iq * Xdpp_) * id; + y_[13] = id; + y_[14] = iq; + y_[15] = ir; + y_[16] = ii; + y_[17] = pmech_set_ = Te; + y_[18] = efd_set_ = Eqp + Xd1_ * (id + Xd3_ * (Eqp - psidp - Xd2_ * id)) + psidpp * ksat; + y_[19] = G_ * (vd * sin(delta) + vq * cos(delta)) + - B_ * (vd * -cos(delta) + vq * sin(delta)); /* inort, real */ + y_[20] = B_ * (vd * sin(delta) + vq * cos(delta)) + + G_ * (vd * -cos(delta) + vq * sin(delta)); /* inort, imag */ + + for (int i = 0; i < size_; ++i) + yp_[i] = 0.0; + return 0; + } + + int tagDifferentiable() override + { + for (int i = 0; i < size_; ++i) { - SA_ = 0; - SB_ = 0; - if (S12_ != 0) - { - double s112 = sqrt(S10_ / S12_); - SA_ = (1.2*s112 + 1) / (s112 + 1); - SB_ = (1.2*s112 - 1) / (s112 - 1); - if (SB_ < SA_) SA_ = SB_; - SB_ = S12_ / pow(SA_ - 1.2, 2); - } - Xd1_ = Xd_ - Xdp_; - Xd2_ = Xdp_ - Xl_; - Xd3_ = (Xdp_ - Xdpp_) / (Xd2_ * Xd2_); - Xd4_ = (Xdp_ - Xdpp_) / Xd2_; - Xd5_ = (Xdpp_ - Xl_) / Xd2_; - Xq1_ = Xq_ - Xqp_; - Xq2_ = Xqp_ - Xl_; - Xq3_ = (Xqp_ - Xqpp_) / (Xq2_ * Xq2_); - Xq4_ = (Xqp_ - Xqpp_) / Xq2_; - Xq5_ = (Xqpp_ - Xl_) / Xq2_; - Xqd_ = (Xq_ - Xl_) / (Xd_ - Xl_); - G_ = Ra_ / (Ra_*Ra_ + Xqpp_*Xqpp_); - B_ = -Xqpp_ / (Ra_*Ra_ + Xqpp_*Xqpp_); - + tag_[i] = i < 6; } - - ~GENROU() { } - - int allocate() override - { - f_.resize(size_); - y_.resize(size_); - yp_.resize(size_); - tag_.resize(size_); - fB_.resize(size_); - yB_.resize(size_); - ypB_.resize(size_); - return 0; - } - - int initialize() override - { - double delta, omega, Eqp, psidp, psiqp, Edp, psiqpp, psidpp, psipp, - vd, vq, id, iq, ir, ii, Te, g, b, ksat; - - /* Initialization tricks -- assuming NO saturation */ - double vr, vi, p, q, vm2, Er, Ei; - vr = Vr(); - vi = Vi(); - p = p0_; - q = q0_; - vm2 = vr*vr + vi*vi; - Er = vr + (Ra_*p*vr + Ra_*q*vi - Xq_*p*vi + Xq_*q*vr) / vm2; - Ei = vi + (Ra_*p*vi - Ra_*q*vr + Xq_*p*vr + Xq_*q*vi) / vm2; - delta = atan2(Ei, Er); - omega = 0; - ir = (p*vr + q*vi) / vm2; - ii = (p*vi - q*vr) / vm2; - id = ir*sin(delta) -ii*cos(delta); - iq = ir*cos(delta) +ii*sin(delta); - vd = vr*sin(delta) - vi*cos(delta) + id*Ra_ - iq*Xqpp_; - vq = vr*cos(delta) + vi*sin(delta) + id*Xqpp_ - iq*Ra_; - psiqpp = -vd / (1 + omega); - psidpp = vq / (1 + omega); - Te = (psidpp - id*Xdpp_)*iq - (psiqpp - iq*Xdpp_)*id; - psiqp = -(-(Xqp_-Xl_)*iq+psiqpp*(Xqp_-Xl_)/(Xqpp_-Xl_)) - /(1+(Xqp_-Xqpp_)/(Xqpp_-Xl_)); - Edp = psiqp - (Xqp_ - Xl_) * iq; - psidp = -((Xdp_-Xl_)*id-psidpp*(Xdp_-Xl_)/(Xdpp_-Xl_)) - /(1+(Xdp_-Xdpp_)/(Xdpp_-Xl_)); - Eqp = psidp + (Xdp_ - Xl_) * id; - - /* Now we have the state variables, solve for alg. variables */ - - y_[0] = delta; //= 0.55399038; - y_[1] = omega; // = 0; - y_[2] = Eqp; // = 0.995472581; - y_[3] = psidp; // = 0.971299567; - y_[4] = psiqp; // = 0.306880069; - y_[5] = Edp; // = 0; - - y_[6] = psiqpp = -psiqp*Xq4_ - Edp*Xq5_; - y_[7] = psidpp = psidp*Xd4_ + Eqp*Xd5_; - y_[8] = psipp = sqrt(psiqpp*psiqpp + psidpp*psidpp); - y_[9] = ksat = SB_*pow(psipp - SA_, 2); - y_[10] = vd = -psiqpp*(1 + omega); - y_[11] = vq = psidpp*(1 + omega); - y_[12] = Te = (psidpp - id*Xdpp_)*iq - (psiqpp - iq*Xdpp_)*id; - y_[13] = id; - y_[14] = iq; - y_[15] = ir; - y_[16] = ii; - y_[17] = pmech_set_ = Te; - y_[18] = efd_set_ = Eqp + Xd1_*(id - + Xd3_*(Eqp - psidp - Xd2_*id)) + psidpp*ksat; - y_[19] = G_*(vd*sin(delta)+vq*cos(delta)) - - B_*(vd*-cos(delta) + vq*sin(delta)); /* inort, real */; - y_[20] = B_*(vd*sin(delta)+vq*cos(delta)) - + G_*(vd*-cos(delta) + vq*sin(delta)); /* inort, imag */ - - for (int i = 0; i < size_; ++i) yp_[i] = 0.0; - return 0; - } - - int tagDifferentiable() override - { - for (int i = 0; i < size_; ++i) - { - tag_[i] = i < 6; - } - return 0; - } - - int evaluateResidual() override - { - double delta, omega, Eqp, psidp, psiqp, Edp, psiqpp, psidpp, psipp, - ksat, vd, vq, telec, id, iq, ir, ii, pmech, efd, inr, ini, vr, - vi, vr_inf, vi_inf, delta_dot, omega_dot, Eqp_dot, psidp_dot, - psiqp_dot, Edp_dot; - - /* Read variables */ - delta = y_[0]; - omega = y_[1]; - Eqp = y_[2]; - psidp = y_[3]; - psiqp = y_[4]; - Edp = y_[5]; - psiqpp = y_[6]; - psidpp = y_[7]; - psipp = y_[8]; - ksat = y_[9]; - vd = y_[10]; - vq = y_[11]; - telec = y_[12]; - id = y_[13]; - iq = y_[14]; - ir = y_[15]; - ii = y_[16]; - pmech = y_[17]; - efd = y_[18]; - inr = y_[19]; - ini = y_[20]; - vr = Vr(); - vi = Vi(); - - /* Read derivatives */ - delta_dot = yp_[0]; - omega_dot = yp_[1]; - Eqp_dot = yp_[2]; - psidp_dot = yp_[3]; - psiqp_dot = yp_[4]; - Edp_dot = yp_[5]; - - /* 6 GENROU differential equations */ - f_[0] = delta_dot - omega*(2*M_PI*60); - f_[1] = omega_dot - (1/(2*H_)) * ((pmech - D_*omega) / (1 + omega) - - telec); - f_[2] = Eqp_dot - (1/Tdop_) * (efd - (Eqp + Xd1_*(id + Xd3_*(Eqp - - psidp - Xd2_*id)) + psidpp*ksat )); - f_[3] = psidp_dot - (1/Tdopp_) * (Eqp - psidp - Xd2_*id); - f_[4] = psiqp_dot - (1/Tqopp_) * (Edp - psiqp + Xq2_*iq); - f_[5] = Edp_dot - (1/Tqop_) * (-Edp + Xqd_*psiqpp*ksat - + Xq1_*(iq - Xq3_*(Edp + iq*Xq2_ - psiqp))); - - /* 11 GENROU algebraic equations */ - f_[6] = psiqpp - (-psiqp * Xq4_ - Edp * Xq5_); - f_[7] = psidpp - (psidp * Xd4_ + Eqp * Xd5_); - f_[8] = psipp - sqrt(pow(psidpp, 2.0) + pow(psiqpp, 2.0)); - f_[9] = ksat - SB_*pow(psipp - SA_, 2.0); - f_[10] = vd + psiqpp * (1 + omega); - f_[11] = vq - psidpp * (1 + omega); - f_[12] = telec - ((psidpp - id*Xdpp_)*iq - (psiqpp - iq*Xdpp_)*id); - f_[13] = id - (ir*sin(delta) - ii*cos(delta)); - f_[14] = iq - (ir*cos(delta) + ii*sin(delta)); - f_[15] = ir + G_*vr - B_*vi - inr; - f_[16] = ii + B_*vr + G_*vi - ini; - - /* 2 GENROU control inputs are set to constant for this example */ - f_[17] = pmech - pmech_set_; - f_[18] = efd - efd_set_; - - /* 2 GENROU current source definitions */ - f_[19] = inr - (G_*(sin(delta)*vd + cos(delta)*vq) - - B_*(-cos(delta)*vd + sin(delta)*vq)); - f_[20] = ini - (B_*(sin(delta)*vd + cos(delta)*vq) - + G_*(-cos(delta)*vd + sin(delta)*vq)); - - /* Current balance */ - Ir() += inr - Vr()*G_ + Vi()*B_; - Ii() += ini - Vr()*B_ - Vi()*G_; - - //printf("GENROU residual\n"); - //for (int i = 0 ; i < 21; ++i) printf("%d: %g\n", i, f_[i]); - - //printf("GENROU inr %g Vr %g B %g Vi %g G %g\n", inr, Vr(), B_, Vi(), G_); - //printf("GENROU Ii = %g\n", inr - Vr()*B_ - Vi()*G_); - - return 0; - } - - int evaluateJacobian() override - { - /* TODO */ - return 0; - } - - /* Don't know what to do with any of these */ - int evaluateIntegrand() override { return 0; } - int initializeAdjoint() override { return 0; } - int evaluateAdjointResidual() override { return 0; } - int evaluateAdjointIntegrand() override { return 0; } - void updateTime(double t, double a) override { } + return 0; + } + + int evaluateResidual() override + { + /* Read variables */ + double delta = y_[0]; + double omega = y_[1]; + double Eqp = y_[2]; + double psidp = y_[3]; + double psiqp = y_[4]; + double Edp = y_[5]; + double psiqpp = y_[6]; + double psidpp = y_[7]; + double psipp = y_[8]; + double ksat = y_[9]; + double vd = y_[10]; + double vq = y_[11]; + double telec = y_[12]; + double id = y_[13]; + double iq = y_[14]; + double ir = y_[15]; + double ii = y_[16]; + double pmech = y_[17]; + double efd = y_[18]; + double inr = y_[19]; + double ini = y_[20]; + double vr = Vr(); + double vi = Vi(); + + /* Read derivatives */ + double delta_dot = yp_[0]; + double omega_dot = yp_[1]; + double Eqp_dot = yp_[2]; + double psidp_dot = yp_[3]; + double psiqp_dot = yp_[4]; + double Edp_dot = yp_[5]; + + /* 6 GENROU differential equations */ + f_[0] = delta_dot - omega * (2 * M_PI * 60); + f_[1] = omega_dot - (1 / (2 * H_)) * ((pmech - D_ * omega) / (1 + omega) - telec); + f_[2] = Eqp_dot - (1 / Tdop_) * (efd - (Eqp + Xd1_ * (id + Xd3_ * (Eqp - psidp - Xd2_ * id)) + psidpp * ksat)); + f_[3] = psidp_dot - (1 / Tdopp_) * (Eqp - psidp - Xd2_ * id); + f_[4] = psiqp_dot - (1 / Tqopp_) * (Edp - psiqp + Xq2_ * iq); + f_[5] = Edp_dot - (1 / Tqop_) * (-Edp + Xqd_ * psiqpp * ksat + Xq1_ * (iq - Xq3_ * (Edp + iq * Xq2_ - psiqp))); + + /* 11 GENROU algebraic equations */ + f_[6] = psiqpp - (-psiqp * Xq4_ - Edp * Xq5_); + f_[7] = psidpp - (psidp * Xd4_ + Eqp * Xd5_); + f_[8] = psipp - sqrt(pow(psidpp, 2.0) + pow(psiqpp, 2.0)); + f_[9] = ksat - SB_ * pow(psipp - SA_, 2.0); + f_[10] = vd + psiqpp * (1 + omega); + f_[11] = vq - psidpp * (1 + omega); + f_[12] = telec - ((psidpp - id * Xdpp_) * iq - (psiqpp - iq * Xdpp_) * id); + f_[13] = id - (ir * sin(delta) - ii * cos(delta)); + f_[14] = iq - (ir * cos(delta) + ii * sin(delta)); + f_[15] = ir + G_ * vr - B_ * vi - inr; + f_[16] = ii + B_ * vr + G_ * vi - ini; + + /* 2 GENROU control inputs are set to constant for this example */ + f_[17] = pmech - pmech_set_; + f_[18] = efd - efd_set_; + + /* 2 GENROU current source definitions */ + f_[19] = inr - (G_ * (sin(delta) * vd + cos(delta) * vq) - B_ * (-cos(delta) * vd + sin(delta) * vq)); + f_[20] = ini - (B_ * (sin(delta) * vd + cos(delta) * vq) + G_ * (-cos(delta) * vd + sin(delta) * vq)); + + /* Current balance */ + Ir() += inr - Vr() * G_ + Vi() * B_; + Ii() += ini - Vr() * B_ - Vi() * G_; + + // printf("GENROU residual\n"); + // for (int i = 0 ; i < 21; ++i) printf("%d: %g\n", i, f_[i]); + + // printf("GENROU inr %g Vr %g B %g Vi %g G %g\n", inr, Vr(), B_, Vi(), G_); + // printf("GENROU Ii = %g\n", inr - Vr()*B_ - Vi()*G_); + + return 0; + } + + int evaluateJacobian() override + { + /* TODO */ + return 0; + } + + /* Don't know what to do with any of these */ + int evaluateIntegrand() override + { + return 0; + } + + int initializeAdjoint() override + { + return 0; + } + + int evaluateAdjointResidual() override + { + return 0; + } + + int evaluateAdjointIntegrand() override + { + return 0; + } + + void updateTime(double t, double a) override + { + } private: - double& Vr() - { - return bus_->Vr(); - } - - double& Vi() - { - return bus_->Vi(); - } - - double& Ir() - { - return bus_->Ir(); - } - - double& Ii() - { - return bus_->Ii(); - } + double& Vr() + { + return bus_->Vr(); + } + + double& Vi() + { + return bus_->Vi(); + } + + double& Ir() + { + return bus_->Ir(); + } + + double& Ii() + { + return bus_->Ii(); + } private: - - /* Identification */ - BaseBusT* bus_; - const int busID_; - int unit_id_; - - /* Initial terminal conditions */ - double p0_, q0_; - - /* Input parameters */ - double H_, D_, Ra_, Tdop_, Tdopp_, Tqopp_, Tqop_, Xd_, Xdp_, Xdpp_, - Xq_, Xqp_, Xqpp_, Xl_, S10_, S12_; - - /* Derivied parameters */ - double SA_, SB_, Xd1_, Xd2_, Xd3_, Xd4_, Xd5_, Xq1_, Xq2_, Xq3_, Xq4_, - Xq5_, Xqd_, G_, B_; - - /* Setpoints for control variables (determined at initialization) */ - double pmech_set_, efd_set_; + /* Identification */ + BaseBusT* bus_; + const int busID_; + int unit_id_; + + /* Initial terminal conditions */ + double p0_; + double q0_; + + /* Input parameters */ + double H_; + double D_; + double Ra_; + double Tdop_; + double Tdopp_; + double Tqopp_; + double Tqop_; + double Xd_; + double Xdp_; + double Xdpp_; + double Xq_; + double Xqp_; + double Xqpp_; + double Xl_; + double S10_; + double S12_; + + /* Derivied parameters */ + double SA_; + double SB_; + double Xd1_; + double Xd2_; + double Xd3_; + double Xd4_; + double Xd5_; + double Xq1_; + double Xq2_; + double Xq3_; + double Xq4_; + double Xq5_; + double Xqd_; + double G_; + double B_; + + /* Setpoints for control variables (determined at initialization) */ + double pmech_set_; + double efd_set_; }; -} -} + } // namespace PhasorDynamics +} // namespace GridKit From 1205fe5206009997577b89120305b04c1716486c Mon Sep 17 00:00:00 2001 From: pelesh Date: Wed, 12 Mar 2025 00:10:06 +0000 Subject: [PATCH 13/20] Apply pre-commmit fixes --- examples/CMakeLists.txt | 2 +- examples/PhasorDynamics/CMakeLists.txt | 2 +- examples/PhasorDynamics/Example1/example1.cpp | 3 +- .../PhasorDynamics/BusFault/BusFault.cpp | 2 +- .../PhasorDynamics/BusFault/BusFault.hpp | 213 ++++++++++-------- .../PhasorDynamics/BusFault/CMakeLists.txt | 2 +- .../SynchronousMachine/GENROUwS/GENROU.cpp | 2 +- .../SynchronousMachine/GENROUwS/GENROU.hpp | 4 +- src/Solver/Dynamic/Ida.cpp | 50 ++-- 9 files changed, 157 insertions(+), 123 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6c867ea0..53e48481 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -25,4 +25,4 @@ if(GRIDKIT_ENABLE_ENZYME) add_subdirectory(Enzyme) endif() -add_subdirectory(PhasorDynamics) \ No newline at end of file +add_subdirectory(PhasorDynamics) diff --git a/examples/PhasorDynamics/CMakeLists.txt b/examples/PhasorDynamics/CMakeLists.txt index 5d441798..c758e4e5 100644 --- a/examples/PhasorDynamics/CMakeLists.txt +++ b/examples/PhasorDynamics/CMakeLists.txt @@ -1 +1 @@ -add_subdirectory(Example1) \ No newline at end of file +add_subdirectory(Example1) diff --git a/examples/PhasorDynamics/Example1/example1.cpp b/examples/PhasorDynamics/Example1/example1.cpp index 702ddc59..8e498c83 100644 --- a/examples/PhasorDynamics/Example1/example1.cpp +++ b/examples/PhasorDynamics/Example1/example1.cpp @@ -24,7 +24,6 @@ #define _CRT_SECURE_NO_WARNINGS - int main() { using namespace GridKit::PhasorDynamics; @@ -82,4 +81,4 @@ int main() fclose(f); return 0; -} \ No newline at end of file +} diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.cpp b/src/Model/PhasorDynamics/BusFault/BusFault.cpp index 42b70b45..277e80e1 100644 --- a/src/Model/PhasorDynamics/BusFault/BusFault.cpp +++ b/src/Model/PhasorDynamics/BusFault/BusFault.cpp @@ -1 +1 @@ -#include "BusFault.hpp" \ No newline at end of file +#include "BusFault.hpp" diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.hpp b/src/Model/PhasorDynamics/BusFault/BusFault.hpp index 9ee2cc98..800a7dfc 100644 --- a/src/Model/PhasorDynamics/BusFault/BusFault.hpp +++ b/src/Model/PhasorDynamics/BusFault/BusFault.hpp @@ -1,113 +1,150 @@ /* Bus Fault Component - Adam Birchfield */ #pragma once -#include #include +#include namespace GridKit { -namespace PhasorDynamics -{ + namespace PhasorDynamics + { using ComponentT = Component; - using BaseBusT = BusBase; + using BaseBusT = BusBase; class BusFault : public ComponentT { - using ComponentT::size_; - using ComponentT::nnz_; - using ComponentT::time_; - using ComponentT::alpha_; - using ComponentT::y_; - using ComponentT::yp_; - using ComponentT::tag_; - using ComponentT::f_; - using ComponentT::g_; - using ComponentT::yB_; - using ComponentT::ypB_; - using ComponentT::fB_; - using ComponentT::gB_; - using ComponentT::param_; - + using ComponentT::alpha_; + using ComponentT::f_; + using ComponentT::fB_; + using ComponentT::g_; + using ComponentT::gB_; + using ComponentT::nnz_; + using ComponentT::param_; + using ComponentT::size_; + using ComponentT::tag_; + using ComponentT::time_; + using ComponentT::y_; + using ComponentT::yB_; + using ComponentT::yp_; + using ComponentT::ypB_; public: - BusFault(BaseBusT* bus) : bus_(bus), R_(0), X_(0.01), - status_(0), busID_(0) + BusFault(BaseBusT* bus) + : bus_(bus), R_(0), X_(0.01), status_(0), busID_(0) + { + size_ = 0; + } + + BusFault(BaseBusT* bus, double R, double X, int status) + : bus_(bus), R_(R), X_(X), status_(status), busID_(0) + { + size_ = 0; + } + + ~BusFault() + { + } + + int allocate() override + { + return 0; + } + + int initialize() override + { + return 0; + } + + int tagDifferentiable() override + { + return 0; + } + + int evaluateResidual() override + { + if (status_) { - size_ = 0; + double B = -X_ / (X_ * X_ + R_ * R_); + double G = R_ / (X_ * X_ + R_ * R_); + Ir() += -Vr() * G + Vi() * B; + Ii() += -Vr() * B - Vi() * G; } - - BusFault(BaseBusT* bus, double R, double X, int status) : - bus_(bus), R_(R), X_(X), status_(status), busID_(0) - { - size_ = 0; - } - - ~BusFault() { } - - int allocate() override { return 0; } - - int initialize() override { return 0; } - - int tagDifferentiable() override { return 0; } - - int evaluateResidual() override - { - if (status_) - { - double B = -X_ / (X_*X_ + R_*R_); - double G = R_ / (X_*X_ + R_*R_); - Ir() += -Vr()*G + Vi()*B; - Ii() += -Vr()*B - Vi()*G; - } - return 0; - } - - int evaluateJacobian() override { return 0; } - - - int evaluateIntegrand() override { return 0; } - int initializeAdjoint() override { return 0; } - int evaluateAdjointResidual() override { return 0; } - int evaluateAdjointIntegrand() override { return 0; } - - void updateTime(double t, double a) override { } + return 0; + } + + int evaluateJacobian() override + { + return 0; + } + + int evaluateIntegrand() override + { + return 0; + } + + int initializeAdjoint() override + { + return 0; + } + + int evaluateAdjointResidual() override + { + return 0; + } + + int evaluateAdjointIntegrand() override + { + return 0; + } + + void updateTime(double t, double a) override + { + } public: - void setR(double R) { R_ = R; } + void setR(double R) + { + R_ = R; + } - void setX(double X) { X_ = X; } - - void setStatus(int status) { status_ = status; } + void setX(double X) + { + X_ = X; + } + void setStatus(int status) + { + status_ = status; + } private: - double& Vr() - { - return bus_->Vr(); - } - - double& Vi() - { - return bus_->Vi(); - } - - double& Ir() - { - return bus_->Ir(); - } - - double& Ii() - { - return bus_->Ii(); - } + double& Vr() + { + return bus_->Vr(); + } + + double& Vi() + { + return bus_->Vi(); + } + + double& Ir() + { + return bus_->Ir(); + } + + double& Ii() + { + return bus_->Ii(); + } private: - BaseBusT* bus_; - double R_; - double X_; - int status_; - const int busID_; + BaseBusT* bus_; + double R_; + double X_; + int status_; + const int busID_; }; -} -} + } // namespace PhasorDynamics +} // namespace GridKit diff --git a/src/Model/PhasorDynamics/BusFault/CMakeLists.txt b/src/Model/PhasorDynamics/BusFault/CMakeLists.txt index c78284ee..57279fe4 100644 --- a/src/Model/PhasorDynamics/BusFault/CMakeLists.txt +++ b/src/Model/PhasorDynamics/BusFault/CMakeLists.txt @@ -2,4 +2,4 @@ gridkit_add_library(phasor_dynamics_BusFault SOURCES BusFault.cpp OUTPUT_NAME - gridkit_phasor_dynamics_BusFault) \ No newline at end of file + gridkit_phasor_dynamics_BusFault) diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp index f4e56bab..3540acfd 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.cpp @@ -1 +1 @@ -#include "GENROU.hpp" \ No newline at end of file +#include "GENROU.hpp" diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp index 5a4550c7..2b1a6692 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GENROU.hpp @@ -170,10 +170,10 @@ namespace GridKit double psidpp = vq / (1 + omega); double Te = (psidpp - id * Xdpp_) * iq - (psiqpp - iq * Xdpp_) * id; double psiqp = -(-(Xqp_ - Xl_) * iq + psiqpp * (Xqp_ - Xl_) / (Xqpp_ - Xl_)) - / (1 + (Xqp_ - Xqpp_) / (Xqpp_ - Xl_)); + / (1 + (Xqp_ - Xqpp_) / (Xqpp_ - Xl_)); double Edp = psiqp - (Xqp_ - Xl_) * iq; double psidp = -((Xdp_ - Xl_) * id - psidpp * (Xdp_ - Xl_) / (Xdpp_ - Xl_)) - / (1 + (Xdp_ - Xdpp_) / (Xdpp_ - Xl_)); + / (1 + (Xdp_ - Xdpp_) / (Xdpp_ - Xl_)); double Eqp = psidp + (Xdp_ - Xl_) * id; /* Now we have the state variables, solve for alg. variables */ diff --git a/src/Solver/Dynamic/Ida.cpp b/src/Solver/Dynamic/Ida.cpp index 41bb5b81..acb9dd2f 100644 --- a/src/Solver/Dynamic/Ida.cpp +++ b/src/Solver/Dynamic/Ida.cpp @@ -206,35 +206,33 @@ namespace AnalysisManager } template - int Ida::runSimulationFixed(real_type t0, real_type dt, - real_type tmax, FILE *f) - { - int retval = 0; - int iout = 0; - real_type t, tret; - - //printOutputF(t0, 0, f); - for (t = t0+dt; t < tmax; t += dt) + int Ida::runSimulationFixed(real_type t0, real_type dt, real_type tmax, FILE* f) + { + int retval = 0; + int iout = 0; + real_type t, tret; + + // printOutputF(t0, 0, f); + for (t = t0 + dt; t < tmax; t += dt) + { + retval = IDASolve(solver_, t, &tret, yy_, yp_, IDA_NORMAL); + checkOutput(retval, "IDASolve"); + printOutputF(t, retval, f); + + if (retval != IDA_SUCCESS) { - retval = IDASolve(solver_, t, &tret, yy_, yp_, IDA_NORMAL); - checkOutput(retval, "IDASolve"); - printOutputF(t, retval, f); - - if (retval != IDA_SUCCESS) - { - printf("IDA Failure! %dn", retval); - break; - } + printf("IDA Failure! %dn", retval); + break; } + } - model_->updateTime(t, 0.0); - copyVec(yy_, model_->y()); - copyVec(yp_, model_->yp()); + model_->updateTime(t, 0.0); + copyVec(yy_, model_->y()); + copyVec(yp_, model_->yp()); - return retval; + return retval; } - template int Ida::runSimulation(real_type tf, int nout) { @@ -682,10 +680,10 @@ namespace AnalysisManager } template - void Ida::printOutputF(sunrealtype t, int res, FILE *f) + void Ida::printOutputF(sunrealtype t, int res, FILE* f) { - sunrealtype *yval = N_VGetArrayPointer_Serial(yy_); - sunrealtype *ypval = N_VGetArrayPointer_Serial(yp_); + sunrealtype* yval = N_VGetArrayPointer_Serial(yy_); + sunrealtype* ypval = N_VGetArrayPointer_Serial(yp_); fprintf(f, "%g,%d", t, res); for (IdxT i = 0; i < model_->size(); ++i) From 419be8dda8d1b03f1b79575fd2a0aab68bfb9f56 Mon Sep 17 00:00:00 2001 From: Slaven Peles Date: Tue, 11 Mar 2025 20:21:43 -0400 Subject: [PATCH 14/20] Updates to CMake and code style. --- examples/CMakeLists.txt | 3 +-- src/Model/PhasorDynamics/BusFault/BusFault.hpp | 9 +++++---- .../PhasorDynamics/SynchronousMachine/GENROUwS/README.md | 9 +++++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 53e48481..6e0382b7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -13,6 +13,7 @@ if(TARGET SUNDIALS::idas) add_subdirectory(RLCircuit) add_subdirectory(Microgrid) add_subdirectory(ScaleMicrogrid) + add_subdirectory(PhasorDynamics) if(GRIDKIT_ENABLE_IPOPT) add_subdirectory(DynamicConstrainedOpt) add_subdirectory(GenConstLoad) @@ -24,5 +25,3 @@ endif() if(GRIDKIT_ENABLE_ENZYME) add_subdirectory(Enzyme) endif() - -add_subdirectory(PhasorDynamics) diff --git a/src/Model/PhasorDynamics/BusFault/BusFault.hpp b/src/Model/PhasorDynamics/BusFault/BusFault.hpp index 800a7dfc..4de69fbd 100644 --- a/src/Model/PhasorDynamics/BusFault/BusFault.hpp +++ b/src/Model/PhasorDynamics/BusFault/BusFault.hpp @@ -64,10 +64,11 @@ namespace GridKit { if (status_) { - double B = -X_ / (X_ * X_ + R_ * R_); - double G = R_ / (X_ * X_ + R_ * R_); - Ir() += -Vr() * G + Vi() * B; - Ii() += -Vr() * B - Vi() * G; + double B = -X_ / (X_ * X_ + R_ * R_); + double G = R_ / (X_ * X_ + R_ * R_); + + Ir() += -Vr() * G + Vi() * B; + Ii() += -Vr() * B - Vi() * G; } return 0; } diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md index 029c89b6..fec362fe 100644 --- a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/README.md @@ -93,9 +93,14 @@ then Sat(\psi'')=Sat(\vert V_{r}+jV_{i} \vert) ``` -It is important to point out that finding the initial value of $`\delta`$ for the model without saturation direct method can be used. In case when saturation is considered some "claver" math is needed. Key insight for determining initial $`\delta`$ is that the magnitude of the saturation depends upon the magnitude of $`\psi''`$, which is independent of $`\delta`$. +It is important to point out that finding the initial value of $`\delta`$ for +the model without saturation direct method can be used. In case when saturation +is considered some "claver" math is needed. Key insight for determining initial +$`\delta`$ is that the magnitude of the saturation depends upon the magnitude +of $`\psi''`$, which is independent of $`\delta`$. ```math -\delta=tan^{-1}(\dfrac{K_{sat}V_{iterm}+K_{sat}R_{a}I_{i}+(K_{sat}X''_{d}+X_{q}-X''_{q})I_{r}}{K_{sat}V_{rterm}+K_{sat}R_{a}I_{r}-(K_{sat}X''_{d}+X_{q}-X''_{q})I_{i}}) +\delta=\tan^{-1}\left(\dfrac{K_{sat}V_{iterm}+K_{sat}R_{a}I_{i}+(K_{sat}X''_{d}+X_{q}-X''_{q})I_{r}} + {K_{sat}V_{rterm}+K_{sat}R_{a}I_{r}-(K_{sat}X''_{d}+X_{q}-X''_{q})I_{i}} \right) ``` where ```math From a028a6e49f1c1bc5c26b3d4656cc2fbb485213db Mon Sep 17 00:00:00 2001 From: Slaven Peles Date: Tue, 11 Mar 2025 20:45:06 -0400 Subject: [PATCH 15/20] Export all symbols when using MSVSC to build GridKit. --- CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 770855b9..a8de08e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,11 @@ set(PACKAGE_VERSION_PATCH "7") set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}") +# TODO: Probably beter to set a debug interface target +# set(CMAKE_CXX_FLAGS_DEBUG "-Wall -O0 -g -DDEBUG") + +set(CMAKE_CXX_STANDARD 17) + # Ipopt support is disabled by default option(GRIDKIT_ENABLE_IPOPT "Enable Ipopt support" OFF) @@ -33,6 +38,7 @@ option(GRIDKIT_ENABLE_SUNDIALS_SPARSE "Enable SUNDIALS sparse linear solvers" ON option(GRIDKIT_ENABLE_ENZYME "Enable automatic differentiation with Enzyme" OFF) set(CMAKE_MACOSX_RPATH 1) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) @@ -57,11 +63,6 @@ if("${isSystemDir}" STREQUAL "-1") endif("${isSystemDir}" STREQUAL "-1") -# TODO: Probably beter to set a debug interface target -set(CMAKE_CXX_FLAGS_DEBUG "-Wall -O0 -g -DDEBUG") - -set(CMAKE_CXX_STANDARD 17) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) option(GRIDKIT_BUILD_SHARED "Build shared libraries" ON) From ff9464dc490e4d9e781e14fb949b9241c0f7e990 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 10 Apr 2025 05:55:12 -0400 Subject: [PATCH 16/20] Added classical generator --- .../SynchronousMachine/GENROUwS/GEN2.cpp | 1 + .../SynchronousMachine/GENROUwS/GEN2.hpp | 211 ++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.cpp create mode 100644 src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.hpp diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.cpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.cpp new file mode 100644 index 00000000..a58c4d30 --- /dev/null +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.cpp @@ -0,0 +1 @@ +#include "GEN2.hpp" \ No newline at end of file diff --git a/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.hpp b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.hpp new file mode 100644 index 00000000..612b598f --- /dev/null +++ b/src/Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.hpp @@ -0,0 +1,211 @@ +/* GENROU Component - Adam Birchfield */ +#pragma once + +#define _USE_MATH_DEFINES +#include +#include + +namespace GridKit +{ + namespace PhasorDynamics + { + using ComponentT = Component; + using BaseBusT = BusBase; + + class GEN2 : public ComponentT + { + using ComponentT::alpha_; + using ComponentT::f_; + using ComponentT::fB_; + using ComponentT::g_; + using ComponentT::gB_; + using ComponentT::nnz_; + using ComponentT::param_; + using ComponentT::size_; + using ComponentT::tag_; + using ComponentT::time_; + using ComponentT::y_; + using ComponentT::yB_; + using ComponentT::yp_; + using ComponentT::ypB_; + + public: + GEN2(BaseBusT* bus, int unit_id) + : bus_(bus), + unit_id_(unit_id), + busID_(0), + H_(3), + D_(0), + Ra_(0), + Xdp_(0.2) + { + size_ = 5; + set_derived_params(); + } + + GEN2(BaseBusT* bus, + int unit_id, + double H, + double D, + double Ra, + double Xdp, + double pmech_, + double ep_) + : bus_(bus), + unit_id_(unit_id), + busID_(0), + H_(H), + D_(D), + Ra_(Ra), + Xdp_(Xdp), + pmech(pmech_), + ep(ep_) + { + size_ = 5; + set_derived_params(); + } + + void set_derived_params() + { + g = Ra_ / (Ra_ * Ra_ + Xdp_ * Xdp_); + b = -Xdp_ / (Ra_ * Ra_ + Xdp_ * Xdp_); + } + + ~GEN2() + { + } + + int allocate() override + { + f_.resize(size_); + y_.resize(size_); + yp_.resize(size_); + tag_.resize(size_); + fB_.resize(size_); + yB_.resize(size_); + ypB_.resize(size_); + return 0; + } + + int initialize() override + { + return 0; + } + + int tagDifferentiable() override + { + return 0; + } + + int evaluateResidual() override + { + /* Read variables */ + double delta = y_[0]; + double omega = y_[1]; + double telec = y_[2]; + double ir = y_[3]; + double ii = y_[4]; + + /* Read derivatives */ + double delta_dot = yp_[0]; + double omega_dot = yp_[1]; + + /* 6 GENROU differential equations */ + f_[0] = delta_dot - omega * (2 * M_PI * 60); + f_[1] = omega_dot - (1.0 / (2 * H_)) * ((pmech - D_ * omega) / (1 + omega) - telec); + + /* 11 GENROU algebraic equations */ + f_[2] = telec - (1.0/(1.0 + omega))*(g*ep*ep - ep*(cos(delta)*(g*Vr() - b*Vi()) + sin(delta)*(b*Vr() + g*Vi()))); + + f_[3] = ir + g*Vr() - b * Vi() - ep*(g*cos(delta) -b*sin(delta)); + f_[4] = ii + b*Vr() + g * Vi() - ep*(b*cos(delta) + g*sin(delta)); + + + /* Current balance */ + Ir() += - (g*Vr() - b * Vi() - ep*(g*cos(delta) -b*sin(delta))); + Ii() += - (b*Vr() + g * Vi() - ep*(b*cos(delta) + g*sin(delta))); + + // printf("GENROU residual\n"); + // for (int i = 0 ; i < 21; ++i) printf("%d: %g\n", i, f_[i]); + + // printf("GENROU inr %g Vr %g B %g Vi %g G %g\n", inr, Vr(), B_, Vi(), G_); + // printf("GENROU Ii = %g\n", inr - Vr()*B_ - Vi()*G_); + + return 0; + } + + int evaluateJacobian() override + { + /* TODO */ + return 0; + } + + /* Don't know what to do with any of these */ + int evaluateIntegrand() override + { + return 0; + } + + int initializeAdjoint() override + { + return 0; + } + + int evaluateAdjointResidual() override + { + return 0; + } + + int evaluateAdjointIntegrand() override + { + return 0; + } + + void updateTime(double t, double a) override + { + } + + private: + double& Vr() + { + return bus_->Vr(); + } + + double& Vi() + { + return bus_->Vi(); + } + + double& Ir() + { + return bus_->Ir(); + } + + double& Ii() + { + return bus_->Ii(); + } + + private: + /* Identification */ + BaseBusT* bus_; + const int busID_; + int unit_id_; + + /* Input parameters */ + double H_; + double D_; + double Ra_; + double Xdp_; + + /* Derivied parameters */ + double g; + double b; + + /* Setpoints for control variables (determined at initialization) */ + double pmech; + double ep; + }; + + } // namespace PhasorDynamics +} // namespace GridKit From fa4b3236e24c49b866dae5e8fd2d0c4257f9be5c Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 10 Apr 2025 05:58:23 -0400 Subject: [PATCH 17/20] Added 3Bus to the list of examples --- examples/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6e0382b7..1ee4d17b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,8 @@ if(TARGET SUNDIALS::idas) add_subdirectory(Microgrid) add_subdirectory(ScaleMicrogrid) add_subdirectory(PhasorDynamics) + add_subdirectory(Bus3Phasor) + add_subdirectory(Gen2Example) if(GRIDKIT_ENABLE_IPOPT) add_subdirectory(DynamicConstrainedOpt) add_subdirectory(GenConstLoad) From 159ccdc4262108cce928a16bbe1a51122efe1dac Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 10 Apr 2025 06:02:44 -0400 Subject: [PATCH 18/20] Added CMake file in 3Bus example folder --- examples/Bus3Phasor/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/Bus3Phasor/CMakeLists.txt diff --git a/examples/Bus3Phasor/CMakeLists.txt b/examples/Bus3Phasor/CMakeLists.txt new file mode 100644 index 00000000..a2b06cde --- /dev/null +++ b/examples/Bus3Phasor/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Bus3Example) \ No newline at end of file From 8d7f00f24a6c0f054c5cb2ad7c1e2568f09213b8 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 10 Apr 2025 06:04:59 -0400 Subject: [PATCH 19/20] Added an example for the second order classical generator --- examples/Gen2Example/CMakeLists.txt | 1 + examples/Gen2Example/Example2/CMakeLists.txt | 9 +++ examples/Gen2Example/Example2/example.cpp | 81 ++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 examples/Gen2Example/CMakeLists.txt create mode 100644 examples/Gen2Example/Example2/CMakeLists.txt create mode 100644 examples/Gen2Example/Example2/example.cpp diff --git a/examples/Gen2Example/CMakeLists.txt b/examples/Gen2Example/CMakeLists.txt new file mode 100644 index 00000000..ca435520 --- /dev/null +++ b/examples/Gen2Example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Example2) \ No newline at end of file diff --git a/examples/Gen2Example/Example2/CMakeLists.txt b/examples/Gen2Example/Example2/CMakeLists.txt new file mode 100644 index 00000000..49d923e5 --- /dev/null +++ b/examples/Gen2Example/Example2/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(gen2_example example.cpp) +target_link_libraries(gen2_example + GRIDKIT::bus + SUNDIALS::sunlinsolklu + SUNDIALS::core + SUNDIALS::ida + SUNDIALS::idas + SUNDIALS::sunmatrixdense) +install(TARGETS gen2_example RUNTIME DESTINATION bin) diff --git a/examples/Gen2Example/Example2/example.cpp b/examples/Gen2Example/Example2/example.cpp new file mode 100644 index 00000000..62797ab8 --- /dev/null +++ b/examples/Gen2Example/Example2/example.cpp @@ -0,0 +1,81 @@ +#include +#define _USE_MATH_DEFINES +#include +#include + +// #include +#include +#include +#include +#include +#include + +#include "Model/PhasorDynamics/Branch/Branch.cpp" +#include "Model/PhasorDynamics/Branch/Branch.hpp" +#include "Model/PhasorDynamics/Bus/Bus.cpp" +#include "Model/PhasorDynamics/Bus/Bus.hpp" +#include "Model/PhasorDynamics/Load/Load.cpp" +#include "Model/PhasorDynamics/Load/Load.hpp" +#include "Model/PhasorDynamics/Bus/BusInfinite.cpp" +#include "Model/PhasorDynamics/Bus/BusInfinite.hpp" +#include "Model/PhasorDynamics/BusFault/BusFault.hpp" +#include "Model/PhasorDynamics/SynchronousMachine/GENROUwS/GEN2.hpp" +#include "Model/PhasorDynamics/SystemModel.hpp" +#include "Solver/Dynamic/Ida.cpp" +#include "Solver/Dynamic/Ida.hpp" + +#define _CRT_SECURE_NO_WARNINGS + +int main() +{ + using namespace GridKit::PhasorDynamics; + using namespace AnalysisManager::Sundials; + + printf("Example 1 version GENERATION 2\n"); + + SystemModel sys; + Bus bus1(0.9949877346411762, 0.09999703952427966); + BusInfinite bus2(1.0, 0.0); + Branch branch(&bus1, &bus2, 0.0, 0.1, 0, 0); + GEN2 gen(&bus1, 1,3, 0, 0, 0.2, 0.1, 0.2); + sys.allocate(); + + + /* Connect everything together */ + sys.addBus(&bus1); + sys.addBus(&bus2); + sys.addComponent(&branch); + sys.addComponent(&gen); + + sys.allocate(); + + + double dt = 1.0 / 4.0 / 60.0; + + /* Output file header */ + FILE* f = fopen("example1_v2_results.csv", "w"); + if (!f) + printf("ERROR writing to output file!\n"); + fprintf(f, "%s,%s", "t", "IDA Return Value"); + for (int i = 0; i < sys.size(); ++i) + fprintf(f, ",Y[%d]", i); + for (int i = 0; i < sys.size(); ++i) + fprintf(f, ",Yp[%d]", i); + fprintf(f, "\n"); + + /* Set up simulation */ + Ida ida(&sys); + ida.configureSimulation(); + + /* Run simulation */ + double start = (double) clock(); + ida.printOutputF(0, 0, f); + ida.initializeSimulation(0.0, false); + ida.runSimulationFixed(0.0, dt, 1.0, f); + + + printf("Complete in %.4g seconds\n", (clock() - start) / CLOCKS_PER_SEC); + fclose(f); + + return 0; +} \ No newline at end of file From 85d15fde71b8c4bb4046b8d2033764d5d287c6c7 Mon Sep 17 00:00:00 2001 From: abdourahmanbarry Date: Thu, 10 Apr 2025 06:14:11 -0400 Subject: [PATCH 20/20] Added a CMake file for 3Bus example --- examples/Bus3Phasor/Bus3Example/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 examples/Bus3Phasor/Bus3Example/CMakeLists.txt diff --git a/examples/Bus3Phasor/Bus3Example/CMakeLists.txt b/examples/Bus3Phasor/Bus3Example/CMakeLists.txt new file mode 100644 index 00000000..afd9f579 --- /dev/null +++ b/examples/Bus3Phasor/Bus3Example/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable(bus3grid bus3phasor.cpp) +target_link_libraries(bus3grid + GRIDKIT::bus + SUNDIALS::sunlinsolklu + SUNDIALS::core + SUNDIALS::ida + SUNDIALS::idas + SUNDIALS::sunmatrixdense) +install(TARGETS bus3grid RUNTIME DESTINATION bin) \ No newline at end of file