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