xref: /petsc/src/ts/tutorials/power_grid/stability_9bus/ex9bus.c (revision 40badf4fbc550ac1f60bd080eaff6de6d55b946d)
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   CHKERRQ(DMCompositeGetLocalVectors(user->dmpgrid,&Xgen,&Xnet));
148   CHKERRQ(DMCompositeScatter(user->dmpgrid,X,Xgen,Xnet));
149 
150   CHKERRQ(VecGetArrayRead(Xgen,&xgen));
151   CHKERRQ(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   CHKERRQ(VecRestoreArrayRead(Xgen,&xgen));
180   CHKERRQ(VecRestoreArrayRead(Xnet,&xnet));
181 
182   CHKERRQ(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   CHKERRQ(DMCompositeGetLocalVectors(user->dmpgrid,&Xgen,&Xnet));
202   CHKERRQ(DMCompositeScatter(user->dmpgrid,X,Xgen,Xnet));
203 
204   CHKERRQ(VecGetArray(Xgen,&xgen));
205   CHKERRQ(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       CHKERRQ(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       CHKERRQ(MatSetValues(user->Ybus,1,&row_loc,1,&col_loc,&val,ADD_VALUES));
218 
219       CHKERRQ(MatAssemblyBegin(user->Ybus,MAT_FINAL_ASSEMBLY));
220       CHKERRQ(MatAssemblyEnd(user->Ybus,MAT_FINAL_ASSEMBLY));
221 
222       /* Solve the algebraic equations */
223       CHKERRQ(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       CHKERRQ(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       CHKERRQ(MatSetValues(user->Ybus,1,&row_loc,1,&col_loc,&val,ADD_VALUES));
232 
233       CHKERRQ(MatAssemblyBegin(user->Ybus,MAT_FINAL_ASSEMBLY));
234       CHKERRQ(MatAssemblyEnd(user->Ybus,MAT_FINAL_ASSEMBLY));
235 
236       /* Solve the algebraic equations */
237       CHKERRQ(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             CHKERRQ(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             CHKERRQ(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           CHKERRQ(PetscPrintf(PETSC_COMM_SELF,"VR[%d]: hit upper limit at time %g\n",idx,t));
273         }
274         else {
275           VRatmax[idx] = 0;
276           CHKERRQ(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           CHKERRQ(PetscPrintf(PETSC_COMM_SELF,"VR[%d]: hit lower limit at time %g\n",idx,t));
282         }
283         else {
284           VRatmin[idx] = 0;
285           CHKERRQ(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   CHKERRQ(VecRestoreArray(Xgen,&xgen));
292   CHKERRQ(VecRestoreArray(Xnet,&xnet));
293 
294   CHKERRQ(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   CHKERRQ(TSGetApplicationContext(ts,&user));
329   CHKERRQ(TSGetTime(ts,&t));
330   CHKERRQ(TSGetSolution(ts,&X));
331   idx      = user->stepnum*(user->neqs_pgrid+1);
332   CHKERRQ(MatDenseGetArray(user->Sol,&mat));
333   CHKERRQ(VecGetArrayRead(X,&x));
334   mat[idx] = t;
335   CHKERRQ(PetscArraycpy(mat+idx+1,x,user->neqs_pgrid));
336   CHKERRQ(MatDenseRestoreArray(user->Sol,&mat));
337   CHKERRQ(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   CHKERRQ(DMCompositeGetLocalVectors(user->dmpgrid,&Xgen,&Xnet));
359 
360   /* Network subsystem initialization */
361   CHKERRQ(VecCopy(user->V0,Xnet));
362 
363   /* Generator subsystem initialization */
364   CHKERRQ(VecGetArrayWrite(Xgen,&xgen));
365   CHKERRQ(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   CHKERRQ(VecRestoreArrayWrite(Xgen,&xgen));
419   CHKERRQ(VecRestoreArrayRead(Xnet,&xnet));
420 
421   /* CHKERRQ(VecView(Xgen,0)); */
422   CHKERRQ(DMCompositeGather(user->dmpgrid,INSERT_VALUES,X,Xgen,Xnet));
423   CHKERRQ(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   CHKERRQ(VecZeroEntries(F));
446   CHKERRQ(DMCompositeGetLocalVectors(user->dmpgrid,&Xgen,&Xnet));
447   CHKERRQ(DMCompositeGetLocalVectors(user->dmpgrid,&Fgen,&Fnet));
448   CHKERRQ(DMCompositeScatter(user->dmpgrid,X,Xgen,Xnet));
449   CHKERRQ(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   CHKERRQ(MatMult(user->Ybus,Xnet,Fnet));
460 
461   CHKERRQ(VecGetArrayRead(Xgen,&xgen));
462   CHKERRQ(VecGetArrayRead(Xnet,&xnet));
463   CHKERRQ(VecGetArrayWrite(Fgen,&fgen));
464   CHKERRQ(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     CHKERRQ(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     CHKERRQ(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   CHKERRQ(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   CHKERRQ(VecRestoreArray(user->V0,&v0));
537 
538   CHKERRQ(VecRestoreArrayRead(Xgen,&xgen));
539   CHKERRQ(VecRestoreArrayRead(Xnet,&xnet));
540   CHKERRQ(VecRestoreArrayWrite(Fgen,&fgen));
541   CHKERRQ(VecRestoreArrayWrite(Fnet,&fnet));
542 
543   CHKERRQ(DMCompositeGather(user->dmpgrid,INSERT_VALUES,F,Fgen,Fnet));
544   CHKERRQ(DMCompositeRestoreLocalVectors(user->dmpgrid,&Xgen,&Xnet));
545   CHKERRQ(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   CHKERRQ(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   CHKERRQ(RHSFunction(ts,t,X,F,ctx));
573   CHKERRQ(VecScale(F,-1.0));
574   CHKERRQ(VecGetArray(F,&f));
575   CHKERRQ(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   CHKERRQ(VecRestoreArray(F,&f));
586   CHKERRQ(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   CHKERRQ(ResidualFunction(X,F,user));
603   CHKERRQ(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   CHKERRQ(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   CHKERRQ(TSGetApplicationContext(ts,&user));
623   CHKERRQ(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   CHKERRQ(TSGetApplicationContext(ts,&user));
634   CHKERRQ(TSGetSolution(ts,&X));
635   CHKERRQ(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   CHKERRQ(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     CHKERRQ(MatGetRow(user->Ybus,2*i,&ncols,NULL,NULL));
671     d_nnz[start+2*i]   += ncols;
672     d_nnz[start+2*i+1] += ncols;
673     CHKERRQ(MatRestoreRow(user->Ybus,2*i,&ncols,NULL,NULL));
674   }
675   CHKERRQ(MatSeqAIJSetPreallocation(J,0,d_nnz));
676   CHKERRQ(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   CHKERRQ(MatZeroEntries(B));
714   CHKERRQ(DMCompositeGetLocalVectors(user->dmpgrid,&Xgen,&Xnet));
715   CHKERRQ(DMCompositeScatter(user->dmpgrid,X,Xgen,Xnet));
716 
717   CHKERRQ(VecGetArrayRead(Xgen,&xgen));
718   CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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     CHKERRQ(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       CHKERRQ(MatSetValues(J,1,row,1,col,val,INSERT_VALUES));
830     } else if (VRatmin[i]) {
831       col[0] = idx + 8; val[0] = -1.0;
832       CHKERRQ(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       CHKERRQ(MatSetValues(J,1,row,5,col,val,INSERT_VALUES));
843     }
844     idx        = idx + 9;
845   }
846 
847   for (i=0; i<nbus; i++) {
848     CHKERRQ(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     CHKERRQ(MatSetValues(J,1,row,ncols,col,val,INSERT_VALUES));
855     CHKERRQ(MatRestoreRow(user->Ybus,2*i,&ncols,&cols,&yvals));
856 
857     CHKERRQ(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     CHKERRQ(MatSetValues(J,1,row,ncols,col,val,INSERT_VALUES));
864     CHKERRQ(MatRestoreRow(user->Ybus,2*i+1,&ncols,&cols,&yvals));
865   }
866 
867   CHKERRQ(MatAssemblyBegin(J,MAT_FLUSH_ASSEMBLY));
868   CHKERRQ(MatAssemblyEnd(J,MAT_FLUSH_ASSEMBLY));
869 
870   CHKERRQ(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     CHKERRQ(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     CHKERRQ(MatSetValues(J,1,row,2,col,val,ADD_VALUES));
908   }
909   CHKERRQ(VecRestoreArrayRead(user->V0,&v0));
910 
911   CHKERRQ(VecRestoreArrayRead(Xgen,&xgen));
912   CHKERRQ(VecRestoreArrayRead(Xnet,&xnet));
913 
914   CHKERRQ(DMCompositeRestoreLocalVectors(user->dmpgrid,&Xgen,&Xnet));
915 
916   CHKERRQ(MatAssemblyBegin(J,MAT_FINAL_ASSEMBLY));
917   CHKERRQ(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   CHKERRQ(ResidualJacobian(X,A,B,ctx));
931   CHKERRQ(MatSetOption(A,MAT_KEEP_NONZERO_PATTERN,PETSC_TRUE));
932   CHKERRQ(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   CHKERRQ(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   CHKERRQ(RHSJacobian(ts,t,X,A,B,user));
967   CHKERRQ(MatScale(B,-1.0));
968   for (i=0;i < ngen;i++) {
969     row = 9*i;
970     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
971     row  = 9*i+1;
972     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
973     row  = 9*i+2;
974     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
975     row  = 9*i+3;
976     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
977     row  = 9*i+6;
978     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
979     row  = 9*i+7;
980     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
981     row  = 9*i+8;
982     CHKERRQ(MatSetValues(A,1,&row,1,&row,&atmp,ADD_VALUES));
983   }
984   CHKERRQ(MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY));
985   CHKERRQ(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   ierr = PetscInitialize(&argc,&argv,"petscoptions",help);if (ierr) return ierr;
1011   CHKERRMPI(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   CHKERRQ(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   CHKERRQ(ISCreateGeneral(PETSC_COMM_WORLD,7*ngen,idx2,PETSC_COPY_VALUES,&user.is_diff));
1026   CHKERRQ(ISComplement(user.is_diff,0,user.neqs_pgrid,&user.is_alg));
1027   CHKERRQ(PetscFree(idx2));
1028 
1029   /* Read initial voltage vector and Ybus */
1030   CHKERRQ(PetscViewerBinaryOpen(PETSC_COMM_WORLD,"X.bin",FILE_MODE_READ,&Xview));
1031   CHKERRQ(PetscViewerBinaryOpen(PETSC_COMM_WORLD,"Ybus.bin",FILE_MODE_READ,&Ybusview));
1032 
1033   CHKERRQ(VecCreate(PETSC_COMM_WORLD,&user.V0));
1034   CHKERRQ(VecSetSizes(user.V0,PETSC_DECIDE,user.neqs_net));
1035   CHKERRQ(VecLoad(user.V0,Xview));
1036 
1037   CHKERRQ(MatCreate(PETSC_COMM_WORLD,&user.Ybus));
1038   CHKERRQ(MatSetSizes(user.Ybus,PETSC_DECIDE,PETSC_DECIDE,user.neqs_net,user.neqs_net));
1039   CHKERRQ(MatSetType(user.Ybus,MATBAIJ));
1040   /*  CHKERRQ(MatSetBlockSize(user.Ybus,2)); */
1041   CHKERRQ(MatLoad(user.Ybus,Ybusview));
1042 
1043   /* Set run time options */
1044   ierr = PetscOptionsBegin(PETSC_COMM_WORLD,NULL,"Transient stability fault options","");CHKERRQ(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     CHKERRQ(PetscOptionsReal("-tfaulton","","",user.tfaulton,&user.tfaulton,NULL));
1053     CHKERRQ(PetscOptionsReal("-tfaultoff","","",user.tfaultoff,&user.tfaultoff,NULL));
1054     CHKERRQ(PetscOptionsInt("-faultbus","","",user.faultbus,&user.faultbus,NULL));
1055     user.t0        = 0.0;
1056     user.tmax      = 5.0;
1057     CHKERRQ(PetscOptionsReal("-t0","","",user.t0,&user.t0,NULL));
1058     CHKERRQ(PetscOptionsReal("-tmax","","",user.tmax,&user.tmax,NULL));
1059     CHKERRQ(PetscOptionsBool("-setisdiff","","",user.setisdiff,&user.setisdiff,NULL));
1060     CHKERRQ(PetscOptionsBool("-dae_semiexplicit","","",user.semiexplicit,&user.semiexplicit,NULL));
1061   }
1062   ierr = PetscOptionsEnd();CHKERRQ(ierr);
1063 
1064   CHKERRQ(PetscViewerDestroy(&Xview));
1065   CHKERRQ(PetscViewerDestroy(&Ybusview));
1066 
1067   /* Create DMs for generator and network subsystems */
1068   CHKERRQ(DMDACreate1d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,user.neqs_gen,1,1,NULL,&user.dmgen));
1069   CHKERRQ(DMSetOptionsPrefix(user.dmgen,"dmgen_"));
1070   CHKERRQ(DMSetFromOptions(user.dmgen));
1071   CHKERRQ(DMSetUp(user.dmgen));
1072   CHKERRQ(DMDACreate1d(PETSC_COMM_WORLD,DM_BOUNDARY_NONE,user.neqs_net,1,1,NULL,&user.dmnet));
1073   CHKERRQ(DMSetOptionsPrefix(user.dmnet,"dmnet_"));
1074   CHKERRQ(DMSetFromOptions(user.dmnet));
1075   CHKERRQ(DMSetUp(user.dmnet));
1076   /* Create a composite DM packer and add the two DMs */
1077   CHKERRQ(DMCompositeCreate(PETSC_COMM_WORLD,&user.dmpgrid));
1078   CHKERRQ(DMSetOptionsPrefix(user.dmpgrid,"pgrid_"));
1079   CHKERRQ(DMCompositeAddDM(user.dmpgrid,user.dmgen));
1080   CHKERRQ(DMCompositeAddDM(user.dmpgrid,user.dmnet));
1081 
1082   CHKERRQ(DMCreateGlobalVector(user.dmpgrid,&X));
1083 
1084   CHKERRQ(MatCreate(PETSC_COMM_WORLD,&J));
1085   CHKERRQ(MatSetSizes(J,PETSC_DECIDE,PETSC_DECIDE,user.neqs_pgrid,user.neqs_pgrid));
1086   CHKERRQ(MatSetFromOptions(J));
1087   CHKERRQ(PreallocateJacobian(J,&user));
1088 
1089   /* Create matrix to save solutions at each time step */
1090   user.stepnum = 0;
1091 
1092   CHKERRQ(MatCreateSeqDense(PETSC_COMM_SELF,user.neqs_pgrid+1,1002,NULL,&user.Sol));
1093   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1094      Create timestepping solver context
1095      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1096   CHKERRQ(TSCreate(PETSC_COMM_WORLD,&ts));
1097   CHKERRQ(TSSetProblemType(ts,TS_NONLINEAR));
1098   if (user.semiexplicit) {
1099     CHKERRQ(TSSetType(ts,TSRK));
1100     CHKERRQ(TSSetRHSFunction(ts,NULL,RHSFunction,&user));
1101     CHKERRQ(TSSetRHSJacobian(ts,J,J,RHSJacobian,&user));
1102   } else {
1103     CHKERRQ(TSSetType(ts,TSCN));
1104     CHKERRQ(TSSetEquationType(ts,TS_EQ_DAE_IMPLICIT_INDEX1));
1105     CHKERRQ(TSSetIFunction(ts,NULL,(TSIFunction) IFunction,&user));
1106     CHKERRQ(TSSetIJacobian(ts,J,J,(TSIJacobian)IJacobian,&user));
1107   }
1108   CHKERRQ(TSSetApplicationContext(ts,&user));
1109 
1110   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1111      Set initial conditions
1112    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1113   CHKERRQ(SetInitialGuess(X,&user));
1114   /* Just to set up the Jacobian structure */
1115 
1116   CHKERRQ(VecDuplicate(X,&Xdot));
1117   CHKERRQ(IJacobian(ts,0.0,X,Xdot,0.0,J,J,&user));
1118   CHKERRQ(VecDestroy(&Xdot));
1119 
1120   /* Save initial solution */
1121 
1122   idx=user.stepnum*(user.neqs_pgrid+1);
1123   CHKERRQ(MatDenseGetArray(user.Sol,&mat));
1124   CHKERRQ(VecGetArray(X,&x));
1125 
1126   mat[idx] = 0.0;
1127 
1128   CHKERRQ(PetscArraycpy(mat+idx+1,x,user.neqs_pgrid));
1129   CHKERRQ(MatDenseRestoreArray(user.Sol,&mat));
1130   CHKERRQ(VecRestoreArray(X,&x));
1131   user.stepnum++;
1132 
1133   CHKERRQ(TSSetMaxTime(ts,user.tmax));
1134   CHKERRQ(TSSetExactFinalTime(ts,TS_EXACTFINALTIME_MATCHSTEP));
1135   CHKERRQ(TSSetTimeStep(ts,0.01));
1136   CHKERRQ(TSSetFromOptions(ts));
1137   CHKERRQ(TSSetPostStep(ts,SaveSolution));
1138   CHKERRQ(TSSetSolution(ts,X));
1139 
1140   CHKERRQ(PetscMalloc1((2*ngen+2),&direction));
1141   CHKERRQ(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   CHKERRQ(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     CHKERRQ(TSSetType(ts,TSRK));
1156     CHKERRQ(TSSetPostStage(ts,PostStage));
1157     CHKERRQ(TSSetPostEvaluate(ts,PostEvaluate));
1158   }
1159 
1160   if (user.setisdiff) {
1161     /* Create vector of absolute tolerances and set the algebraic part to infinity */
1162     CHKERRQ(VecDuplicate(X,&vatol));
1163     CHKERRQ(VecSet(vatol,100000.0));
1164     CHKERRQ(VecGetArray(vatol,&vatoli));
1165     CHKERRQ(ISGetIndices(user.is_diff,&idx3));
1166     for (k=0; k < 7*ngen; k++) vatoli[idx3[k]] = 1e-2;
1167     CHKERRQ(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   CHKERRQ(VecDuplicate(X,&F_alg));
1178   CHKERRQ(SNESCreate(PETSC_COMM_WORLD,&snes_alg));
1179   CHKERRQ(SNESSetFunction(snes_alg,F_alg,AlgFunction,&user));
1180   CHKERRQ(SNESSetJacobian(snes_alg,J,J,AlgJacobian,&user));
1181   CHKERRQ(SNESSetFromOptions(snes_alg));
1182 
1183   user.snes_alg=snes_alg;
1184 
1185   /* Solve */
1186   CHKERRQ(TSSolve(ts,X));
1187 
1188   CHKERRQ(MatAssemblyBegin(user.Sol,MAT_FINAL_ASSEMBLY));
1189   CHKERRQ(MatAssemblyEnd(user.Sol,MAT_FINAL_ASSEMBLY));
1190 
1191   CHKERRQ(MatCreateSeqDense(PETSC_COMM_SELF,user.neqs_pgrid+1,user.stepnum,NULL,&A));
1192   CHKERRQ(MatDenseGetArrayRead(user.Sol,&rmat));
1193   CHKERRQ(MatDenseGetArray(A,&amat));
1194   CHKERRQ(PetscArraycpy(amat,rmat,user.stepnum*(user.neqs_pgrid+1)));
1195   CHKERRQ(MatDenseRestoreArray(A,&amat));
1196   CHKERRQ(MatDenseRestoreArrayRead(user.Sol,&rmat));
1197   CHKERRQ(PetscViewerBinaryOpen(PETSC_COMM_SELF,"out.bin",FILE_MODE_WRITE,&viewer));
1198   CHKERRQ(MatView(A,viewer));
1199   CHKERRQ(PetscViewerDestroy(&viewer));
1200   CHKERRQ(MatDestroy(&A));
1201 
1202   CHKERRQ(PetscFree(direction));
1203   CHKERRQ(PetscFree(terminate));
1204   CHKERRQ(SNESDestroy(&snes_alg));
1205   CHKERRQ(VecDestroy(&F_alg));
1206   CHKERRQ(MatDestroy(&J));
1207   CHKERRQ(MatDestroy(&user.Ybus));
1208   CHKERRQ(MatDestroy(&user.Sol));
1209   CHKERRQ(VecDestroy(&X));
1210   CHKERRQ(VecDestroy(&user.V0));
1211   CHKERRQ(DMDestroy(&user.dmgen));
1212   CHKERRQ(DMDestroy(&user.dmnet));
1213   CHKERRQ(DMDestroy(&user.dmpgrid));
1214   CHKERRQ(ISDestroy(&user.is_diff));
1215   CHKERRQ(ISDestroy(&user.is_alg));
1216   CHKERRQ(TSDestroy(&ts));
1217   if (user.setisdiff) {
1218     CHKERRQ(VecDestroy(&vatol));
1219   }
1220   ierr = PetscFinalize();
1221   return ierr;
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