1 static char help[] = "Power grid stability analysis of WECC 9 bus system.\n\ 2 This example is based on the 9-bus (node) example given in the book Power\n\ 3 Systems Dynamics and Stability (Chapter 7) by P. Sauer and M. A. Pai.\n\ 4 The power grid in this example consists of 9 buses (nodes), 3 generators,\n\ 5 3 loads, and 9 transmission lines. The network equations are written\n\ 6 in current balance form using rectangular coordinates.\n\n"; 7 8 /* 9 The equations for the stability analysis are described by the DAE 10 11 \dot{x} = f(x,y,t) 12 0 = g(x,y,t) 13 14 where the generators are described by differential equations, while the algebraic 15 constraints define the network equations. 16 17 The generators are modeled with a 4th order differential equation describing the electrical 18 and mechanical dynamics. Each generator also has an exciter system modeled by 3rd order 19 diff. eqns. describing the exciter, voltage regulator, and the feedback stabilizer 20 mechanism. 21 22 The network equations are described by nodal current balance equations. 23 I(x,y) - Y*V = 0 24 25 where: 26 I(x,y) is the current injected from generators and loads. 27 Y is the admittance matrix, and 28 V is the voltage vector 29 */ 30 31 /* 32 Include "petscts.h" so that we can use TS solvers. Note that this 33 file automatically includes: 34 petscsys.h - base PETSc routines petscvec.h - vectors 35 petscmat.h - matrices 36 petscis.h - index sets petscksp.h - Krylov subspace methods 37 petscviewer.h - viewers petscpc.h - preconditioners 38 petscksp.h - linear solvers 39 */ 40 41 #include <petscts.h> 42 #include <petscdm.h> 43 #include <petscdmda.h> 44 #include <petscdmcomposite.h> 45 46 #define freq 60 47 #define w_s (2 * PETSC_PI * freq) 48 49 /* Sizes and indices */ 50 const PetscInt nbus = 9; /* Number of network buses */ 51 const PetscInt ngen = 3; /* Number of generators */ 52 const PetscInt nload = 3; /* Number of loads */ 53 const PetscInt gbus[3] = {0, 1, 2}; /* Buses at which generators are incident */ 54 const PetscInt lbus[3] = {4, 5, 7}; /* Buses at which loads are incident */ 55 56 /* Generator real and reactive powers (found via loadflow) */ 57 const PetscScalar PG[3] = {0.716786142395021, 1.630000000000000, 0.850000000000000}; 58 const PetscScalar QG[3] = {0.270702180178785, 0.066120127797275, -0.108402221791588}; 59 /* Generator constants */ 60 const PetscScalar H[3] = {23.64, 6.4, 3.01}; /* Inertia constant */ 61 const PetscScalar Rs[3] = {0.0, 0.0, 0.0}; /* Stator Resistance */ 62 const PetscScalar Xd[3] = {0.146, 0.8958, 1.3125}; /* d-axis reactance */ 63 const PetscScalar Xdp[3] = {0.0608, 0.1198, 0.1813}; /* d-axis transient reactance */ 64 const PetscScalar Xq[3] = {0.4360, 0.8645, 1.2578}; /* q-axis reactance Xq(1) set to 0.4360, value given in text 0.0969 */ 65 const PetscScalar Xqp[3] = {0.0969, 0.1969, 0.25}; /* q-axis transient reactance */ 66 const PetscScalar Td0p[3] = {8.96, 6.0, 5.89}; /* d-axis open circuit time constant */ 67 const PetscScalar Tq0p[3] = {0.31, 0.535, 0.6}; /* q-axis open circuit time constant */ 68 PetscScalar M[3]; /* M = 2*H/w_s */ 69 PetscScalar D[3]; /* D = 0.1*M */ 70 71 PetscScalar TM[3]; /* Mechanical Torque */ 72 /* Exciter system constants */ 73 const PetscScalar KA[3] = {20.0, 20.0, 20.0}; /* Voltage regulartor gain constant */ 74 const PetscScalar TA[3] = {0.2, 0.2, 0.2}; /* Voltage regulator time constant */ 75 const PetscScalar KE[3] = {1.0, 1.0, 1.0}; /* Exciter gain constant */ 76 const PetscScalar TE[3] = {0.314, 0.314, 0.314}; /* Exciter time constant */ 77 const PetscScalar KF[3] = {0.063, 0.063, 0.063}; /* Feedback stabilizer gain constant */ 78 const PetscScalar TF[3] = {0.35, 0.35, 0.35}; /* Feedback stabilizer time constant */ 79 const PetscScalar k1[3] = {0.0039, 0.0039, 0.0039}; 80 const PetscScalar k2[3] = {1.555, 1.555, 1.555}; /* k1 and k2 for calculating the saturation function SE = k1*exp(k2*Efd) */ 81 const PetscScalar VRMIN[3] = {-4.0, -4.0, -4.0}; 82 const PetscScalar VRMAX[3] = {7.0, 7.0, 7.0}; 83 PetscInt VRatmin[3]; 84 PetscInt VRatmax[3]; 85 86 PetscScalar Vref[3]; 87 /* Load constants 88 We use a composite load model that describes the load and reactive powers at each time instant as follows 89 P(t) = \sum\limits_{i=0}^ld_nsegsp \ld_alphap_i*P_D0(\frac{V_m(t)}{V_m0})^\ld_betap_i 90 Q(t) = \sum\limits_{i=0}^ld_nsegsq \ld_alphaq_i*Q_D0(\frac{V_m(t)}{V_m0})^\ld_betaq_i 91 where 92 ld_nsegsp,ld_nsegsq - Number of individual load models for real and reactive power loads 93 ld_alphap,ld_alphap - Percentage contribution (weights) or loads 94 P_D0 - Real power load 95 Q_D0 - Reactive power load 96 V_m(t) - Voltage magnitude at time t 97 V_m0 - Voltage magnitude at t = 0 98 ld_betap, ld_betaq - exponents describing the load model for real and reactive part 99 100 Note: All loads have the same characteristic currently. 101 */ 102 const PetscScalar PD0[3] = {1.25, 0.9, 1.0}; 103 const PetscScalar QD0[3] = {0.5, 0.3, 0.35}; 104 const PetscInt ld_nsegsp[3] = {3, 3, 3}; 105 const PetscScalar ld_alphap[3] = {1.0, 0.0, 0.0}; 106 const PetscScalar ld_betap[3] = {2.0, 1.0, 0.0}; 107 const PetscInt ld_nsegsq[3] = {3, 3, 3}; 108 const PetscScalar ld_alphaq[3] = {1.0, 0.0, 0.0}; 109 const PetscScalar ld_betaq[3] = {2.0, 1.0, 0.0}; 110 111 typedef struct { 112 DM dmgen, dmnet; /* DMs to manage generator and network subsystem */ 113 DM dmpgrid; /* Composite DM to manage the entire power grid */ 114 Mat Ybus; /* Network admittance matrix */ 115 Vec V0; /* Initial voltage vector (Power flow solution) */ 116 PetscReal tfaulton, tfaultoff; /* Fault on and off times */ 117 PetscInt faultbus; /* Fault bus */ 118 PetscScalar Rfault; 119 PetscReal t0, tmax; 120 PetscInt neqs_gen, neqs_net, neqs_pgrid; 121 Mat Sol; /* Matrix to save solution at each time step */ 122 PetscInt stepnum; 123 PetscReal t; 124 SNES snes_alg; 125 IS is_diff; /* indices for differential equations */ 126 IS is_alg; /* indices for algebraic equations */ 127 PetscBool setisdiff; /* TS computes truncation error based only on the differential variables */ 128 PetscBool semiexplicit; /* If the flag is set then a semi-explicit method is used using TSRK */ 129 } Userctx; 130 131 /* 132 The first two events are for fault on and off, respectively. The following events are 133 to check the min/max limits on the state variable VR. A non windup limiter is used for 134 the VR limits. 135 */ 136 PetscErrorCode EventFunction(TS ts, PetscReal t, Vec X, PetscScalar *fvalue, void *ctx) 137 { 138 Userctx *user = (Userctx *)ctx; 139 Vec Xgen, Xnet; 140 PetscInt i, idx = 0; 141 const PetscScalar *xgen, *xnet; 142 PetscScalar Efd, RF, VR, Vr, Vi, Vm; 143 144 PetscFunctionBegin; 145 146 PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 147 PetscCall(DMCompositeScatter(user->dmpgrid, X, Xgen, Xnet)); 148 149 PetscCall(VecGetArrayRead(Xgen, &xgen)); 150 PetscCall(VecGetArrayRead(Xnet, &xnet)); 151 152 /* Event for fault-on time */ 153 fvalue[0] = t - user->tfaulton; 154 /* Event for fault-off time */ 155 fvalue[1] = t - user->tfaultoff; 156 157 for (i = 0; i < ngen; i++) { 158 Efd = xgen[idx + 6]; 159 RF = xgen[idx + 7]; 160 VR = xgen[idx + 8]; 161 162 Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */ 163 Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */ 164 Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi); 165 166 if (!VRatmax[i]) { 167 fvalue[2 + 2 * i] = VRMAX[i] - VR; 168 } else { 169 fvalue[2 + 2 * i] = (VR - KA[i] * RF + KA[i] * KF[i] * Efd / TF[i] - KA[i] * (Vref[i] - Vm)) / TA[i]; 170 } 171 if (!VRatmin[i]) { 172 fvalue[2 + 2 * i + 1] = VRMIN[i] - VR; 173 } else { 174 fvalue[2 + 2 * i + 1] = (VR - KA[i] * RF + KA[i] * KF[i] * Efd / TF[i] - KA[i] * (Vref[i] - Vm)) / TA[i]; 175 } 176 idx = idx + 9; 177 } 178 PetscCall(VecRestoreArrayRead(Xgen, &xgen)); 179 PetscCall(VecRestoreArrayRead(Xnet, &xnet)); 180 181 PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 182 183 PetscFunctionReturn(PETSC_SUCCESS); 184 } 185 186 PetscErrorCode PostEventFunction(TS ts, PetscInt nevents, PetscInt event_list[], PetscReal t, Vec X, PetscBool forwardsolve, void *ctx) 187 { 188 Userctx *user = (Userctx *)ctx; 189 Vec Xgen, Xnet; 190 PetscScalar *xgen, *xnet; 191 PetscInt row_loc, col_loc; 192 PetscScalar val; 193 PetscInt i, idx = 0, event_num; 194 PetscScalar fvalue; 195 PetscScalar Efd, RF, VR; 196 PetscScalar Vr, Vi, Vm; 197 198 PetscFunctionBegin; 199 200 PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 201 PetscCall(DMCompositeScatter(user->dmpgrid, X, Xgen, Xnet)); 202 203 PetscCall(VecGetArray(Xgen, &xgen)); 204 PetscCall(VecGetArray(Xnet, &xnet)); 205 206 for (i = 0; i < nevents; i++) { 207 if (event_list[i] == 0) { 208 /* Apply disturbance - resistive fault at user->faultbus */ 209 /* This is done by adding shunt conductance to the diagonal location 210 in the Ybus matrix */ 211 row_loc = 2 * user->faultbus; 212 col_loc = 2 * user->faultbus + 1; /* Location for G */ 213 val = 1 / user->Rfault; 214 PetscCall(MatSetValues(user->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES)); 215 row_loc = 2 * user->faultbus + 1; 216 col_loc = 2 * user->faultbus; /* Location for G */ 217 val = 1 / user->Rfault; 218 PetscCall(MatSetValues(user->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES)); 219 220 PetscCall(MatAssemblyBegin(user->Ybus, MAT_FINAL_ASSEMBLY)); 221 PetscCall(MatAssemblyEnd(user->Ybus, MAT_FINAL_ASSEMBLY)); 222 223 /* Solve the algebraic equations */ 224 PetscCall(SNESSolve(user->snes_alg, NULL, X)); 225 } else if (event_list[i] == 1) { 226 /* Remove the fault */ 227 row_loc = 2 * user->faultbus; 228 col_loc = 2 * user->faultbus + 1; 229 val = -1 / user->Rfault; 230 PetscCall(MatSetValues(user->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES)); 231 row_loc = 2 * user->faultbus + 1; 232 col_loc = 2 * user->faultbus; 233 val = -1 / user->Rfault; 234 PetscCall(MatSetValues(user->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES)); 235 236 PetscCall(MatAssemblyBegin(user->Ybus, MAT_FINAL_ASSEMBLY)); 237 PetscCall(MatAssemblyEnd(user->Ybus, MAT_FINAL_ASSEMBLY)); 238 239 /* Solve the algebraic equations */ 240 PetscCall(SNESSolve(user->snes_alg, NULL, X)); 241 242 /* Check the VR derivatives and reset flags if needed */ 243 for (i = 0; i < ngen; i++) { 244 Efd = xgen[idx + 6]; 245 RF = xgen[idx + 7]; 246 VR = xgen[idx + 8]; 247 248 Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */ 249 Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */ 250 Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi); 251 252 if (VRatmax[i]) { 253 fvalue = (VR - KA[i] * RF + KA[i] * KF[i] * Efd / TF[i] - KA[i] * (Vref[i] - Vm)) / TA[i]; 254 if (fvalue < 0) { 255 VRatmax[i] = 0; 256 PetscCall(PetscPrintf(PETSC_COMM_SELF, "VR[%" PetscInt_FMT "]: dVR_dt went negative on fault clearing at time %g\n", i, (double)t)); 257 } 258 } 259 if (VRatmin[i]) { 260 fvalue = (VR - KA[i] * RF + KA[i] * KF[i] * Efd / TF[i] - KA[i] * (Vref[i] - Vm)) / TA[i]; 261 262 if (fvalue > 0) { 263 VRatmin[i] = 0; 264 PetscCall(PetscPrintf(PETSC_COMM_SELF, "VR[%" PetscInt_FMT "]: dVR_dt went positive on fault clearing at time %g\n", i, (double)t)); 265 } 266 } 267 idx = idx + 9; 268 } 269 } else { 270 idx = (event_list[i] - 2) / 2; 271 event_num = (event_list[i] - 2) % 2; 272 if (event_num == 0) { /* Max VR */ 273 if (!VRatmax[idx]) { 274 VRatmax[idx] = 1; 275 PetscCall(PetscPrintf(PETSC_COMM_SELF, "VR[%" PetscInt_FMT "]: hit upper limit at time %g\n", idx, (double)t)); 276 } else { 277 VRatmax[idx] = 0; 278 PetscCall(PetscPrintf(PETSC_COMM_SELF, "VR[%" PetscInt_FMT "]: freeing variable as dVR_dt is negative at time %g\n", idx, (double)t)); 279 } 280 } else { 281 if (!VRatmin[idx]) { 282 VRatmin[idx] = 1; 283 PetscCall(PetscPrintf(PETSC_COMM_SELF, "VR[%" PetscInt_FMT "]: hit lower limit at time %g\n", idx, (double)t)); 284 } else { 285 VRatmin[idx] = 0; 286 PetscCall(PetscPrintf(PETSC_COMM_SELF, "VR[%" PetscInt_FMT "]: freeing variable as dVR_dt is positive at time %g\n", idx, (double)t)); 287 } 288 } 289 } 290 } 291 292 PetscCall(VecRestoreArray(Xgen, &xgen)); 293 PetscCall(VecRestoreArray(Xnet, &xnet)); 294 295 PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 296 297 PetscFunctionReturn(PETSC_SUCCESS); 298 } 299 300 /* Converts from machine frame (dq) to network (phase a real,imag) reference frame */ 301 PetscErrorCode dq2ri(PetscScalar Fd, PetscScalar Fq, PetscScalar delta, PetscScalar *Fr, PetscScalar *Fi) 302 { 303 PetscFunctionBegin; 304 *Fr = Fd * PetscSinScalar(delta) + Fq * PetscCosScalar(delta); 305 *Fi = -Fd * PetscCosScalar(delta) + Fq * PetscSinScalar(delta); 306 PetscFunctionReturn(PETSC_SUCCESS); 307 } 308 309 /* Converts from network frame ([phase a real,imag) to machine (dq) reference frame */ 310 PetscErrorCode ri2dq(PetscScalar Fr, PetscScalar Fi, PetscScalar delta, PetscScalar *Fd, PetscScalar *Fq) 311 { 312 PetscFunctionBegin; 313 *Fd = Fr * PetscSinScalar(delta) - Fi * PetscCosScalar(delta); 314 *Fq = Fr * PetscCosScalar(delta) + Fi * PetscSinScalar(delta); 315 PetscFunctionReturn(PETSC_SUCCESS); 316 } 317 318 /* Saves the solution at each time to a matrix */ 319 PetscErrorCode SaveSolution(TS ts) 320 { 321 Userctx *user; 322 Vec X; 323 const PetscScalar *x; 324 PetscScalar *mat; 325 PetscInt idx; 326 PetscReal t; 327 328 PetscFunctionBegin; 329 PetscCall(TSGetApplicationContext(ts, &user)); 330 PetscCall(TSGetTime(ts, &t)); 331 PetscCall(TSGetSolution(ts, &X)); 332 idx = user->stepnum * (user->neqs_pgrid + 1); 333 PetscCall(MatDenseGetArray(user->Sol, &mat)); 334 PetscCall(VecGetArrayRead(X, &x)); 335 mat[idx] = t; 336 PetscCall(PetscArraycpy(mat + idx + 1, x, user->neqs_pgrid)); 337 PetscCall(MatDenseRestoreArray(user->Sol, &mat)); 338 PetscCall(VecRestoreArrayRead(X, &x)); 339 user->stepnum++; 340 PetscFunctionReturn(PETSC_SUCCESS); 341 } 342 343 PetscErrorCode SetInitialGuess(Vec X, Userctx *user) 344 { 345 Vec Xgen, Xnet; 346 PetscScalar *xgen; 347 const PetscScalar *xnet; 348 PetscInt i, idx = 0; 349 PetscScalar Vr, Vi, IGr, IGi, Vm, Vm2; 350 PetscScalar Eqp, Edp, delta; 351 PetscScalar Efd, RF, VR; /* Exciter variables */ 352 PetscScalar Id, Iq; /* Generator dq axis currents */ 353 PetscScalar theta, Vd, Vq, SE; 354 355 PetscFunctionBegin; 356 M[0] = 2 * H[0] / w_s; 357 M[1] = 2 * H[1] / w_s; 358 M[2] = 2 * H[2] / w_s; 359 D[0] = 0.1 * M[0]; 360 D[1] = 0.1 * M[1]; 361 D[2] = 0.1 * M[2]; 362 363 PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 364 365 /* Network subsystem initialization */ 366 PetscCall(VecCopy(user->V0, Xnet)); 367 368 /* Generator subsystem initialization */ 369 PetscCall(VecGetArrayWrite(Xgen, &xgen)); 370 PetscCall(VecGetArrayRead(Xnet, &xnet)); 371 372 for (i = 0; i < ngen; i++) { 373 Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */ 374 Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */ 375 Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi); 376 Vm2 = Vm * Vm; 377 IGr = (Vr * PG[i] + Vi * QG[i]) / Vm2; 378 IGi = (Vi * PG[i] - Vr * QG[i]) / Vm2; 379 380 delta = PetscAtan2Real(Vi + Xq[i] * IGr, Vr - Xq[i] * IGi); /* Machine angle */ 381 382 theta = PETSC_PI / 2.0 - delta; 383 384 Id = IGr * PetscCosScalar(theta) - IGi * PetscSinScalar(theta); /* d-axis stator current */ 385 Iq = IGr * PetscSinScalar(theta) + IGi * PetscCosScalar(theta); /* q-axis stator current */ 386 387 Vd = Vr * PetscCosScalar(theta) - Vi * PetscSinScalar(theta); 388 Vq = Vr * PetscSinScalar(theta) + Vi * PetscCosScalar(theta); 389 390 Edp = Vd + Rs[i] * Id - Xqp[i] * Iq; /* d-axis transient EMF */ 391 Eqp = Vq + Rs[i] * Iq + Xdp[i] * Id; /* q-axis transient EMF */ 392 393 TM[i] = PG[i]; 394 395 /* The generator variables are ordered as [Eqp,Edp,delta,w,Id,Iq] */ 396 xgen[idx] = Eqp; 397 xgen[idx + 1] = Edp; 398 xgen[idx + 2] = delta; 399 xgen[idx + 3] = w_s; 400 401 idx = idx + 4; 402 403 xgen[idx] = Id; 404 xgen[idx + 1] = Iq; 405 406 idx = idx + 2; 407 408 /* Exciter */ 409 Efd = Eqp + (Xd[i] - Xdp[i]) * Id; 410 SE = k1[i] * PetscExpScalar(k2[i] * Efd); 411 VR = KE[i] * Efd + SE; 412 RF = KF[i] * Efd / TF[i]; 413 414 xgen[idx] = Efd; 415 xgen[idx + 1] = RF; 416 xgen[idx + 2] = VR; 417 418 Vref[i] = Vm + (VR / KA[i]); 419 420 VRatmin[i] = VRatmax[i] = 0; 421 422 idx = idx + 3; 423 } 424 PetscCall(VecRestoreArrayWrite(Xgen, &xgen)); 425 PetscCall(VecRestoreArrayRead(Xnet, &xnet)); 426 427 /* PetscCall(VecView(Xgen,0)); */ 428 PetscCall(DMCompositeGather(user->dmpgrid, INSERT_VALUES, X, Xgen, Xnet)); 429 PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 430 PetscFunctionReturn(PETSC_SUCCESS); 431 } 432 433 /* Computes F = [f(x,y);g(x,y)] */ 434 PetscErrorCode ResidualFunction(Vec X, Vec F, Userctx *user) 435 { 436 Vec Xgen, Xnet, Fgen, Fnet; 437 const PetscScalar *xgen, *xnet; 438 PetscScalar *fgen, *fnet; 439 PetscInt i, idx = 0; 440 PetscScalar Vr, Vi, Vm, Vm2; 441 PetscScalar Eqp, Edp, delta, w; /* Generator variables */ 442 PetscScalar Efd, RF, VR; /* Exciter variables */ 443 PetscScalar Id, Iq; /* Generator dq axis currents */ 444 PetscScalar Vd, Vq, SE; 445 PetscScalar IGr, IGi, IDr, IDi; 446 PetscScalar Zdq_inv[4], det; 447 PetscScalar PD, QD, Vm0, *v0; 448 PetscInt k; 449 450 PetscFunctionBegin; 451 PetscCall(VecZeroEntries(F)); 452 PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 453 PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Fgen, &Fnet)); 454 PetscCall(DMCompositeScatter(user->dmpgrid, X, Xgen, Xnet)); 455 PetscCall(DMCompositeScatter(user->dmpgrid, F, Fgen, Fnet)); 456 457 /* Network current balance residual IG + Y*V + IL = 0. Only YV is added here. 458 The generator current injection, IG, and load current injection, ID are added later 459 */ 460 /* Note that the values in Ybus are stored assuming the imaginary current balance 461 equation is ordered first followed by real current balance equation for each bus. 462 Thus imaginary current contribution goes in location 2*i, and 463 real current contribution in 2*i+1 464 */ 465 PetscCall(MatMult(user->Ybus, Xnet, Fnet)); 466 467 PetscCall(VecGetArrayRead(Xgen, &xgen)); 468 PetscCall(VecGetArrayRead(Xnet, &xnet)); 469 PetscCall(VecGetArrayWrite(Fgen, &fgen)); 470 PetscCall(VecGetArrayWrite(Fnet, &fnet)); 471 472 /* Generator subsystem */ 473 for (i = 0; i < ngen; i++) { 474 Eqp = xgen[idx]; 475 Edp = xgen[idx + 1]; 476 delta = xgen[idx + 2]; 477 w = xgen[idx + 3]; 478 Id = xgen[idx + 4]; 479 Iq = xgen[idx + 5]; 480 Efd = xgen[idx + 6]; 481 RF = xgen[idx + 7]; 482 VR = xgen[idx + 8]; 483 484 /* Generator differential equations */ 485 fgen[idx] = (-Eqp - (Xd[i] - Xdp[i]) * Id + Efd) / Td0p[i]; 486 fgen[idx + 1] = (-Edp + (Xq[i] - Xqp[i]) * Iq) / Tq0p[i]; 487 fgen[idx + 2] = w - w_s; 488 fgen[idx + 3] = (TM[i] - Edp * Id - Eqp * Iq - (Xqp[i] - Xdp[i]) * Id * Iq - D[i] * (w - w_s)) / M[i]; 489 490 Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */ 491 Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */ 492 493 PetscCall(ri2dq(Vr, Vi, delta, &Vd, &Vq)); 494 /* Algebraic equations for stator currents */ 495 det = Rs[i] * Rs[i] + Xdp[i] * Xqp[i]; 496 497 Zdq_inv[0] = Rs[i] / det; 498 Zdq_inv[1] = Xqp[i] / det; 499 Zdq_inv[2] = -Xdp[i] / det; 500 Zdq_inv[3] = Rs[i] / det; 501 502 fgen[idx + 4] = Zdq_inv[0] * (-Edp + Vd) + Zdq_inv[1] * (-Eqp + Vq) + Id; 503 fgen[idx + 5] = Zdq_inv[2] * (-Edp + Vd) + Zdq_inv[3] * (-Eqp + Vq) + Iq; 504 505 /* Add generator current injection to network */ 506 PetscCall(dq2ri(Id, Iq, delta, &IGr, &IGi)); 507 508 fnet[2 * gbus[i]] -= IGi; 509 fnet[2 * gbus[i] + 1] -= IGr; 510 511 Vm = PetscSqrtScalar(Vd * Vd + Vq * Vq); 512 513 SE = k1[i] * PetscExpScalar(k2[i] * Efd); 514 515 /* Exciter differential equations */ 516 fgen[idx + 6] = (-KE[i] * Efd - SE + VR) / TE[i]; 517 fgen[idx + 7] = (-RF + KF[i] * Efd / TF[i]) / TF[i]; 518 if (VRatmax[i]) fgen[idx + 8] = VR - VRMAX[i]; 519 else if (VRatmin[i]) fgen[idx + 8] = VRMIN[i] - VR; 520 else fgen[idx + 8] = (-VR + KA[i] * RF - KA[i] * KF[i] * Efd / TF[i] + KA[i] * (Vref[i] - Vm)) / TA[i]; 521 522 idx = idx + 9; 523 } 524 525 PetscCall(VecGetArray(user->V0, &v0)); 526 for (i = 0; i < nload; i++) { 527 Vr = xnet[2 * lbus[i]]; /* Real part of load bus voltage */ 528 Vi = xnet[2 * lbus[i] + 1]; /* Imaginary part of the load bus voltage */ 529 Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi); 530 Vm2 = Vm * Vm; 531 Vm0 = PetscSqrtScalar(v0[2 * lbus[i]] * v0[2 * lbus[i]] + v0[2 * lbus[i] + 1] * v0[2 * lbus[i] + 1]); 532 PD = QD = 0.0; 533 for (k = 0; k < ld_nsegsp[i]; k++) PD += ld_alphap[k] * PD0[i] * PetscPowScalar((Vm / Vm0), ld_betap[k]); 534 for (k = 0; k < ld_nsegsq[i]; k++) QD += ld_alphaq[k] * QD0[i] * PetscPowScalar((Vm / Vm0), ld_betaq[k]); 535 536 /* Load currents */ 537 IDr = (PD * Vr + QD * Vi) / Vm2; 538 IDi = (-QD * Vr + PD * Vi) / Vm2; 539 540 fnet[2 * lbus[i]] += IDi; 541 fnet[2 * lbus[i] + 1] += IDr; 542 } 543 PetscCall(VecRestoreArray(user->V0, &v0)); 544 545 PetscCall(VecRestoreArrayRead(Xgen, &xgen)); 546 PetscCall(VecRestoreArrayRead(Xnet, &xnet)); 547 PetscCall(VecRestoreArrayWrite(Fgen, &fgen)); 548 PetscCall(VecRestoreArrayWrite(Fnet, &fnet)); 549 550 PetscCall(DMCompositeGather(user->dmpgrid, INSERT_VALUES, F, Fgen, Fnet)); 551 PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 552 PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Fgen, &Fnet)); 553 PetscFunctionReturn(PETSC_SUCCESS); 554 } 555 556 /* f(x,y) 557 g(x,y) 558 */ 559 PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec X, Vec F, void *ctx) 560 { 561 Userctx *user = (Userctx *)ctx; 562 563 PetscFunctionBegin; 564 user->t = t; 565 PetscCall(ResidualFunction(X, F, user)); 566 PetscFunctionReturn(PETSC_SUCCESS); 567 } 568 569 /* f(x,y) - \dot{x} 570 g(x,y) = 0 571 */ 572 PetscErrorCode IFunction(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) 573 { 574 PetscScalar *f; 575 const PetscScalar *xdot; 576 PetscInt i; 577 578 PetscFunctionBegin; 579 PetscCall(RHSFunction(ts, t, X, F, ctx)); 580 PetscCall(VecScale(F, -1.0)); 581 PetscCall(VecGetArray(F, &f)); 582 PetscCall(VecGetArrayRead(Xdot, &xdot)); 583 for (i = 0; i < ngen; i++) { 584 f[9 * i] += xdot[9 * i]; 585 f[9 * i + 1] += xdot[9 * i + 1]; 586 f[9 * i + 2] += xdot[9 * i + 2]; 587 f[9 * i + 3] += xdot[9 * i + 3]; 588 f[9 * i + 6] += xdot[9 * i + 6]; 589 f[9 * i + 7] += xdot[9 * i + 7]; 590 f[9 * i + 8] += xdot[9 * i + 8]; 591 } 592 PetscCall(VecRestoreArray(F, &f)); 593 PetscCall(VecRestoreArrayRead(Xdot, &xdot)); 594 PetscFunctionReturn(PETSC_SUCCESS); 595 } 596 597 /* This function is used for solving the algebraic system only during fault on and 598 off times. It computes the entire F and then zeros out the part corresponding to 599 differential equations 600 F = [0;g(y)]; 601 */ 602 PetscErrorCode AlgFunction(SNES snes, Vec X, Vec F, void *ctx) 603 { 604 Userctx *user = (Userctx *)ctx; 605 PetscScalar *f; 606 PetscInt i; 607 608 PetscFunctionBegin; 609 PetscCall(ResidualFunction(X, F, user)); 610 PetscCall(VecGetArray(F, &f)); 611 for (i = 0; i < ngen; i++) { 612 f[9 * i] = 0; 613 f[9 * i + 1] = 0; 614 f[9 * i + 2] = 0; 615 f[9 * i + 3] = 0; 616 f[9 * i + 6] = 0; 617 f[9 * i + 7] = 0; 618 f[9 * i + 8] = 0; 619 } 620 PetscCall(VecRestoreArray(F, &f)); 621 PetscFunctionReturn(PETSC_SUCCESS); 622 } 623 624 PetscErrorCode PostStage(TS ts, PetscReal t, PetscInt i, Vec *X) 625 { 626 Userctx *user; 627 628 PetscFunctionBegin; 629 PetscCall(TSGetApplicationContext(ts, &user)); 630 PetscCall(SNESSolve(user->snes_alg, NULL, X[i])); 631 PetscFunctionReturn(PETSC_SUCCESS); 632 } 633 634 PetscErrorCode PostEvaluate(TS ts) 635 { 636 Userctx *user; 637 Vec X; 638 639 PetscFunctionBegin; 640 PetscCall(TSGetApplicationContext(ts, &user)); 641 PetscCall(TSGetSolution(ts, &X)); 642 PetscCall(SNESSolve(user->snes_alg, NULL, X)); 643 PetscFunctionReturn(PETSC_SUCCESS); 644 } 645 646 PetscErrorCode PreallocateJacobian(Mat J, Userctx *user) 647 { 648 PetscInt *d_nnz; 649 PetscInt i, idx = 0, start = 0; 650 PetscInt ncols; 651 652 PetscFunctionBegin; 653 PetscCall(PetscMalloc1(user->neqs_pgrid, &d_nnz)); 654 for (i = 0; i < user->neqs_pgrid; i++) d_nnz[i] = 0; 655 /* Generator subsystem */ 656 for (i = 0; i < ngen; i++) { 657 d_nnz[idx] += 3; 658 d_nnz[idx + 1] += 2; 659 d_nnz[idx + 2] += 2; 660 d_nnz[idx + 3] += 5; 661 d_nnz[idx + 4] += 6; 662 d_nnz[idx + 5] += 6; 663 664 d_nnz[user->neqs_gen + 2 * gbus[i]] += 3; 665 d_nnz[user->neqs_gen + 2 * gbus[i] + 1] += 3; 666 667 d_nnz[idx + 6] += 2; 668 d_nnz[idx + 7] += 2; 669 d_nnz[idx + 8] += 5; 670 671 idx = idx + 9; 672 } 673 start = user->neqs_gen; 674 675 for (i = 0; i < nbus; i++) { 676 PetscCall(MatGetRow(user->Ybus, 2 * i, &ncols, NULL, NULL)); 677 d_nnz[start + 2 * i] += ncols; 678 d_nnz[start + 2 * i + 1] += ncols; 679 PetscCall(MatRestoreRow(user->Ybus, 2 * i, &ncols, NULL, NULL)); 680 } 681 PetscCall(MatSeqAIJSetPreallocation(J, 0, d_nnz)); 682 PetscCall(PetscFree(d_nnz)); 683 PetscFunctionReturn(PETSC_SUCCESS); 684 } 685 686 /* 687 J = [df_dx, df_dy 688 dg_dx, dg_dy] 689 */ 690 PetscErrorCode ResidualJacobian(Vec X, Mat J, Mat B, void *ctx) 691 { 692 Userctx *user = (Userctx *)ctx; 693 Vec Xgen, Xnet; 694 const PetscScalar *xgen, *xnet; 695 PetscInt i, idx = 0; 696 PetscScalar Vr, Vi, Vm, Vm2; 697 PetscScalar Eqp, Edp, delta; /* Generator variables */ 698 PetscScalar Efd; 699 PetscScalar Id, Iq; /* Generator dq axis currents */ 700 PetscScalar Vd, Vq; 701 PetscScalar val[10]; 702 PetscInt row[2], col[10]; 703 PetscInt net_start = user->neqs_gen; 704 PetscScalar Zdq_inv[4], det; 705 PetscScalar dVd_dVr, dVd_dVi, dVq_dVr, dVq_dVi, dVd_ddelta, dVq_ddelta; 706 PetscScalar dIGr_ddelta, dIGi_ddelta, dIGr_dId, dIGr_dIq, dIGi_dId, dIGi_dIq; 707 PetscScalar dSE_dEfd; 708 PetscScalar dVm_dVd, dVm_dVq, dVm_dVr, dVm_dVi; 709 PetscInt ncols; 710 const PetscInt *cols; 711 const PetscScalar *yvals; 712 PetscInt k; 713 PetscScalar PD, QD, Vm0, Vm4; 714 const PetscScalar *v0; 715 PetscScalar dPD_dVr, dPD_dVi, dQD_dVr, dQD_dVi; 716 PetscScalar dIDr_dVr, dIDr_dVi, dIDi_dVr, dIDi_dVi; 717 718 PetscFunctionBegin; 719 PetscCall(MatZeroEntries(B)); 720 PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 721 PetscCall(DMCompositeScatter(user->dmpgrid, X, Xgen, Xnet)); 722 723 PetscCall(VecGetArrayRead(Xgen, &xgen)); 724 PetscCall(VecGetArrayRead(Xnet, &xnet)); 725 726 /* Generator subsystem */ 727 for (i = 0; i < ngen; i++) { 728 Eqp = xgen[idx]; 729 Edp = xgen[idx + 1]; 730 delta = xgen[idx + 2]; 731 Id = xgen[idx + 4]; 732 Iq = xgen[idx + 5]; 733 Efd = xgen[idx + 6]; 734 735 /* fgen[idx] = (-Eqp - (Xd[i] - Xdp[i])*Id + Efd)/Td0p[i]; */ 736 row[0] = idx; 737 col[0] = idx; 738 col[1] = idx + 4; 739 col[2] = idx + 6; 740 val[0] = -1 / Td0p[i]; 741 val[1] = -(Xd[i] - Xdp[i]) / Td0p[i]; 742 val[2] = 1 / Td0p[i]; 743 744 PetscCall(MatSetValues(J, 1, row, 3, col, val, INSERT_VALUES)); 745 746 /* fgen[idx+1] = (-Edp + (Xq[i] - Xqp[i])*Iq)/Tq0p[i]; */ 747 row[0] = idx + 1; 748 col[0] = idx + 1; 749 col[1] = idx + 5; 750 val[0] = -1 / Tq0p[i]; 751 val[1] = (Xq[i] - Xqp[i]) / Tq0p[i]; 752 PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES)); 753 754 /* fgen[idx+2] = w - w_s; */ 755 row[0] = idx + 2; 756 col[0] = idx + 2; 757 col[1] = idx + 3; 758 val[0] = 0; 759 val[1] = 1; 760 PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES)); 761 762 /* fgen[idx+3] = (TM[i] - Edp*Id - Eqp*Iq - (Xqp[i] - Xdp[i])*Id*Iq - D[i]*(w - w_s))/M[i]; */ 763 row[0] = idx + 3; 764 col[0] = idx; 765 col[1] = idx + 1; 766 col[2] = idx + 3; 767 col[3] = idx + 4; 768 col[4] = idx + 5; 769 val[0] = -Iq / M[i]; 770 val[1] = -Id / M[i]; 771 val[2] = -D[i] / M[i]; 772 val[3] = (-Edp - (Xqp[i] - Xdp[i]) * Iq) / M[i]; 773 val[4] = (-Eqp - (Xqp[i] - Xdp[i]) * Id) / M[i]; 774 PetscCall(MatSetValues(J, 1, row, 5, col, val, INSERT_VALUES)); 775 776 Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */ 777 Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */ 778 PetscCall(ri2dq(Vr, Vi, delta, &Vd, &Vq)); 779 780 det = Rs[i] * Rs[i] + Xdp[i] * Xqp[i]; 781 782 Zdq_inv[0] = Rs[i] / det; 783 Zdq_inv[1] = Xqp[i] / det; 784 Zdq_inv[2] = -Xdp[i] / det; 785 Zdq_inv[3] = Rs[i] / det; 786 787 dVd_dVr = PetscSinScalar(delta); 788 dVd_dVi = -PetscCosScalar(delta); 789 dVq_dVr = PetscCosScalar(delta); 790 dVq_dVi = PetscSinScalar(delta); 791 dVd_ddelta = Vr * PetscCosScalar(delta) + Vi * PetscSinScalar(delta); 792 dVq_ddelta = -Vr * PetscSinScalar(delta) + Vi * PetscCosScalar(delta); 793 794 /* fgen[idx+4] = Zdq_inv[0]*(-Edp + Vd) + Zdq_inv[1]*(-Eqp + Vq) + Id; */ 795 row[0] = idx + 4; 796 col[0] = idx; 797 col[1] = idx + 1; 798 col[2] = idx + 2; 799 val[0] = -Zdq_inv[1]; 800 val[1] = -Zdq_inv[0]; 801 val[2] = Zdq_inv[0] * dVd_ddelta + Zdq_inv[1] * dVq_ddelta; 802 col[3] = idx + 4; 803 col[4] = net_start + 2 * gbus[i]; 804 col[5] = net_start + 2 * gbus[i] + 1; 805 val[3] = 1; 806 val[4] = Zdq_inv[0] * dVd_dVr + Zdq_inv[1] * dVq_dVr; 807 val[5] = Zdq_inv[0] * dVd_dVi + Zdq_inv[1] * dVq_dVi; 808 PetscCall(MatSetValues(J, 1, row, 6, col, val, INSERT_VALUES)); 809 810 /* fgen[idx+5] = Zdq_inv[2]*(-Edp + Vd) + Zdq_inv[3]*(-Eqp + Vq) + Iq; */ 811 row[0] = idx + 5; 812 col[0] = idx; 813 col[1] = idx + 1; 814 col[2] = idx + 2; 815 val[0] = -Zdq_inv[3]; 816 val[1] = -Zdq_inv[2]; 817 val[2] = Zdq_inv[2] * dVd_ddelta + Zdq_inv[3] * dVq_ddelta; 818 col[3] = idx + 5; 819 col[4] = net_start + 2 * gbus[i]; 820 col[5] = net_start + 2 * gbus[i] + 1; 821 val[3] = 1; 822 val[4] = Zdq_inv[2] * dVd_dVr + Zdq_inv[3] * dVq_dVr; 823 val[5] = Zdq_inv[2] * dVd_dVi + Zdq_inv[3] * dVq_dVi; 824 PetscCall(MatSetValues(J, 1, row, 6, col, val, INSERT_VALUES)); 825 826 dIGr_ddelta = Id * PetscCosScalar(delta) - Iq * PetscSinScalar(delta); 827 dIGi_ddelta = Id * PetscSinScalar(delta) + Iq * PetscCosScalar(delta); 828 dIGr_dId = PetscSinScalar(delta); 829 dIGr_dIq = PetscCosScalar(delta); 830 dIGi_dId = -PetscCosScalar(delta); 831 dIGi_dIq = PetscSinScalar(delta); 832 833 /* fnet[2*gbus[i]] -= IGi; */ 834 row[0] = net_start + 2 * gbus[i]; 835 col[0] = idx + 2; 836 col[1] = idx + 4; 837 col[2] = idx + 5; 838 val[0] = -dIGi_ddelta; 839 val[1] = -dIGi_dId; 840 val[2] = -dIGi_dIq; 841 PetscCall(MatSetValues(J, 1, row, 3, col, val, INSERT_VALUES)); 842 843 /* fnet[2*gbus[i]+1] -= IGr; */ 844 row[0] = net_start + 2 * gbus[i] + 1; 845 col[0] = idx + 2; 846 col[1] = idx + 4; 847 col[2] = idx + 5; 848 val[0] = -dIGr_ddelta; 849 val[1] = -dIGr_dId; 850 val[2] = -dIGr_dIq; 851 PetscCall(MatSetValues(J, 1, row, 3, col, val, INSERT_VALUES)); 852 853 Vm = PetscSqrtScalar(Vd * Vd + Vq * Vq); 854 855 /* fgen[idx+6] = (-KE[i]*Efd - SE + VR)/TE[i]; */ 856 /* SE = k1[i]*PetscExpScalar(k2[i]*Efd); */ 857 858 dSE_dEfd = k1[i] * k2[i] * PetscExpScalar(k2[i] * Efd); 859 860 row[0] = idx + 6; 861 col[0] = idx + 6; 862 col[1] = idx + 8; 863 val[0] = (-KE[i] - dSE_dEfd) / TE[i]; 864 val[1] = 1 / TE[i]; 865 PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES)); 866 867 /* Exciter differential equations */ 868 869 /* fgen[idx+7] = (-RF + KF[i]*Efd/TF[i])/TF[i]; */ 870 row[0] = idx + 7; 871 col[0] = idx + 6; 872 col[1] = idx + 7; 873 val[0] = (KF[i] / TF[i]) / TF[i]; 874 val[1] = -1 / TF[i]; 875 PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES)); 876 877 /* fgen[idx+8] = (-VR + KA[i]*RF - KA[i]*KF[i]*Efd/TF[i] + KA[i]*(Vref[i] - Vm))/TA[i]; */ 878 /* Vm = (Vd^2 + Vq^2)^0.5; */ 879 880 row[0] = idx + 8; 881 if (VRatmax[i]) { 882 col[0] = idx + 8; 883 val[0] = 1.0; 884 PetscCall(MatSetValues(J, 1, row, 1, col, val, INSERT_VALUES)); 885 } else if (VRatmin[i]) { 886 col[0] = idx + 8; 887 val[0] = -1.0; 888 PetscCall(MatSetValues(J, 1, row, 1, col, val, INSERT_VALUES)); 889 } else { 890 dVm_dVd = Vd / Vm; 891 dVm_dVq = Vq / Vm; 892 dVm_dVr = dVm_dVd * dVd_dVr + dVm_dVq * dVq_dVr; 893 dVm_dVi = dVm_dVd * dVd_dVi + dVm_dVq * dVq_dVi; 894 row[0] = idx + 8; 895 col[0] = idx + 6; 896 col[1] = idx + 7; 897 col[2] = idx + 8; 898 val[0] = -(KA[i] * KF[i] / TF[i]) / TA[i]; 899 val[1] = KA[i] / TA[i]; 900 val[2] = -1 / TA[i]; 901 col[3] = net_start + 2 * gbus[i]; 902 col[4] = net_start + 2 * gbus[i] + 1; 903 val[3] = -KA[i] * dVm_dVr / TA[i]; 904 val[4] = -KA[i] * dVm_dVi / TA[i]; 905 PetscCall(MatSetValues(J, 1, row, 5, col, val, INSERT_VALUES)); 906 } 907 idx = idx + 9; 908 } 909 910 for (i = 0; i < nbus; i++) { 911 PetscCall(MatGetRow(user->Ybus, 2 * i, &ncols, &cols, &yvals)); 912 row[0] = net_start + 2 * i; 913 for (k = 0; k < ncols; k++) { 914 col[k] = net_start + cols[k]; 915 val[k] = yvals[k]; 916 } 917 PetscCall(MatSetValues(J, 1, row, ncols, col, val, INSERT_VALUES)); 918 PetscCall(MatRestoreRow(user->Ybus, 2 * i, &ncols, &cols, &yvals)); 919 920 PetscCall(MatGetRow(user->Ybus, 2 * i + 1, &ncols, &cols, &yvals)); 921 row[0] = net_start + 2 * i + 1; 922 for (k = 0; k < ncols; k++) { 923 col[k] = net_start + cols[k]; 924 val[k] = yvals[k]; 925 } 926 PetscCall(MatSetValues(J, 1, row, ncols, col, val, INSERT_VALUES)); 927 PetscCall(MatRestoreRow(user->Ybus, 2 * i + 1, &ncols, &cols, &yvals)); 928 } 929 930 PetscCall(MatAssemblyBegin(J, MAT_FLUSH_ASSEMBLY)); 931 PetscCall(MatAssemblyEnd(J, MAT_FLUSH_ASSEMBLY)); 932 933 PetscCall(VecGetArrayRead(user->V0, &v0)); 934 for (i = 0; i < nload; i++) { 935 Vr = xnet[2 * lbus[i]]; /* Real part of load bus voltage */ 936 Vi = xnet[2 * lbus[i] + 1]; /* Imaginary part of the load bus voltage */ 937 Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi); 938 Vm2 = Vm * Vm; 939 Vm4 = Vm2 * Vm2; 940 Vm0 = PetscSqrtScalar(v0[2 * lbus[i]] * v0[2 * lbus[i]] + v0[2 * lbus[i] + 1] * v0[2 * lbus[i] + 1]); 941 PD = QD = 0.0; 942 dPD_dVr = dPD_dVi = dQD_dVr = dQD_dVi = 0.0; 943 for (k = 0; k < ld_nsegsp[i]; k++) { 944 PD += ld_alphap[k] * PD0[i] * PetscPowScalar((Vm / Vm0), ld_betap[k]); 945 dPD_dVr += ld_alphap[k] * ld_betap[k] * PD0[i] * PetscPowScalar((1 / Vm0), ld_betap[k]) * Vr * PetscPowScalar(Vm, (ld_betap[k] - 2)); 946 dPD_dVi += ld_alphap[k] * ld_betap[k] * PD0[i] * PetscPowScalar((1 / Vm0), ld_betap[k]) * Vi * PetscPowScalar(Vm, (ld_betap[k] - 2)); 947 } 948 for (k = 0; k < ld_nsegsq[i]; k++) { 949 QD += ld_alphaq[k] * QD0[i] * PetscPowScalar((Vm / Vm0), ld_betaq[k]); 950 dQD_dVr += ld_alphaq[k] * ld_betaq[k] * QD0[i] * PetscPowScalar((1 / Vm0), ld_betaq[k]) * Vr * PetscPowScalar(Vm, (ld_betaq[k] - 2)); 951 dQD_dVi += ld_alphaq[k] * ld_betaq[k] * QD0[i] * PetscPowScalar((1 / Vm0), ld_betaq[k]) * Vi * PetscPowScalar(Vm, (ld_betaq[k] - 2)); 952 } 953 954 /* IDr = (PD*Vr + QD*Vi)/Vm2; */ 955 /* IDi = (-QD*Vr + PD*Vi)/Vm2; */ 956 957 dIDr_dVr = (dPD_dVr * Vr + dQD_dVr * Vi + PD) / Vm2 - ((PD * Vr + QD * Vi) * 2 * Vr) / Vm4; 958 dIDr_dVi = (dPD_dVi * Vr + dQD_dVi * Vi + QD) / Vm2 - ((PD * Vr + QD * Vi) * 2 * Vi) / Vm4; 959 960 dIDi_dVr = (-dQD_dVr * Vr + dPD_dVr * Vi - QD) / Vm2 - ((-QD * Vr + PD * Vi) * 2 * Vr) / Vm4; 961 dIDi_dVi = (-dQD_dVi * Vr + dPD_dVi * Vi + PD) / Vm2 - ((-QD * Vr + PD * Vi) * 2 * Vi) / Vm4; 962 963 /* fnet[2*lbus[i]] += IDi; */ 964 row[0] = net_start + 2 * lbus[i]; 965 col[0] = net_start + 2 * lbus[i]; 966 col[1] = net_start + 2 * lbus[i] + 1; 967 val[0] = dIDi_dVr; 968 val[1] = dIDi_dVi; 969 PetscCall(MatSetValues(J, 1, row, 2, col, val, ADD_VALUES)); 970 /* fnet[2*lbus[i]+1] += IDr; */ 971 row[0] = net_start + 2 * lbus[i] + 1; 972 col[0] = net_start + 2 * lbus[i]; 973 col[1] = net_start + 2 * lbus[i] + 1; 974 val[0] = dIDr_dVr; 975 val[1] = dIDr_dVi; 976 PetscCall(MatSetValues(J, 1, row, 2, col, val, ADD_VALUES)); 977 } 978 PetscCall(VecRestoreArrayRead(user->V0, &v0)); 979 980 PetscCall(VecRestoreArrayRead(Xgen, &xgen)); 981 PetscCall(VecRestoreArrayRead(Xnet, &xnet)); 982 983 PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet)); 984 985 PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY)); 986 PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY)); 987 PetscFunctionReturn(PETSC_SUCCESS); 988 } 989 990 /* 991 J = [I, 0 992 dg_dx, dg_dy] 993 */ 994 PetscErrorCode AlgJacobian(SNES snes, Vec X, Mat A, Mat B, void *ctx) 995 { 996 Userctx *user = (Userctx *)ctx; 997 998 PetscFunctionBegin; 999 PetscCall(ResidualJacobian(X, A, B, ctx)); 1000 PetscCall(MatSetOption(A, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE)); 1001 PetscCall(MatZeroRowsIS(A, user->is_diff, 1.0, NULL, NULL)); 1002 PetscFunctionReturn(PETSC_SUCCESS); 1003 } 1004 1005 /* 1006 J = [-df_dx, -df_dy 1007 dg_dx, dg_dy] 1008 */ 1009 1010 PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec X, Mat A, Mat B, void *ctx) 1011 { 1012 Userctx *user = (Userctx *)ctx; 1013 1014 PetscFunctionBegin; 1015 user->t = t; 1016 1017 PetscCall(ResidualJacobian(X, A, B, user)); 1018 1019 PetscFunctionReturn(PETSC_SUCCESS); 1020 } 1021 1022 /* 1023 J = [df_dx-aI, df_dy 1024 dg_dx, dg_dy] 1025 */ 1026 1027 PetscErrorCode IJacobian(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, Userctx *user) 1028 { 1029 PetscScalar atmp = (PetscScalar)a; 1030 PetscInt i, row; 1031 1032 PetscFunctionBegin; 1033 user->t = t; 1034 1035 PetscCall(RHSJacobian(ts, t, X, A, B, user)); 1036 PetscCall(MatScale(B, -1.0)); 1037 for (i = 0; i < ngen; i++) { 1038 row = 9 * i; 1039 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1040 row = 9 * i + 1; 1041 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1042 row = 9 * i + 2; 1043 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1044 row = 9 * i + 3; 1045 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1046 row = 9 * i + 6; 1047 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1048 row = 9 * i + 7; 1049 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1050 row = 9 * i + 8; 1051 PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES)); 1052 } 1053 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 1054 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 1055 PetscFunctionReturn(PETSC_SUCCESS); 1056 } 1057 1058 int main(int argc, char **argv) 1059 { 1060 TS ts; 1061 SNES snes_alg; 1062 PetscMPIInt size; 1063 Userctx user; 1064 PetscViewer Xview, Ybusview, viewer; 1065 Vec X, F_alg; 1066 Mat J, A; 1067 PetscInt i, idx, *idx2; 1068 Vec Xdot; 1069 PetscScalar *x, *mat, *amat; 1070 const PetscScalar *rmat; 1071 Vec vatol; 1072 PetscInt *direction; 1073 PetscBool *terminate; 1074 const PetscInt *idx3; 1075 PetscScalar *vatoli; 1076 PetscInt k; 1077 1078 PetscFunctionBeginUser; 1079 PetscCall(PetscInitialize(&argc, &argv, "petscoptions", help)); 1080 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size)); 1081 PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Only for sequential runs"); 1082 1083 user.neqs_gen = 9 * ngen; /* # eqs. for generator subsystem */ 1084 user.neqs_net = 2 * nbus; /* # eqs. for network subsystem */ 1085 user.neqs_pgrid = user.neqs_gen + user.neqs_net; 1086 1087 /* Create indices for differential and algebraic equations */ 1088 1089 PetscCall(PetscMalloc1(7 * ngen, &idx2)); 1090 for (i = 0; i < ngen; i++) { 1091 idx2[7 * i] = 9 * i; 1092 idx2[7 * i + 1] = 9 * i + 1; 1093 idx2[7 * i + 2] = 9 * i + 2; 1094 idx2[7 * i + 3] = 9 * i + 3; 1095 idx2[7 * i + 4] = 9 * i + 6; 1096 idx2[7 * i + 5] = 9 * i + 7; 1097 idx2[7 * i + 6] = 9 * i + 8; 1098 } 1099 PetscCall(ISCreateGeneral(PETSC_COMM_WORLD, 7 * ngen, idx2, PETSC_COPY_VALUES, &user.is_diff)); 1100 PetscCall(ISComplement(user.is_diff, 0, user.neqs_pgrid, &user.is_alg)); 1101 PetscCall(PetscFree(idx2)); 1102 1103 /* Read initial voltage vector and Ybus */ 1104 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, "X.bin", FILE_MODE_READ, &Xview)); 1105 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, "Ybus.bin", FILE_MODE_READ, &Ybusview)); 1106 1107 PetscCall(VecCreate(PETSC_COMM_WORLD, &user.V0)); 1108 PetscCall(VecSetSizes(user.V0, PETSC_DECIDE, user.neqs_net)); 1109 PetscCall(VecLoad(user.V0, Xview)); 1110 1111 PetscCall(MatCreate(PETSC_COMM_WORLD, &user.Ybus)); 1112 PetscCall(MatSetSizes(user.Ybus, PETSC_DECIDE, PETSC_DECIDE, user.neqs_net, user.neqs_net)); 1113 PetscCall(MatSetType(user.Ybus, MATBAIJ)); 1114 /* PetscCall(MatSetBlockSize(user.Ybus,2)); */ 1115 PetscCall(MatLoad(user.Ybus, Ybusview)); 1116 1117 /* Set run time options */ 1118 PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "Transient stability fault options", ""); 1119 { 1120 user.tfaulton = 1.0; 1121 user.tfaultoff = 1.2; 1122 user.Rfault = 0.0001; 1123 user.setisdiff = PETSC_FALSE; 1124 user.semiexplicit = PETSC_FALSE; 1125 user.faultbus = 8; 1126 PetscCall(PetscOptionsReal("-tfaulton", "", "", user.tfaulton, &user.tfaulton, NULL)); 1127 PetscCall(PetscOptionsReal("-tfaultoff", "", "", user.tfaultoff, &user.tfaultoff, NULL)); 1128 PetscCall(PetscOptionsInt("-faultbus", "", "", user.faultbus, &user.faultbus, NULL)); 1129 user.t0 = 0.0; 1130 user.tmax = 5.0; 1131 PetscCall(PetscOptionsReal("-t0", "", "", user.t0, &user.t0, NULL)); 1132 PetscCall(PetscOptionsReal("-tmax", "", "", user.tmax, &user.tmax, NULL)); 1133 PetscCall(PetscOptionsBool("-setisdiff", "", "", user.setisdiff, &user.setisdiff, NULL)); 1134 PetscCall(PetscOptionsBool("-dae_semiexplicit", "", "", user.semiexplicit, &user.semiexplicit, NULL)); 1135 } 1136 PetscOptionsEnd(); 1137 1138 PetscCall(PetscViewerDestroy(&Xview)); 1139 PetscCall(PetscViewerDestroy(&Ybusview)); 1140 1141 /* Create DMs for generator and network subsystems */ 1142 PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, user.neqs_gen, 1, 1, NULL, &user.dmgen)); 1143 PetscCall(DMSetOptionsPrefix(user.dmgen, "dmgen_")); 1144 PetscCall(DMSetFromOptions(user.dmgen)); 1145 PetscCall(DMSetUp(user.dmgen)); 1146 PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, user.neqs_net, 1, 1, NULL, &user.dmnet)); 1147 PetscCall(DMSetOptionsPrefix(user.dmnet, "dmnet_")); 1148 PetscCall(DMSetFromOptions(user.dmnet)); 1149 PetscCall(DMSetUp(user.dmnet)); 1150 /* Create a composite DM packer and add the two DMs */ 1151 PetscCall(DMCompositeCreate(PETSC_COMM_WORLD, &user.dmpgrid)); 1152 PetscCall(DMSetOptionsPrefix(user.dmpgrid, "pgrid_")); 1153 PetscCall(DMCompositeAddDM(user.dmpgrid, user.dmgen)); 1154 PetscCall(DMCompositeAddDM(user.dmpgrid, user.dmnet)); 1155 1156 PetscCall(DMCreateGlobalVector(user.dmpgrid, &X)); 1157 1158 PetscCall(MatCreate(PETSC_COMM_WORLD, &J)); 1159 PetscCall(MatSetSizes(J, PETSC_DECIDE, PETSC_DECIDE, user.neqs_pgrid, user.neqs_pgrid)); 1160 PetscCall(MatSetFromOptions(J)); 1161 PetscCall(PreallocateJacobian(J, &user)); 1162 1163 /* Create matrix to save solutions at each time step */ 1164 user.stepnum = 0; 1165 1166 PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, user.neqs_pgrid + 1, 1002, NULL, &user.Sol)); 1167 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1168 Create timestepping solver context 1169 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 1170 PetscCall(TSCreate(PETSC_COMM_WORLD, &ts)); 1171 PetscCall(TSSetProblemType(ts, TS_NONLINEAR)); 1172 if (user.semiexplicit) { 1173 PetscCall(TSSetType(ts, TSRK)); 1174 PetscCall(TSSetRHSFunction(ts, NULL, RHSFunction, &user)); 1175 PetscCall(TSSetRHSJacobian(ts, J, J, RHSJacobian, &user)); 1176 } else { 1177 PetscCall(TSSetType(ts, TSCN)); 1178 PetscCall(TSSetEquationType(ts, TS_EQ_DAE_IMPLICIT_INDEX1)); 1179 PetscCall(TSSetIFunction(ts, NULL, (TSIFunction)IFunction, &user)); 1180 PetscCall(TSSetIJacobian(ts, J, J, (TSIJacobian)IJacobian, &user)); 1181 } 1182 PetscCall(TSSetApplicationContext(ts, &user)); 1183 1184 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1185 Set initial conditions 1186 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 1187 PetscCall(SetInitialGuess(X, &user)); 1188 /* Just to set up the Jacobian structure */ 1189 1190 PetscCall(VecDuplicate(X, &Xdot)); 1191 PetscCall(IJacobian(ts, 0.0, X, Xdot, 0.0, J, J, &user)); 1192 PetscCall(VecDestroy(&Xdot)); 1193 1194 /* Save initial solution */ 1195 1196 idx = user.stepnum * (user.neqs_pgrid + 1); 1197 PetscCall(MatDenseGetArray(user.Sol, &mat)); 1198 PetscCall(VecGetArray(X, &x)); 1199 1200 mat[idx] = 0.0; 1201 1202 PetscCall(PetscArraycpy(mat + idx + 1, x, user.neqs_pgrid)); 1203 PetscCall(MatDenseRestoreArray(user.Sol, &mat)); 1204 PetscCall(VecRestoreArray(X, &x)); 1205 user.stepnum++; 1206 1207 PetscCall(TSSetMaxTime(ts, user.tmax)); 1208 PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP)); 1209 PetscCall(TSSetTimeStep(ts, 0.01)); 1210 PetscCall(TSSetFromOptions(ts)); 1211 PetscCall(TSSetPostStep(ts, SaveSolution)); 1212 PetscCall(TSSetSolution(ts, X)); 1213 1214 PetscCall(PetscMalloc1((2 * ngen + 2), &direction)); 1215 PetscCall(PetscMalloc1((2 * ngen + 2), &terminate)); 1216 direction[0] = direction[1] = 1; 1217 terminate[0] = terminate[1] = PETSC_FALSE; 1218 for (i = 0; i < ngen; i++) { 1219 direction[2 + 2 * i] = -1; 1220 direction[2 + 2 * i + 1] = 1; 1221 terminate[2 + 2 * i] = terminate[2 + 2 * i + 1] = PETSC_FALSE; 1222 } 1223 1224 PetscCall(TSSetEventHandler(ts, 2 * ngen + 2, direction, terminate, EventFunction, PostEventFunction, (void *)&user)); 1225 1226 if (user.semiexplicit) { 1227 /* Use a semi-explicit approach with the time-stepping done by an explicit method and the 1228 algrebraic part solved via PostStage and PostEvaluate callbacks 1229 */ 1230 PetscCall(TSSetType(ts, TSRK)); 1231 PetscCall(TSSetPostStage(ts, PostStage)); 1232 PetscCall(TSSetPostEvaluate(ts, PostEvaluate)); 1233 } 1234 1235 if (user.setisdiff) { 1236 /* Create vector of absolute tolerances and set the algebraic part to infinity */ 1237 PetscCall(VecDuplicate(X, &vatol)); 1238 PetscCall(VecSet(vatol, 100000.0)); 1239 PetscCall(VecGetArray(vatol, &vatoli)); 1240 PetscCall(ISGetIndices(user.is_diff, &idx3)); 1241 for (k = 0; k < 7 * ngen; k++) vatoli[idx3[k]] = 1e-2; 1242 PetscCall(VecRestoreArray(vatol, &vatoli)); 1243 } 1244 1245 /* Create the nonlinear solver for solving the algebraic system */ 1246 /* Note that although the algebraic system needs to be solved only for 1247 Idq and V, we reuse the entire system including xgen. The xgen 1248 variables are held constant by setting their residuals to 0 and 1249 putting a 1 on the Jacobian diagonal for xgen rows 1250 */ 1251 1252 PetscCall(VecDuplicate(X, &F_alg)); 1253 PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes_alg)); 1254 PetscCall(SNESSetFunction(snes_alg, F_alg, AlgFunction, &user)); 1255 PetscCall(SNESSetJacobian(snes_alg, J, J, AlgJacobian, &user)); 1256 PetscCall(SNESSetFromOptions(snes_alg)); 1257 1258 user.snes_alg = snes_alg; 1259 1260 /* Solve */ 1261 PetscCall(TSSolve(ts, X)); 1262 1263 PetscCall(MatAssemblyBegin(user.Sol, MAT_FINAL_ASSEMBLY)); 1264 PetscCall(MatAssemblyEnd(user.Sol, MAT_FINAL_ASSEMBLY)); 1265 1266 PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, user.neqs_pgrid + 1, user.stepnum, NULL, &A)); 1267 PetscCall(MatDenseGetArrayRead(user.Sol, &rmat)); 1268 PetscCall(MatDenseGetArray(A, &amat)); 1269 PetscCall(PetscArraycpy(amat, rmat, user.stepnum * (user.neqs_pgrid + 1))); 1270 PetscCall(MatDenseRestoreArray(A, &amat)); 1271 PetscCall(MatDenseRestoreArrayRead(user.Sol, &rmat)); 1272 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_SELF, "out.bin", FILE_MODE_WRITE, &viewer)); 1273 PetscCall(MatView(A, viewer)); 1274 PetscCall(PetscViewerDestroy(&viewer)); 1275 PetscCall(MatDestroy(&A)); 1276 1277 PetscCall(PetscFree(direction)); 1278 PetscCall(PetscFree(terminate)); 1279 PetscCall(SNESDestroy(&snes_alg)); 1280 PetscCall(VecDestroy(&F_alg)); 1281 PetscCall(MatDestroy(&J)); 1282 PetscCall(MatDestroy(&user.Ybus)); 1283 PetscCall(MatDestroy(&user.Sol)); 1284 PetscCall(VecDestroy(&X)); 1285 PetscCall(VecDestroy(&user.V0)); 1286 PetscCall(DMDestroy(&user.dmgen)); 1287 PetscCall(DMDestroy(&user.dmnet)); 1288 PetscCall(DMDestroy(&user.dmpgrid)); 1289 PetscCall(ISDestroy(&user.is_diff)); 1290 PetscCall(ISDestroy(&user.is_alg)); 1291 PetscCall(TSDestroy(&ts)); 1292 if (user.setisdiff) PetscCall(VecDestroy(&vatol)); 1293 PetscCall(PetscFinalize()); 1294 return 0; 1295 } 1296 1297 /*TEST 1298 1299 build: 1300 requires: double !complex !defined(PETSC_USE_64BIT_INDICES) 1301 1302 test: 1303 suffix: implicit 1304 args: -ts_monitor -snes_monitor_short 1305 localrunfiles: petscoptions X.bin Ybus.bin 1306 1307 test: 1308 suffix: semiexplicit 1309 args: -ts_monitor -snes_monitor_short -dae_semiexplicit -ts_rk_type 2a 1310 localrunfiles: petscoptions X.bin Ybus.bin 1311 1312 test: 1313 suffix: steprestart 1314 # needs ARKIMEX methods with all implicit stages since the mass matrix is not the identity 1315 args: -ts_monitor -snes_monitor_short -ts_type arkimex -ts_arkimex_type prssp2 1316 localrunfiles: petscoptions X.bin Ybus.bin 1317 1318 TEST*/ 1319