Actual source code: ex9busopt.c
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";
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: */
15: #include <petsctao.h>
16: #include <petscts.h>
17: #include <petscdm.h>
18: #include <petscdmda.h>
19: #include <petscdmcomposite.h>
20: #include <petsctime.h>
22: PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *);
24: #define freq 60
25: #define w_s (2 * PETSC_PI * freq)
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 */
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};*/
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 */
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) */
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
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};
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;
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(PETSC_SUCCESS);
117: }
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(PETSC_SUCCESS);
126: }
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;
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(PETSC_SUCCESS);
151: }
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;
164: PetscFunctionBegin;
165: M[0] = 2 * H[0] / w_s;
166: M[1] = 2 * H[1] / w_s;
167: M[2] = 2 * H[2] / w_s;
168: D[0] = 0.1 * M[0];
169: D[1] = 0.1 * M[1];
170: D[2] = 0.1 * M[2];
172: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet));
174: /* Network subsystem initialization */
175: PetscCall(VecCopy(user->V0, Xnet));
177: /* Generator subsystem initialization */
178: PetscCall(VecGetArray(Xgen, &xgen));
179: PetscCall(VecGetArray(Xnet, &xnet));
181: for (i = 0; i < ngen; i++) {
182: Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */
183: Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */
184: Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi);
185: Vm2 = Vm * Vm;
186: IGr = (Vr * PG[i] + Vi * QG[i]) / Vm2;
187: IGi = (Vi * PG[i] - Vr * QG[i]) / Vm2;
189: delta = PetscAtan2Real(Vi + Xq[i] * IGr, Vr - Xq[i] * IGi); /* Machine angle */
191: theta = PETSC_PI / 2.0 - delta;
193: Id = IGr * PetscCosScalar(theta) - IGi * PetscSinScalar(theta); /* d-axis stator current */
194: Iq = IGr * PetscSinScalar(theta) + IGi * PetscCosScalar(theta); /* q-axis stator current */
196: Vd = Vr * PetscCosScalar(theta) - Vi * PetscSinScalar(theta);
197: Vq = Vr * PetscSinScalar(theta) + Vi * PetscCosScalar(theta);
199: Edp = Vd + Rs[i] * Id - Xqp[i] * Iq; /* d-axis transient EMF */
200: Eqp = Vq + Rs[i] * Iq + Xdp[i] * Id; /* q-axis transient EMF */
202: TM[i] = PG[i];
204: /* The generator variables are ordered as [Eqp,Edp,delta,w,Id,Iq] */
205: xgen[idx] = Eqp;
206: xgen[idx + 1] = Edp;
207: xgen[idx + 2] = delta;
208: xgen[idx + 3] = w_s;
210: idx = idx + 4;
212: xgen[idx] = Id;
213: xgen[idx + 1] = Iq;
215: idx = idx + 2;
217: /* Exciter */
218: Efd = Eqp + (Xd[i] - Xdp[i]) * Id;
219: SE = k1[i] * PetscExpScalar(k2[i] * Efd);
220: VR = KE[i] * Efd + SE;
221: RF = KF[i] * Efd / TF[i];
223: xgen[idx] = Efd;
224: xgen[idx + 1] = RF;
225: xgen[idx + 2] = VR;
227: Vref[i] = Vm + (VR / KA[i]);
229: idx = idx + 3;
230: }
232: PetscCall(VecRestoreArray(Xgen, &xgen));
233: PetscCall(VecRestoreArray(Xnet, &xnet));
235: /* PetscCall(VecView(Xgen,0)); */
236: PetscCall(DMCompositeGather(user->dmpgrid, INSERT_VALUES, X, Xgen, Xnet));
237: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet));
238: PetscFunctionReturn(PETSC_SUCCESS);
239: }
241: PetscErrorCode InitialGuess(Vec X, Userctx *user, const PetscScalar PGv[])
242: {
243: Vec Xgen, Xnet;
244: PetscScalar *xgen, *xnet;
245: PetscInt i, idx = 0;
246: PetscScalar Vr, Vi, IGr, IGi, Vm, Vm2;
247: PetscScalar Eqp, Edp, delta;
248: PetscScalar Efd, RF, VR; /* Exciter variables */
249: PetscScalar Id, Iq; /* Generator dq axis currents */
250: PetscScalar theta, Vd, Vq, SE;
252: PetscFunctionBegin;
253: M[0] = 2 * H[0] / w_s;
254: M[1] = 2 * H[1] / w_s;
255: M[2] = 2 * H[2] / w_s;
256: D[0] = 0.1 * M[0];
257: D[1] = 0.1 * M[1];
258: D[2] = 0.1 * M[2];
260: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet));
262: /* Network subsystem initialization */
263: PetscCall(VecCopy(user->V0, Xnet));
265: /* Generator subsystem initialization */
266: PetscCall(VecGetArray(Xgen, &xgen));
267: PetscCall(VecGetArray(Xnet, &xnet));
269: for (i = 0; i < ngen; i++) {
270: Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */
271: Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */
272: Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi);
273: Vm2 = Vm * Vm;
274: IGr = (Vr * PGv[i] + Vi * QG[i]) / Vm2;
275: IGi = (Vi * PGv[i] - Vr * QG[i]) / Vm2;
277: delta = PetscAtan2Real(Vi + Xq[i] * IGr, Vr - Xq[i] * IGi); /* Machine angle */
279: theta = PETSC_PI / 2.0 - delta;
281: Id = IGr * PetscCosScalar(theta) - IGi * PetscSinScalar(theta); /* d-axis stator current */
282: Iq = IGr * PetscSinScalar(theta) + IGi * PetscCosScalar(theta); /* q-axis stator current */
284: Vd = Vr * PetscCosScalar(theta) - Vi * PetscSinScalar(theta);
285: Vq = Vr * PetscSinScalar(theta) + Vi * PetscCosScalar(theta);
287: Edp = Vd + Rs[i] * Id - Xqp[i] * Iq; /* d-axis transient EMF */
288: Eqp = Vq + Rs[i] * Iq + Xdp[i] * Id; /* q-axis transient EMF */
290: /* The generator variables are ordered as [Eqp,Edp,delta,w,Id,Iq] */
291: xgen[idx] = Eqp;
292: xgen[idx + 1] = Edp;
293: xgen[idx + 2] = delta;
294: xgen[idx + 3] = w_s;
296: idx = idx + 4;
298: xgen[idx] = Id;
299: xgen[idx + 1] = Iq;
301: idx = idx + 2;
303: /* Exciter */
304: Efd = Eqp + (Xd[i] - Xdp[i]) * Id;
305: SE = k1[i] * PetscExpScalar(k2[i] * Efd);
306: VR = KE[i] * Efd + SE;
307: RF = KF[i] * Efd / TF[i];
309: xgen[idx] = Efd;
310: xgen[idx + 1] = RF;
311: xgen[idx + 2] = VR;
313: idx = idx + 3;
314: }
316: PetscCall(VecRestoreArray(Xgen, &xgen));
317: PetscCall(VecRestoreArray(Xnet, &xnet));
319: /* PetscCall(VecView(Xgen,0)); */
320: PetscCall(DMCompositeGather(user->dmpgrid, INSERT_VALUES, X, Xgen, Xnet));
321: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet));
322: PetscFunctionReturn(PETSC_SUCCESS);
323: }
325: PetscErrorCode DICDPFiniteDifference(Vec X, Vec *DICDP, Userctx *user)
326: {
327: Vec Y;
328: PetscScalar PGv[3], eps;
329: PetscInt i, j;
331: PetscFunctionBegin;
332: eps = 1.e-7;
333: PetscCall(VecDuplicate(X, &Y));
335: for (i = 0; i < ngen; i++) {
336: for (j = 0; j < 3; j++) PGv[j] = PG[j];
337: PGv[i] = PG[i] + eps;
338: PetscCall(InitialGuess(Y, user, PGv));
339: PetscCall(InitialGuess(X, user, PG));
341: PetscCall(VecAXPY(Y, -1.0, X));
342: PetscCall(VecScale(Y, 1. / eps));
343: PetscCall(VecCopy(Y, DICDP[i]));
344: }
345: PetscCall(VecDestroy(&Y));
346: PetscFunctionReturn(PETSC_SUCCESS);
347: }
349: /* Computes F = [-f(x,y);g(x,y)] */
350: PetscErrorCode ResidualFunction(SNES snes, Vec X, Vec F, Userctx *user)
351: {
352: Vec Xgen, Xnet, Fgen, Fnet;
353: PetscScalar *xgen, *xnet, *fgen, *fnet;
354: PetscInt i, idx = 0;
355: PetscScalar Vr, Vi, Vm, Vm2;
356: PetscScalar Eqp, Edp, delta, w; /* Generator variables */
357: PetscScalar Efd, RF, VR; /* Exciter variables */
358: PetscScalar Id, Iq; /* Generator dq axis currents */
359: PetscScalar Vd, Vq, SE;
360: PetscScalar IGr, IGi, IDr, IDi;
361: PetscScalar Zdq_inv[4], det;
362: PetscScalar PD, QD, Vm0, *v0;
363: PetscInt k;
365: PetscFunctionBegin;
366: PetscCall(VecZeroEntries(F));
367: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet));
368: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Fgen, &Fnet));
369: PetscCall(DMCompositeScatter(user->dmpgrid, X, Xgen, Xnet));
370: PetscCall(DMCompositeScatter(user->dmpgrid, F, Fgen, Fnet));
372: /* Network current balance residual IG + Y*V + IL = 0. Only YV is added here.
373: The generator current injection, IG, and load current injection, ID are added later
374: */
375: /* Note that the values in Ybus are stored assuming the imaginary current balance
376: equation is ordered first followed by real current balance equation for each bus.
377: Thus imaginary current contribution goes in location 2*i, and
378: real current contribution in 2*i+1
379: */
380: PetscCall(MatMult(user->Ybus, Xnet, Fnet));
382: PetscCall(VecGetArray(Xgen, &xgen));
383: PetscCall(VecGetArray(Xnet, &xnet));
384: PetscCall(VecGetArray(Fgen, &fgen));
385: PetscCall(VecGetArray(Fnet, &fnet));
387: /* Generator subsystem */
388: for (i = 0; i < ngen; i++) {
389: Eqp = xgen[idx];
390: Edp = xgen[idx + 1];
391: delta = xgen[idx + 2];
392: w = xgen[idx + 3];
393: Id = xgen[idx + 4];
394: Iq = xgen[idx + 5];
395: Efd = xgen[idx + 6];
396: RF = xgen[idx + 7];
397: VR = xgen[idx + 8];
399: /* Generator differential equations */
400: fgen[idx] = (Eqp + (Xd[i] - Xdp[i]) * Id - Efd) / Td0p[i];
401: fgen[idx + 1] = (Edp - (Xq[i] - Xqp[i]) * Iq) / Tq0p[i];
402: fgen[idx + 2] = -w + w_s;
403: fgen[idx + 3] = (-TM[i] + Edp * Id + Eqp * Iq + (Xqp[i] - Xdp[i]) * Id * Iq + D[i] * (w - w_s)) / M[i];
405: Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */
406: Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */
408: PetscCall(ri2dq(Vr, Vi, delta, &Vd, &Vq));
409: /* Algebraic equations for stator currents */
410: det = Rs[i] * Rs[i] + Xdp[i] * Xqp[i];
412: Zdq_inv[0] = Rs[i] / det;
413: Zdq_inv[1] = Xqp[i] / det;
414: Zdq_inv[2] = -Xdp[i] / det;
415: Zdq_inv[3] = Rs[i] / det;
417: fgen[idx + 4] = Zdq_inv[0] * (-Edp + Vd) + Zdq_inv[1] * (-Eqp + Vq) + Id;
418: fgen[idx + 5] = Zdq_inv[2] * (-Edp + Vd) + Zdq_inv[3] * (-Eqp + Vq) + Iq;
420: /* Add generator current injection to network */
421: PetscCall(dq2ri(Id, Iq, delta, &IGr, &IGi));
423: fnet[2 * gbus[i]] -= IGi;
424: fnet[2 * gbus[i] + 1] -= IGr;
426: Vm = PetscSqrtScalar(Vd * Vd + Vq * Vq);
428: SE = k1[i] * PetscExpScalar(k2[i] * Efd);
430: /* Exciter differential equations */
431: fgen[idx + 6] = (KE[i] * Efd + SE - VR) / TE[i];
432: fgen[idx + 7] = (RF - KF[i] * Efd / TF[i]) / TF[i];
433: fgen[idx + 8] = (VR - KA[i] * RF + KA[i] * KF[i] * Efd / TF[i] - KA[i] * (Vref[i] - Vm)) / TA[i];
435: idx = idx + 9;
436: }
438: PetscCall(VecGetArray(user->V0, &v0));
439: for (i = 0; i < nload; i++) {
440: Vr = xnet[2 * lbus[i]]; /* Real part of load bus voltage */
441: Vi = xnet[2 * lbus[i] + 1]; /* Imaginary part of the load bus voltage */
442: Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi);
443: Vm2 = Vm * Vm;
444: Vm0 = PetscSqrtScalar(v0[2 * lbus[i]] * v0[2 * lbus[i]] + v0[2 * lbus[i] + 1] * v0[2 * lbus[i] + 1]);
445: PD = QD = 0.0;
446: for (k = 0; k < ld_nsegsp[i]; k++) PD += ld_alphap[k] * PD0[i] * PetscPowScalar((Vm / Vm0), ld_betap[k]);
447: for (k = 0; k < ld_nsegsq[i]; k++) QD += ld_alphaq[k] * QD0[i] * PetscPowScalar((Vm / Vm0), ld_betaq[k]);
449: /* Load currents */
450: IDr = (PD * Vr + QD * Vi) / Vm2;
451: IDi = (-QD * Vr + PD * Vi) / Vm2;
453: fnet[2 * lbus[i]] += IDi;
454: fnet[2 * lbus[i] + 1] += IDr;
455: }
456: PetscCall(VecRestoreArray(user->V0, &v0));
458: PetscCall(VecRestoreArray(Xgen, &xgen));
459: PetscCall(VecRestoreArray(Xnet, &xnet));
460: PetscCall(VecRestoreArray(Fgen, &fgen));
461: PetscCall(VecRestoreArray(Fnet, &fnet));
463: PetscCall(DMCompositeGather(user->dmpgrid, INSERT_VALUES, F, Fgen, Fnet));
464: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet));
465: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Fgen, &Fnet));
466: PetscFunctionReturn(PETSC_SUCCESS);
467: }
469: /* \dot{x} - f(x,y)
470: g(x,y) = 0
471: */
472: PetscErrorCode IFunction(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, Userctx *user)
473: {
474: SNES snes;
475: PetscScalar *f;
476: const PetscScalar *xdot;
477: PetscInt i;
479: PetscFunctionBegin;
480: user->t = t;
482: PetscCall(TSGetSNES(ts, &snes));
483: PetscCall(ResidualFunction(snes, X, F, user));
484: PetscCall(VecGetArray(F, &f));
485: PetscCall(VecGetArrayRead(Xdot, &xdot));
486: for (i = 0; i < ngen; i++) {
487: f[9 * i] += xdot[9 * i];
488: f[9 * i + 1] += xdot[9 * i + 1];
489: f[9 * i + 2] += xdot[9 * i + 2];
490: f[9 * i + 3] += xdot[9 * i + 3];
491: f[9 * i + 6] += xdot[9 * i + 6];
492: f[9 * i + 7] += xdot[9 * i + 7];
493: f[9 * i + 8] += xdot[9 * i + 8];
494: }
495: PetscCall(VecRestoreArray(F, &f));
496: PetscCall(VecRestoreArrayRead(Xdot, &xdot));
497: PetscFunctionReturn(PETSC_SUCCESS);
498: }
500: /* This function is used for solving the algebraic system only during fault on and
501: off times. It computes the entire F and then zeros out the part corresponding to
502: differential equations
503: F = [0;g(y)];
504: */
505: PetscErrorCode AlgFunction(SNES snes, Vec X, Vec F, void *ctx)
506: {
507: Userctx *user = (Userctx *)ctx;
508: PetscScalar *f;
509: PetscInt i;
511: PetscFunctionBegin;
512: PetscCall(ResidualFunction(snes, X, F, user));
513: PetscCall(VecGetArray(F, &f));
514: for (i = 0; i < ngen; i++) {
515: f[9 * i] = 0;
516: f[9 * i + 1] = 0;
517: f[9 * i + 2] = 0;
518: f[9 * i + 3] = 0;
519: f[9 * i + 6] = 0;
520: f[9 * i + 7] = 0;
521: f[9 * i + 8] = 0;
522: }
523: PetscCall(VecRestoreArray(F, &f));
524: PetscFunctionReturn(PETSC_SUCCESS);
525: }
527: PetscErrorCode PreallocateJacobian(Mat J, Userctx *user)
528: {
529: PetscInt *d_nnz;
530: PetscInt i, idx = 0, start = 0;
531: PetscInt ncols;
533: PetscFunctionBegin;
534: PetscCall(PetscMalloc1(user->neqs_pgrid, &d_nnz));
535: for (i = 0; i < user->neqs_pgrid; i++) d_nnz[i] = 0;
536: /* Generator subsystem */
537: for (i = 0; i < ngen; i++) {
538: d_nnz[idx] += 3;
539: d_nnz[idx + 1] += 2;
540: d_nnz[idx + 2] += 2;
541: d_nnz[idx + 3] += 5;
542: d_nnz[idx + 4] += 6;
543: d_nnz[idx + 5] += 6;
545: d_nnz[user->neqs_gen + 2 * gbus[i]] += 3;
546: d_nnz[user->neqs_gen + 2 * gbus[i] + 1] += 3;
548: d_nnz[idx + 6] += 2;
549: d_nnz[idx + 7] += 2;
550: d_nnz[idx + 8] += 5;
552: idx = idx + 9;
553: }
555: start = user->neqs_gen;
556: for (i = 0; i < nbus; i++) {
557: PetscCall(MatGetRow(user->Ybus, 2 * i, &ncols, NULL, NULL));
558: d_nnz[start + 2 * i] += ncols;
559: d_nnz[start + 2 * i + 1] += ncols;
560: PetscCall(MatRestoreRow(user->Ybus, 2 * i, &ncols, NULL, NULL));
561: }
563: PetscCall(MatSeqAIJSetPreallocation(J, 0, d_nnz));
564: PetscCall(PetscFree(d_nnz));
565: PetscFunctionReturn(PETSC_SUCCESS);
566: }
568: /*
569: J = [-df_dx, -df_dy
570: dg_dx, dg_dy]
571: */
572: PetscErrorCode ResidualJacobian(SNES snes, Vec X, Mat J, Mat B, void *ctx)
573: {
574: Userctx *user = (Userctx *)ctx;
575: Vec Xgen, Xnet;
576: PetscScalar *xgen, *xnet;
577: PetscInt i, idx = 0;
578: PetscScalar Vr, Vi, Vm, Vm2;
579: PetscScalar Eqp, Edp, delta; /* Generator variables */
580: PetscScalar Efd; /* Exciter variables */
581: PetscScalar Id, Iq; /* Generator dq axis currents */
582: PetscScalar Vd, Vq;
583: PetscScalar val[10];
584: PetscInt row[2], col[10];
585: PetscInt net_start = user->neqs_gen;
586: PetscInt ncols;
587: const PetscInt *cols;
588: const PetscScalar *yvals;
589: PetscInt k;
590: PetscScalar Zdq_inv[4], det;
591: PetscScalar dVd_dVr, dVd_dVi, dVq_dVr, dVq_dVi, dVd_ddelta, dVq_ddelta;
592: PetscScalar dIGr_ddelta, dIGi_ddelta, dIGr_dId, dIGr_dIq, dIGi_dId, dIGi_dIq;
593: PetscScalar dSE_dEfd;
594: PetscScalar dVm_dVd, dVm_dVq, dVm_dVr, dVm_dVi;
595: PetscScalar PD, QD, Vm0, *v0, Vm4;
596: PetscScalar dPD_dVr, dPD_dVi, dQD_dVr, dQD_dVi;
597: PetscScalar dIDr_dVr, dIDr_dVi, dIDi_dVr, dIDi_dVi;
599: PetscFunctionBegin;
600: PetscCall(MatZeroEntries(B));
601: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet));
602: PetscCall(DMCompositeScatter(user->dmpgrid, X, Xgen, Xnet));
604: PetscCall(VecGetArray(Xgen, &xgen));
605: PetscCall(VecGetArray(Xnet, &xnet));
607: /* Generator subsystem */
608: for (i = 0; i < ngen; i++) {
609: Eqp = xgen[idx];
610: Edp = xgen[idx + 1];
611: delta = xgen[idx + 2];
612: Id = xgen[idx + 4];
613: Iq = xgen[idx + 5];
614: Efd = xgen[idx + 6];
616: /* fgen[idx] = (Eqp + (Xd[i] - Xdp[i])*Id - Efd)/Td0p[i]; */
617: row[0] = idx;
618: col[0] = idx;
619: col[1] = idx + 4;
620: col[2] = idx + 6;
621: val[0] = 1 / Td0p[i];
622: val[1] = (Xd[i] - Xdp[i]) / Td0p[i];
623: val[2] = -1 / Td0p[i];
625: PetscCall(MatSetValues(J, 1, row, 3, col, val, INSERT_VALUES));
627: /* fgen[idx+1] = (Edp - (Xq[i] - Xqp[i])*Iq)/Tq0p[i]; */
628: row[0] = idx + 1;
629: col[0] = idx + 1;
630: col[1] = idx + 5;
631: val[0] = 1 / Tq0p[i];
632: val[1] = -(Xq[i] - Xqp[i]) / Tq0p[i];
633: PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES));
635: /* fgen[idx+2] = - w + w_s; */
636: row[0] = idx + 2;
637: col[0] = idx + 2;
638: col[1] = idx + 3;
639: val[0] = 0;
640: val[1] = -1;
641: PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES));
643: /* fgen[idx+3] = (-TM[i] + Edp*Id + Eqp*Iq + (Xqp[i] - Xdp[i])*Id*Iq + D[i]*(w - w_s))/M[i]; */
644: row[0] = idx + 3;
645: col[0] = idx;
646: col[1] = idx + 1;
647: col[2] = idx + 3;
648: col[3] = idx + 4;
649: col[4] = idx + 5;
650: val[0] = Iq / M[i];
651: val[1] = Id / M[i];
652: val[2] = D[i] / M[i];
653: val[3] = (Edp + (Xqp[i] - Xdp[i]) * Iq) / M[i];
654: val[4] = (Eqp + (Xqp[i] - Xdp[i]) * Id) / M[i];
655: PetscCall(MatSetValues(J, 1, row, 5, col, val, INSERT_VALUES));
657: Vr = xnet[2 * gbus[i]]; /* Real part of generator terminal voltage */
658: Vi = xnet[2 * gbus[i] + 1]; /* Imaginary part of the generator terminal voltage */
659: PetscCall(ri2dq(Vr, Vi, delta, &Vd, &Vq));
661: det = Rs[i] * Rs[i] + Xdp[i] * Xqp[i];
663: Zdq_inv[0] = Rs[i] / det;
664: Zdq_inv[1] = Xqp[i] / det;
665: Zdq_inv[2] = -Xdp[i] / det;
666: Zdq_inv[3] = Rs[i] / det;
668: dVd_dVr = PetscSinScalar(delta);
669: dVd_dVi = -PetscCosScalar(delta);
670: dVq_dVr = PetscCosScalar(delta);
671: dVq_dVi = PetscSinScalar(delta);
672: dVd_ddelta = Vr * PetscCosScalar(delta) + Vi * PetscSinScalar(delta);
673: dVq_ddelta = -Vr * PetscSinScalar(delta) + Vi * PetscCosScalar(delta);
675: /* fgen[idx+4] = Zdq_inv[0]*(-Edp + Vd) + Zdq_inv[1]*(-Eqp + Vq) + Id; */
676: row[0] = idx + 4;
677: col[0] = idx;
678: col[1] = idx + 1;
679: col[2] = idx + 2;
680: val[0] = -Zdq_inv[1];
681: val[1] = -Zdq_inv[0];
682: val[2] = Zdq_inv[0] * dVd_ddelta + Zdq_inv[1] * dVq_ddelta;
683: col[3] = idx + 4;
684: col[4] = net_start + 2 * gbus[i];
685: col[5] = net_start + 2 * gbus[i] + 1;
686: val[3] = 1;
687: val[4] = Zdq_inv[0] * dVd_dVr + Zdq_inv[1] * dVq_dVr;
688: val[5] = Zdq_inv[0] * dVd_dVi + Zdq_inv[1] * dVq_dVi;
689: PetscCall(MatSetValues(J, 1, row, 6, col, val, INSERT_VALUES));
691: /* fgen[idx+5] = Zdq_inv[2]*(-Edp + Vd) + Zdq_inv[3]*(-Eqp + Vq) + Iq; */
692: row[0] = idx + 5;
693: col[0] = idx;
694: col[1] = idx + 1;
695: col[2] = idx + 2;
696: val[0] = -Zdq_inv[3];
697: val[1] = -Zdq_inv[2];
698: val[2] = Zdq_inv[2] * dVd_ddelta + Zdq_inv[3] * dVq_ddelta;
699: col[3] = idx + 5;
700: col[4] = net_start + 2 * gbus[i];
701: col[5] = net_start + 2 * gbus[i] + 1;
702: val[3] = 1;
703: val[4] = Zdq_inv[2] * dVd_dVr + Zdq_inv[3] * dVq_dVr;
704: val[5] = Zdq_inv[2] * dVd_dVi + Zdq_inv[3] * dVq_dVi;
705: PetscCall(MatSetValues(J, 1, row, 6, col, val, INSERT_VALUES));
707: dIGr_ddelta = Id * PetscCosScalar(delta) - Iq * PetscSinScalar(delta);
708: dIGi_ddelta = Id * PetscSinScalar(delta) + Iq * PetscCosScalar(delta);
709: dIGr_dId = PetscSinScalar(delta);
710: dIGr_dIq = PetscCosScalar(delta);
711: dIGi_dId = -PetscCosScalar(delta);
712: dIGi_dIq = PetscSinScalar(delta);
714: /* fnet[2*gbus[i]] -= IGi; */
715: row[0] = net_start + 2 * gbus[i];
716: col[0] = idx + 2;
717: col[1] = idx + 4;
718: col[2] = idx + 5;
719: val[0] = -dIGi_ddelta;
720: val[1] = -dIGi_dId;
721: val[2] = -dIGi_dIq;
722: PetscCall(MatSetValues(J, 1, row, 3, col, val, INSERT_VALUES));
724: /* fnet[2*gbus[i]+1] -= IGr; */
725: row[0] = net_start + 2 * gbus[i] + 1;
726: col[0] = idx + 2;
727: col[1] = idx + 4;
728: col[2] = idx + 5;
729: val[0] = -dIGr_ddelta;
730: val[1] = -dIGr_dId;
731: val[2] = -dIGr_dIq;
732: PetscCall(MatSetValues(J, 1, row, 3, col, val, INSERT_VALUES));
734: Vm = PetscSqrtScalar(Vd * Vd + Vq * Vq);
736: /* fgen[idx+6] = (KE[i]*Efd + SE - VR)/TE[i]; */
737: /* SE = k1[i]*PetscExpScalar(k2[i]*Efd); */
738: dSE_dEfd = k1[i] * k2[i] * PetscExpScalar(k2[i] * Efd);
740: row[0] = idx + 6;
741: col[0] = idx + 6;
742: col[1] = idx + 8;
743: val[0] = (KE[i] + dSE_dEfd) / TE[i];
744: val[1] = -1 / TE[i];
745: PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES));
747: /* Exciter differential equations */
749: /* fgen[idx+7] = (RF - KF[i]*Efd/TF[i])/TF[i]; */
750: row[0] = idx + 7;
751: col[0] = idx + 6;
752: col[1] = idx + 7;
753: val[0] = (-KF[i] / TF[i]) / TF[i];
754: val[1] = 1 / TF[i];
755: PetscCall(MatSetValues(J, 1, row, 2, col, val, INSERT_VALUES));
757: /* fgen[idx+8] = (VR - KA[i]*RF + KA[i]*KF[i]*Efd/TF[i] - KA[i]*(Vref[i] - Vm))/TA[i]; */
758: /* Vm = (Vd^2 + Vq^2)^0.5; */
759: dVm_dVd = Vd / Vm;
760: dVm_dVq = Vq / Vm;
761: dVm_dVr = dVm_dVd * dVd_dVr + dVm_dVq * dVq_dVr;
762: dVm_dVi = dVm_dVd * dVd_dVi + dVm_dVq * dVq_dVi;
763: row[0] = idx + 8;
764: col[0] = idx + 6;
765: col[1] = idx + 7;
766: col[2] = idx + 8;
767: val[0] = (KA[i] * KF[i] / TF[i]) / TA[i];
768: val[1] = -KA[i] / TA[i];
769: val[2] = 1 / TA[i];
770: col[3] = net_start + 2 * gbus[i];
771: col[4] = net_start + 2 * gbus[i] + 1;
772: val[3] = KA[i] * dVm_dVr / TA[i];
773: val[4] = KA[i] * dVm_dVi / TA[i];
774: PetscCall(MatSetValues(J, 1, row, 5, col, val, INSERT_VALUES));
775: idx = idx + 9;
776: }
778: for (i = 0; i < nbus; i++) {
779: PetscCall(MatGetRow(user->Ybus, 2 * i, &ncols, &cols, &yvals));
780: row[0] = net_start + 2 * i;
781: for (k = 0; k < ncols; k++) {
782: col[k] = net_start + cols[k];
783: val[k] = yvals[k];
784: }
785: PetscCall(MatSetValues(J, 1, row, ncols, col, val, INSERT_VALUES));
786: PetscCall(MatRestoreRow(user->Ybus, 2 * i, &ncols, &cols, &yvals));
788: PetscCall(MatGetRow(user->Ybus, 2 * i + 1, &ncols, &cols, &yvals));
789: row[0] = net_start + 2 * i + 1;
790: for (k = 0; k < ncols; k++) {
791: col[k] = net_start + cols[k];
792: val[k] = yvals[k];
793: }
794: PetscCall(MatSetValues(J, 1, row, ncols, col, val, INSERT_VALUES));
795: PetscCall(MatRestoreRow(user->Ybus, 2 * i + 1, &ncols, &cols, &yvals));
796: }
798: PetscCall(MatAssemblyBegin(J, MAT_FLUSH_ASSEMBLY));
799: PetscCall(MatAssemblyEnd(J, MAT_FLUSH_ASSEMBLY));
801: PetscCall(VecGetArray(user->V0, &v0));
802: for (i = 0; i < nload; i++) {
803: Vr = xnet[2 * lbus[i]]; /* Real part of load bus voltage */
804: Vi = xnet[2 * lbus[i] + 1]; /* Imaginary part of the load bus voltage */
805: Vm = PetscSqrtScalar(Vr * Vr + Vi * Vi);
806: Vm2 = Vm * Vm;
807: Vm4 = Vm2 * Vm2;
808: Vm0 = PetscSqrtScalar(v0[2 * lbus[i]] * v0[2 * lbus[i]] + v0[2 * lbus[i] + 1] * v0[2 * lbus[i] + 1]);
809: PD = QD = 0.0;
810: dPD_dVr = dPD_dVi = dQD_dVr = dQD_dVi = 0.0;
811: for (k = 0; k < ld_nsegsp[i]; k++) {
812: PD += ld_alphap[k] * PD0[i] * PetscPowScalar((Vm / Vm0), ld_betap[k]);
813: dPD_dVr += ld_alphap[k] * ld_betap[k] * PD0[i] * PetscPowScalar((1 / Vm0), ld_betap[k]) * Vr * PetscPowScalar(Vm, (ld_betap[k] - 2));
814: dPD_dVi += ld_alphap[k] * ld_betap[k] * PD0[i] * PetscPowScalar((1 / Vm0), ld_betap[k]) * Vi * PetscPowScalar(Vm, (ld_betap[k] - 2));
815: }
816: for (k = 0; k < ld_nsegsq[i]; k++) {
817: QD += ld_alphaq[k] * QD0[i] * PetscPowScalar((Vm / Vm0), ld_betaq[k]);
818: dQD_dVr += ld_alphaq[k] * ld_betaq[k] * QD0[i] * PetscPowScalar((1 / Vm0), ld_betaq[k]) * Vr * PetscPowScalar(Vm, (ld_betaq[k] - 2));
819: dQD_dVi += ld_alphaq[k] * ld_betaq[k] * QD0[i] * PetscPowScalar((1 / Vm0), ld_betaq[k]) * Vi * PetscPowScalar(Vm, (ld_betaq[k] - 2));
820: }
822: /* IDr = (PD*Vr + QD*Vi)/Vm2; */
823: /* IDi = (-QD*Vr + PD*Vi)/Vm2; */
825: dIDr_dVr = (dPD_dVr * Vr + dQD_dVr * Vi + PD) / Vm2 - ((PD * Vr + QD * Vi) * 2 * Vr) / Vm4;
826: dIDr_dVi = (dPD_dVi * Vr + dQD_dVi * Vi + QD) / Vm2 - ((PD * Vr + QD * Vi) * 2 * Vi) / Vm4;
828: dIDi_dVr = (-dQD_dVr * Vr + dPD_dVr * Vi - QD) / Vm2 - ((-QD * Vr + PD * Vi) * 2 * Vr) / Vm4;
829: dIDi_dVi = (-dQD_dVi * Vr + dPD_dVi * Vi + PD) / Vm2 - ((-QD * Vr + PD * Vi) * 2 * Vi) / Vm4;
831: /* fnet[2*lbus[i]] += IDi; */
832: row[0] = net_start + 2 * lbus[i];
833: col[0] = net_start + 2 * lbus[i];
834: col[1] = net_start + 2 * lbus[i] + 1;
835: val[0] = dIDi_dVr;
836: val[1] = dIDi_dVi;
837: PetscCall(MatSetValues(J, 1, row, 2, col, val, ADD_VALUES));
838: /* fnet[2*lbus[i]+1] += IDr; */
839: row[0] = net_start + 2 * lbus[i] + 1;
840: col[0] = net_start + 2 * lbus[i];
841: col[1] = net_start + 2 * lbus[i] + 1;
842: val[0] = dIDr_dVr;
843: val[1] = dIDr_dVi;
844: PetscCall(MatSetValues(J, 1, row, 2, col, val, ADD_VALUES));
845: }
846: PetscCall(VecRestoreArray(user->V0, &v0));
848: PetscCall(VecRestoreArray(Xgen, &xgen));
849: PetscCall(VecRestoreArray(Xnet, &xnet));
851: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet));
853: PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
854: PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
855: PetscFunctionReturn(PETSC_SUCCESS);
856: }
858: /*
859: J = [I, 0
860: dg_dx, dg_dy]
861: */
862: PetscErrorCode AlgJacobian(SNES snes, Vec X, Mat A, Mat B, void *ctx)
863: {
864: Userctx *user = (Userctx *)ctx;
866: PetscFunctionBegin;
867: PetscCall(ResidualJacobian(snes, X, A, B, ctx));
868: PetscCall(MatSetOption(A, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE));
869: PetscCall(MatZeroRowsIS(A, user->is_diff, 1.0, NULL, NULL));
870: PetscFunctionReturn(PETSC_SUCCESS);
871: }
873: /*
874: J = [a*I-df_dx, -df_dy
875: dg_dx, dg_dy]
876: */
878: PetscErrorCode IJacobian(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, Userctx *user)
879: {
880: SNES snes;
881: PetscScalar atmp = (PetscScalar)a;
882: PetscInt i, row;
884: PetscFunctionBegin;
885: user->t = t;
887: PetscCall(TSGetSNES(ts, &snes));
888: PetscCall(ResidualJacobian(snes, X, A, B, user));
889: for (i = 0; i < ngen; i++) {
890: row = 9 * i;
891: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
892: row = 9 * i + 1;
893: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
894: row = 9 * i + 2;
895: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
896: row = 9 * i + 3;
897: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
898: row = 9 * i + 6;
899: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
900: row = 9 * i + 7;
901: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
902: row = 9 * i + 8;
903: PetscCall(MatSetValues(A, 1, &row, 1, &row, &atmp, ADD_VALUES));
904: }
905: PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
906: PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
907: PetscFunctionReturn(PETSC_SUCCESS);
908: }
910: /* Matrix JacobianP is constant so that it only needs to be evaluated once */
911: static PetscErrorCode RHSJacobianP(TS ts, PetscReal t, Vec X, Mat A, void *ctx0)
912: {
913: PetscScalar a;
914: PetscInt row, col;
915: Userctx *ctx = (Userctx *)ctx0;
917: PetscFunctionBeginUser;
918: if (ctx->jacp_flg) {
919: PetscCall(MatZeroEntries(A));
921: for (col = 0; col < 3; col++) {
922: a = 1.0 / M[col];
923: row = 9 * col + 3;
924: PetscCall(MatSetValues(A, 1, &row, 1, &col, &a, INSERT_VALUES));
925: }
927: ctx->jacp_flg = PETSC_FALSE;
929: PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
930: PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
931: }
932: PetscFunctionReturn(PETSC_SUCCESS);
933: }
935: static PetscErrorCode CostIntegrand(TS ts, PetscReal t, Vec U, Vec R, Userctx *user)
936: {
937: const PetscScalar *u;
938: PetscInt idx;
939: Vec Xgen, Xnet;
940: PetscScalar *r, *xgen;
941: PetscInt i;
943: PetscFunctionBegin;
944: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet));
945: PetscCall(DMCompositeScatter(user->dmpgrid, U, Xgen, Xnet));
947: PetscCall(VecGetArray(Xgen, &xgen));
949: PetscCall(VecGetArrayRead(U, &u));
950: PetscCall(VecGetArray(R, &r));
951: r[0] = 0.;
952: idx = 0;
953: for (i = 0; i < ngen; i++) {
954: 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);
955: idx += 9;
956: }
957: PetscCall(VecRestoreArrayRead(U, &u));
958: PetscCall(VecRestoreArray(R, &r));
959: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet));
960: PetscFunctionReturn(PETSC_SUCCESS);
961: }
963: static PetscErrorCode DRDUJacobianTranspose(TS ts, PetscReal t, Vec U, Mat DRDU, Mat B, Userctx *user)
964: {
965: Vec Xgen, Xnet, Dgen, Dnet;
966: PetscScalar *xgen, *dgen;
967: PetscInt i;
968: PetscInt idx;
969: Vec drdu_col;
970: PetscScalar *xarr;
972: PetscFunctionBegin;
973: PetscCall(VecDuplicate(U, &drdu_col));
974: PetscCall(MatDenseGetColumn(DRDU, 0, &xarr));
975: PetscCall(VecPlaceArray(drdu_col, xarr));
976: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Xgen, &Xnet));
977: PetscCall(DMCompositeGetLocalVectors(user->dmpgrid, &Dgen, &Dnet));
978: PetscCall(DMCompositeScatter(user->dmpgrid, U, Xgen, Xnet));
979: PetscCall(DMCompositeScatter(user->dmpgrid, drdu_col, Dgen, Dnet));
981: PetscCall(VecGetArray(Xgen, &xgen));
982: PetscCall(VecGetArray(Dgen, &dgen));
984: idx = 0;
985: for (i = 0; i < ngen; i++) {
986: dgen[idx + 3] = 0.;
987: 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);
988: 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);
989: idx += 9;
990: }
992: PetscCall(VecRestoreArray(Dgen, &dgen));
993: PetscCall(VecRestoreArray(Xgen, &xgen));
994: PetscCall(DMCompositeGather(user->dmpgrid, INSERT_VALUES, drdu_col, Dgen, Dnet));
995: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Dgen, &Dnet));
996: PetscCall(DMCompositeRestoreLocalVectors(user->dmpgrid, &Xgen, &Xnet));
997: PetscCall(VecResetArray(drdu_col));
998: PetscCall(MatDenseRestoreColumn(DRDU, &xarr));
999: PetscCall(VecDestroy(&drdu_col));
1000: PetscFunctionReturn(PETSC_SUCCESS);
1001: }
1003: static PetscErrorCode DRDPJacobianTranspose(TS ts, PetscReal t, Vec U, Mat drdp, Userctx *user)
1004: {
1005: PetscFunctionBegin;
1006: PetscFunctionReturn(PETSC_SUCCESS);
1007: }
1009: PetscErrorCode ComputeSensiP(Vec lambda, Vec mu, Vec *DICDP, Userctx *user)
1010: {
1011: PetscScalar *x, *y, sensip;
1012: PetscInt i;
1014: PetscFunctionBegin;
1015: PetscCall(VecGetArray(lambda, &x));
1016: PetscCall(VecGetArray(mu, &y));
1018: for (i = 0; i < 3; i++) {
1019: PetscCall(VecDot(lambda, DICDP[i], &sensip));
1020: sensip = sensip + y[i];
1021: /* PetscCall(PetscPrintf(PETSC_COMM_WORLD,"\n sensitivity wrt %" PetscInt_FMT " th parameter: %g \n",i,(double)sensip)); */
1022: y[i] = sensip;
1023: }
1024: PetscCall(VecRestoreArray(mu, &y));
1025: PetscFunctionReturn(PETSC_SUCCESS);
1026: }
1028: int main(int argc, char **argv)
1029: {
1030: Userctx user;
1031: Vec p;
1032: PetscScalar *x_ptr;
1033: PetscMPIInt size;
1034: PetscInt i;
1035: PetscViewer Xview, Ybusview;
1036: PetscInt *idx2;
1037: Tao tao;
1038: KSP ksp;
1039: PC pc;
1040: Vec lowerb, upperb;
1042: PetscFunctionBeginUser;
1043: PetscCall(PetscInitialize(&argc, &argv, "petscoptions", help));
1044: PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
1045: PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Only for sequential runs");
1047: user.jacp_flg = PETSC_TRUE;
1048: user.neqs_gen = 9 * ngen; /* # eqs. for generator subsystem */
1049: user.neqs_net = 2 * nbus; /* # eqs. for network subsystem */
1050: user.neqs_pgrid = user.neqs_gen + user.neqs_net;
1052: /* Create indices for differential and algebraic equations */
1053: PetscCall(PetscMalloc1(7 * ngen, &idx2));
1054: for (i = 0; i < ngen; i++) {
1055: idx2[7 * i] = 9 * i;
1056: idx2[7 * i + 1] = 9 * i + 1;
1057: idx2[7 * i + 2] = 9 * i + 2;
1058: idx2[7 * i + 3] = 9 * i + 3;
1059: idx2[7 * i + 4] = 9 * i + 6;
1060: idx2[7 * i + 5] = 9 * i + 7;
1061: idx2[7 * i + 6] = 9 * i + 8;
1062: }
1063: PetscCall(ISCreateGeneral(PETSC_COMM_WORLD, 7 * ngen, idx2, PETSC_COPY_VALUES, &user.is_diff));
1064: PetscCall(ISComplement(user.is_diff, 0, user.neqs_pgrid, &user.is_alg));
1065: PetscCall(PetscFree(idx2));
1067: /* Set run time options */
1068: PetscOptionsBegin(PETSC_COMM_WORLD, NULL, "Transient stability fault options", "");
1069: {
1070: user.tfaulton = 1.0;
1071: user.tfaultoff = 1.2;
1072: user.Rfault = 0.0001;
1073: user.faultbus = 8;
1074: PetscCall(PetscOptionsReal("-tfaulton", "", "", user.tfaulton, &user.tfaulton, NULL));
1075: PetscCall(PetscOptionsReal("-tfaultoff", "", "", user.tfaultoff, &user.tfaultoff, NULL));
1076: PetscCall(PetscOptionsInt("-faultbus", "", "", user.faultbus, &user.faultbus, NULL));
1077: user.t0 = 0.0;
1078: user.tmax = 1.3;
1079: PetscCall(PetscOptionsReal("-t0", "", "", user.t0, &user.t0, NULL));
1080: PetscCall(PetscOptionsReal("-tmax", "", "", user.tmax, &user.tmax, NULL));
1081: user.freq_u = 61.0;
1082: user.freq_l = 59.0;
1083: user.pow = 2;
1084: PetscCall(PetscOptionsReal("-frequ", "", "", user.freq_u, &user.freq_u, NULL));
1085: PetscCall(PetscOptionsReal("-freql", "", "", user.freq_l, &user.freq_l, NULL));
1086: PetscCall(PetscOptionsInt("-pow", "", "", user.pow, &user.pow, NULL));
1087: }
1088: PetscOptionsEnd();
1090: /* Create DMs for generator and network subsystems */
1091: PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, user.neqs_gen, 1, 1, NULL, &user.dmgen));
1092: PetscCall(DMSetOptionsPrefix(user.dmgen, "dmgen_"));
1093: PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, user.neqs_net, 1, 1, NULL, &user.dmnet));
1094: PetscCall(DMSetOptionsPrefix(user.dmnet, "dmnet_"));
1095: PetscCall(DMSetFromOptions(user.dmnet));
1096: PetscCall(DMSetUp(user.dmnet));
1097: /* Create a composite DM packer and add the two DMs */
1098: PetscCall(DMCompositeCreate(PETSC_COMM_WORLD, &user.dmpgrid));
1099: PetscCall(DMSetOptionsPrefix(user.dmpgrid, "pgrid_"));
1100: PetscCall(DMSetFromOptions(user.dmgen));
1101: PetscCall(DMSetUp(user.dmgen));
1102: PetscCall(DMCompositeAddDM(user.dmpgrid, user.dmgen));
1103: PetscCall(DMCompositeAddDM(user.dmpgrid, user.dmnet));
1105: /* Read initial voltage vector and Ybus */
1106: PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, "X.bin", FILE_MODE_READ, &Xview));
1107: PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, "Ybus.bin", FILE_MODE_READ, &Ybusview));
1109: PetscCall(VecCreate(PETSC_COMM_WORLD, &user.V0));
1110: PetscCall(VecSetSizes(user.V0, PETSC_DECIDE, user.neqs_net));
1111: PetscCall(VecLoad(user.V0, Xview));
1113: PetscCall(MatCreate(PETSC_COMM_WORLD, &user.Ybus));
1114: PetscCall(MatSetSizes(user.Ybus, PETSC_DECIDE, PETSC_DECIDE, user.neqs_net, user.neqs_net));
1115: PetscCall(MatSetType(user.Ybus, MATBAIJ));
1116: /* PetscCall(MatSetBlockSize(ctx->Ybus,2)); */
1117: PetscCall(MatLoad(user.Ybus, Ybusview));
1119: PetscCall(PetscViewerDestroy(&Xview));
1120: PetscCall(PetscViewerDestroy(&Ybusview));
1122: /* Allocate space for Jacobians */
1123: PetscCall(MatCreate(PETSC_COMM_WORLD, &user.J));
1124: PetscCall(MatSetSizes(user.J, PETSC_DECIDE, PETSC_DECIDE, user.neqs_pgrid, user.neqs_pgrid));
1125: PetscCall(MatSetFromOptions(user.J));
1126: PetscCall(PreallocateJacobian(user.J, &user));
1128: PetscCall(MatCreate(PETSC_COMM_WORLD, &user.Jacp));
1129: PetscCall(MatSetSizes(user.Jacp, PETSC_DECIDE, PETSC_DECIDE, user.neqs_pgrid, 3));
1130: PetscCall(MatSetFromOptions(user.Jacp));
1131: PetscCall(MatSetUp(user.Jacp));
1132: PetscCall(MatZeroEntries(user.Jacp)); /* initialize to zeros */
1134: PetscCall(MatCreateDense(PETSC_COMM_WORLD, PETSC_DECIDE, PETSC_DECIDE, 3, 1, NULL, &user.DRDP));
1135: PetscCall(MatSetUp(user.DRDP));
1136: PetscCall(MatCreateDense(PETSC_COMM_WORLD, PETSC_DECIDE, PETSC_DECIDE, user.neqs_pgrid, 1, NULL, &user.DRDU));
1137: PetscCall(MatSetUp(user.DRDU));
1139: /* Create TAO solver and set desired solution method */
1140: PetscCall(TaoCreate(PETSC_COMM_WORLD, &tao));
1141: PetscCall(TaoSetType(tao, TAOBLMVM));
1142: /*
1143: Optimization starts
1144: */
1145: /* Set initial solution guess */
1146: PetscCall(VecCreateSeq(PETSC_COMM_WORLD, 3, &p));
1147: PetscCall(VecGetArray(p, &x_ptr));
1148: x_ptr[0] = PG[0];
1149: x_ptr[1] = PG[1];
1150: x_ptr[2] = PG[2];
1151: PetscCall(VecRestoreArray(p, &x_ptr));
1153: PetscCall(TaoSetSolution(tao, p));
1154: /* Set routine for function and gradient evaluation */
1155: PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, &user));
1157: /* Set bounds for the optimization */
1158: PetscCall(VecDuplicate(p, &lowerb));
1159: PetscCall(VecDuplicate(p, &upperb));
1160: PetscCall(VecGetArray(lowerb, &x_ptr));
1161: x_ptr[0] = 0.5;
1162: x_ptr[1] = 0.5;
1163: x_ptr[2] = 0.5;
1164: PetscCall(VecRestoreArray(lowerb, &x_ptr));
1165: PetscCall(VecGetArray(upperb, &x_ptr));
1166: x_ptr[0] = 2.0;
1167: x_ptr[1] = 2.0;
1168: x_ptr[2] = 2.0;
1169: PetscCall(VecRestoreArray(upperb, &x_ptr));
1170: PetscCall(TaoSetVariableBounds(tao, lowerb, upperb));
1172: /* Check for any TAO command line options */
1173: PetscCall(TaoSetFromOptions(tao));
1174: PetscCall(TaoGetKSP(tao, &ksp));
1175: if (ksp) {
1176: PetscCall(KSPGetPC(ksp, &pc));
1177: PetscCall(PCSetType(pc, PCNONE));
1178: }
1180: /* SOLVE THE APPLICATION */
1181: PetscCall(TaoSolve(tao));
1183: PetscCall(VecView(p, PETSC_VIEWER_STDOUT_WORLD));
1184: /* Free TAO data structures */
1185: PetscCall(TaoDestroy(&tao));
1187: PetscCall(DMDestroy(&user.dmgen));
1188: PetscCall(DMDestroy(&user.dmnet));
1189: PetscCall(DMDestroy(&user.dmpgrid));
1190: PetscCall(ISDestroy(&user.is_diff));
1191: PetscCall(ISDestroy(&user.is_alg));
1193: PetscCall(MatDestroy(&user.J));
1194: PetscCall(MatDestroy(&user.Jacp));
1195: PetscCall(MatDestroy(&user.Ybus));
1196: /* PetscCall(MatDestroy(&user.Sol)); */
1197: PetscCall(VecDestroy(&user.V0));
1198: PetscCall(VecDestroy(&p));
1199: PetscCall(VecDestroy(&lowerb));
1200: PetscCall(VecDestroy(&upperb));
1201: PetscCall(MatDestroy(&user.DRDU));
1202: PetscCall(MatDestroy(&user.DRDP));
1203: PetscCall(PetscFinalize());
1204: return 0;
1205: }
1207: /* ------------------------------------------------------------------ */
1208: /*
1209: FormFunction - Evaluates the function and corresponding gradient.
1211: Input Parameters:
1212: tao - the Tao context
1213: X - the input vector
1214: ptr - optional user-defined context, as set by TaoSetObjectiveAndGradient()
1216: Output Parameters:
1217: f - the newly evaluated function
1218: G - the newly evaluated gradient
1219: */
1220: PetscErrorCode FormFunctionGradient(Tao tao, Vec P, PetscReal *f, Vec G, void *ctx0)
1221: {
1222: TS ts, quadts;
1223: SNES snes_alg;
1224: Userctx *ctx = (Userctx *)ctx0;
1225: Vec X;
1226: PetscInt i;
1227: /* sensitivity context */
1228: PetscScalar *x_ptr;
1229: Vec lambda[1], q;
1230: Vec mu[1];
1231: PetscInt steps1, steps2, steps3;
1232: Vec DICDP[3];
1233: Vec F_alg;
1234: PetscInt row_loc, col_loc;
1235: PetscScalar val;
1236: Vec Xdot;
1238: PetscFunctionBegin;
1239: PetscCall(VecGetArrayRead(P, (const PetscScalar **)&x_ptr));
1240: PG[0] = x_ptr[0];
1241: PG[1] = x_ptr[1];
1242: PG[2] = x_ptr[2];
1243: PetscCall(VecRestoreArrayRead(P, (const PetscScalar **)&x_ptr));
1245: ctx->stepnum = 0;
1247: PetscCall(DMCreateGlobalVector(ctx->dmpgrid, &X));
1249: /* Create matrix to save solutions at each time step */
1250: /* PetscCall(MatCreateSeqDense(PETSC_COMM_SELF,ctx->neqs_pgrid+1,1002,NULL,&ctx->Sol)); */
1251: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1252: Create timestepping solver context
1253: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1254: PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
1255: PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
1256: PetscCall(TSSetType(ts, TSCN));
1257: PetscCall(TSSetIFunction(ts, NULL, (TSIFunctionFn *)IFunction, ctx));
1258: PetscCall(TSSetIJacobian(ts, ctx->J, ctx->J, (TSIJacobianFn *)IJacobian, ctx));
1259: PetscCall(TSSetApplicationContext(ts, ctx));
1260: /* Set RHS JacobianP */
1261: PetscCall(TSSetRHSJacobianP(ts, ctx->Jacp, RHSJacobianP, ctx));
1263: PetscCall(TSCreateQuadratureTS(ts, PETSC_FALSE, &quadts));
1264: PetscCall(TSSetRHSFunction(quadts, NULL, (TSRHSFunctionFn *)CostIntegrand, ctx));
1265: PetscCall(TSSetRHSJacobian(quadts, ctx->DRDU, ctx->DRDU, (TSRHSJacobianFn *)DRDUJacobianTranspose, ctx));
1266: PetscCall(TSSetRHSJacobianP(quadts, ctx->DRDP, (TSRHSJacobianPFn *)DRDPJacobianTranspose, ctx));
1268: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1269: Set initial conditions
1270: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1271: PetscCall(SetInitialGuess(X, ctx));
1273: /* Approximate DICDP with finite difference, we want to zero out network variables */
1274: for (i = 0; i < 3; i++) PetscCall(VecDuplicate(X, &DICDP[i]));
1275: PetscCall(DICDPFiniteDifference(X, DICDP, ctx));
1277: PetscCall(VecDuplicate(X, &F_alg));
1278: PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes_alg));
1279: PetscCall(SNESSetFunction(snes_alg, F_alg, AlgFunction, ctx));
1280: PetscCall(MatZeroEntries(ctx->J));
1281: PetscCall(SNESSetJacobian(snes_alg, ctx->J, ctx->J, AlgJacobian, ctx));
1282: PetscCall(SNESSetOptionsPrefix(snes_alg, "alg_"));
1283: PetscCall(SNESSetFromOptions(snes_alg));
1284: ctx->alg_flg = PETSC_TRUE;
1285: /* Solve the algebraic equations */
1286: PetscCall(SNESSolve(snes_alg, NULL, X));
1288: /* Just to set up the Jacobian structure */
1289: PetscCall(VecDuplicate(X, &Xdot));
1290: PetscCall(IJacobian(ts, 0.0, X, Xdot, 0.0, ctx->J, ctx->J, ctx));
1291: PetscCall(VecDestroy(&Xdot));
1293: ctx->stepnum++;
1295: /*
1296: Save trajectory of solution so that TSAdjointSolve() may be used
1297: */
1298: PetscCall(TSSetSaveTrajectory(ts));
1300: PetscCall(TSSetTimeStep(ts, 0.01));
1301: PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP));
1302: PetscCall(TSSetFromOptions(ts));
1303: /* PetscCall(TSSetPostStep(ts,SaveSolution)); */
1305: /* Prefault period */
1306: ctx->alg_flg = PETSC_FALSE;
1307: PetscCall(TSSetTime(ts, 0.0));
1308: PetscCall(TSSetMaxTime(ts, ctx->tfaulton));
1309: PetscCall(TSSolve(ts, X));
1310: PetscCall(TSGetStepNumber(ts, &steps1));
1312: /* Create the nonlinear solver for solving the algebraic system */
1313: /* Note that although the algebraic system needs to be solved only for
1314: Idq and V, we reuse the entire system including xgen. The xgen
1315: variables are held constant by setting their residuals to 0 and
1316: putting a 1 on the Jacobian diagonal for xgen rows
1317: */
1318: PetscCall(MatZeroEntries(ctx->J));
1320: /* Apply disturbance - resistive fault at ctx->faultbus */
1321: /* This is done by adding shunt conductance to the diagonal location
1322: in the Ybus matrix */
1323: row_loc = 2 * ctx->faultbus;
1324: col_loc = 2 * ctx->faultbus + 1; /* Location for G */
1325: val = 1 / ctx->Rfault;
1326: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1327: row_loc = 2 * ctx->faultbus + 1;
1328: col_loc = 2 * ctx->faultbus; /* Location for G */
1329: val = 1 / ctx->Rfault;
1330: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1332: PetscCall(MatAssemblyBegin(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1333: PetscCall(MatAssemblyEnd(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1335: ctx->alg_flg = PETSC_TRUE;
1336: /* Solve the algebraic equations */
1337: PetscCall(SNESSolve(snes_alg, NULL, X));
1339: ctx->stepnum++;
1341: /* Disturbance period */
1342: ctx->alg_flg = PETSC_FALSE;
1343: PetscCall(TSSetTime(ts, ctx->tfaulton));
1344: PetscCall(TSSetMaxTime(ts, ctx->tfaultoff));
1345: PetscCall(TSSolve(ts, X));
1346: PetscCall(TSGetStepNumber(ts, &steps2));
1347: steps2 -= steps1;
1349: /* Remove the fault */
1350: row_loc = 2 * ctx->faultbus;
1351: col_loc = 2 * ctx->faultbus + 1;
1352: val = -1 / ctx->Rfault;
1353: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1354: row_loc = 2 * ctx->faultbus + 1;
1355: col_loc = 2 * ctx->faultbus;
1356: val = -1 / ctx->Rfault;
1357: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1359: PetscCall(MatAssemblyBegin(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1360: PetscCall(MatAssemblyEnd(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1362: PetscCall(MatZeroEntries(ctx->J));
1364: ctx->alg_flg = PETSC_TRUE;
1366: /* Solve the algebraic equations */
1367: PetscCall(SNESSolve(snes_alg, NULL, X));
1369: ctx->stepnum++;
1371: /* Post-disturbance period */
1372: ctx->alg_flg = PETSC_TRUE;
1373: PetscCall(TSSetTime(ts, ctx->tfaultoff));
1374: PetscCall(TSSetMaxTime(ts, ctx->tmax));
1375: PetscCall(TSSolve(ts, X));
1376: PetscCall(TSGetStepNumber(ts, &steps3));
1377: steps3 -= steps2;
1378: steps3 -= steps1;
1380: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1381: Adjoint model starts here
1382: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1383: PetscCall(TSSetPostStep(ts, NULL));
1384: PetscCall(MatCreateVecs(ctx->J, &lambda[0], NULL));
1385: /* Set initial conditions for the adjoint integration */
1386: PetscCall(VecZeroEntries(lambda[0]));
1388: PetscCall(MatCreateVecs(ctx->Jacp, &mu[0], NULL));
1389: PetscCall(VecZeroEntries(mu[0]));
1390: PetscCall(TSSetCostGradients(ts, 1, lambda, mu));
1392: PetscCall(TSAdjointSetSteps(ts, steps3));
1393: PetscCall(TSAdjointSolve(ts));
1395: PetscCall(MatZeroEntries(ctx->J));
1396: /* Applying disturbance - resistive fault at ctx->faultbus */
1397: /* This is done by deducting shunt conductance to the diagonal location
1398: in the Ybus matrix */
1399: row_loc = 2 * ctx->faultbus;
1400: col_loc = 2 * ctx->faultbus + 1; /* Location for G */
1401: val = 1. / ctx->Rfault;
1402: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1403: row_loc = 2 * ctx->faultbus + 1;
1404: col_loc = 2 * ctx->faultbus; /* Location for G */
1405: val = 1. / ctx->Rfault;
1406: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1408: PetscCall(MatAssemblyBegin(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1409: PetscCall(MatAssemblyEnd(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1411: /* Set number of steps for the adjoint integration */
1412: PetscCall(TSAdjointSetSteps(ts, steps2));
1413: PetscCall(TSAdjointSolve(ts));
1415: PetscCall(MatZeroEntries(ctx->J));
1416: /* remove the fault */
1417: row_loc = 2 * ctx->faultbus;
1418: col_loc = 2 * ctx->faultbus + 1; /* Location for G */
1419: val = -1. / ctx->Rfault;
1420: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1421: row_loc = 2 * ctx->faultbus + 1;
1422: col_loc = 2 * ctx->faultbus; /* Location for G */
1423: val = -1. / ctx->Rfault;
1424: PetscCall(MatSetValues(ctx->Ybus, 1, &row_loc, 1, &col_loc, &val, ADD_VALUES));
1426: PetscCall(MatAssemblyBegin(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1427: PetscCall(MatAssemblyEnd(ctx->Ybus, MAT_FINAL_ASSEMBLY));
1429: /* Set number of steps for the adjoint integration */
1430: PetscCall(TSAdjointSetSteps(ts, steps1));
1431: PetscCall(TSAdjointSolve(ts));
1433: PetscCall(ComputeSensiP(lambda[0], mu[0], DICDP, ctx));
1434: PetscCall(VecCopy(mu[0], G));
1436: PetscCall(TSGetQuadratureTS(ts, NULL, &quadts));
1437: PetscCall(TSGetSolution(quadts, &q));
1438: PetscCall(VecGetArray(q, &x_ptr));
1439: *f = x_ptr[0];
1440: x_ptr[0] = 0;
1441: PetscCall(VecRestoreArray(q, &x_ptr));
1443: PetscCall(VecDestroy(&lambda[0]));
1444: PetscCall(VecDestroy(&mu[0]));
1446: PetscCall(SNESDestroy(&snes_alg));
1447: PetscCall(VecDestroy(&F_alg));
1448: PetscCall(VecDestroy(&X));
1449: PetscCall(TSDestroy(&ts));
1450: for (i = 0; i < 3; i++) PetscCall(VecDestroy(&DICDP[i]));
1451: PetscFunctionReturn(PETSC_SUCCESS);
1452: }
1454: /*TEST
1456: build:
1457: requires: double !complex !defined(PETSC_USE_64BIT_INDICES)
1459: test:
1460: args: -viewer_binary_skip_info -tao_monitor -tao_gttol .2
1461: localrunfiles: petscoptions X.bin Ybus.bin
1463: TEST*/