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