Actual source code: biharmonic.c
petsc-3.4.2 2013-07-02
2: static char help[] = "Solves biharmonic equation in 1d.\n";
4: /*
5: Solves the equation
7: u_t = - kappa \Delta \Delta u
8: Periodic boundary conditions
10: Evolve the biharmonic heat equation:
11: ---------------
12: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu -draw_pause .1 -snes_converged_reason -wait -ts_type cn -da_refine 5 -mymonitor
14: Evolve with the restriction that -1 <= u <= 1; i.e. as a variational inequality
15: ---------------
16: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu -draw_pause .1 -snes_converged_reason -wait -ts_type cn -da_refine 5 -vi -mymonitor
18: u_t = kappa \Delta \Delta u + 6.*u*(u_x)^2 + (3*u^2 - 12) \Delta u
19: -1 <= u <= 1
20: Periodic boundary conditions
22: Evolve the Cahn-Hillard equations: double well Initial hump shrinks then grows
23: ---------------
24: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu -draw_pause .1 -snes_converged_reason -wait -ts_type cn -da_refine 6 -vi -kappa .00001 -ts_dt 5.96046e-06 -cahn-hillard -ts_monitor_draw_solution --mymonitor
26: Initial hump neither shrinks nor grows when degenerate (otherwise similar solution)
28: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu -draw_pause .1 -snes_converged_reason -wait -ts_type cn -da_refine 6 -vi -kappa .00001 -ts_dt 5.96046e-06 -cahn-hillard -degenerate -ts_monitor_draw_solution --mymonitor
30: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu -draw_pause .1 -snes_converged_reason -wait -ts_type cn -da_refine 6 -vi -kappa .00001 -ts_dt 5.96046e-06 -cahn-hillard -snes_vi_ignore_function_sign -ts_monitor_draw_solution --mymonitor
32: Evolve the Cahn-Hillard equations: double obstacle
33: ---------------
34: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu -draw_pause .1 -snes_converged_reason -wait -ts_type cn -da_refine 5 -vi -kappa .00001 -ts_dt 5.96046e-06 -cahn-hillard -energy 2 -snes_linesearch_monitor -vi -ts_monitor_draw_solution --mymonitor
36: Evolve the Cahn-Hillard equations: logarithmic + double well (never shrinks and then grows)
37: ---------------
38: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu --snes_converged_reason -wait -ts_type cn -da_refine 5 -vi -kappa .0001 -ts_dt 5.96046e-06 -cahn-hillard -energy 3 -snes_linesearch_monitor -theta .00000001 -vi -ts_monitor_draw_solution --ts_final_time 1. -mymonitor
40: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu --snes_converged_reason -wait -ts_type cn -da_refine 5 -vi -kappa .0001 -ts_dt 5.96046e-06 -cahn-hillard -energy 3 -snes_linesearch_monitor -theta .00000001 -vi -ts_monitor_draw_solution --ts_final_time 1. -degenerate -mymonitor
43: Evolve the Cahn-Hillard equations: logarithmic + double obstacle (never shrinks, never grows)
44: ---------------
45: ./biharmonic -ts_monitor -snes_vi_monitor -pc_type lu --snes_converged_reason -wait -ts_type cn -da_refine 5 -vi -kappa .00001 -ts_dt 5.96046e-06 -cahn-hillard -energy 4 -snes_linesearch_monitor -theta .00000001 -vi -ts_monitor_draw_solution --mymonitor
50: */
51: #include <petscdmda.h>
52: #include <petscts.h>
53: #include <petscdraw.h>
55: extern PetscErrorCode FormFunction(TS,PetscReal,Vec,Vec,void*),FormInitialSolution(DM,Vec),MyMonitor(TS,PetscInt,PetscReal,Vec,void*),MyDestroy(void**),FormJacobian(TS,PetscReal,Vec,Mat*,Mat*,MatStructure*,void*);
56: typedef struct {PetscBool cahnhillard;PetscBool degenerate;PetscReal kappa;PetscInt energy;PetscReal tol;PetscReal theta,theta_c;PetscInt truncation;PetscBool netforce; PetscDrawViewPorts *ports;} UserCtx;
60: int main(int argc,char **argv)
61: {
62: TS ts; /* nonlinear solver */
63: Vec x,r; /* solution, residual vectors */
64: Mat J; /* Jacobian matrix */
65: PetscInt steps,Mx,maxsteps = 10000000;
67: DM da;
68: PetscReal dt;
69: PetscReal vbounds[] = {-1.1,1.1};
70: PetscBool wait,vi = PETSC_FALSE,mymonitor;
71: Vec ul,uh;
72: UserCtx ctx;
74: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
75: Initialize program
76: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
77: PetscInitialize(&argc,&argv,(char*)0,help);
78: ctx.kappa = 1.0;
79: PetscOptionsGetReal(NULL,"-kappa",&ctx.kappa,NULL);
80: ctx.degenerate = PETSC_FALSE;
81: PetscOptionsGetBool(NULL,"-degenerate",&ctx.degenerate,NULL);
82: ctx.cahnhillard = PETSC_FALSE;
83: PetscOptionsGetBool(NULL,"-cahn-hillard",&ctx.cahnhillard,NULL);
84: PetscOptionsGetBool(NULL,"-vi",&vi,NULL);
85: ctx.netforce = PETSC_FALSE;
86: PetscOptionsGetBool(NULL,"-netforce",&ctx.netforce,NULL);
87: ctx.energy = 1;
88: PetscOptionsInt("-energy","type of energy (1=double well, 2=double obstacle, 3=logarithmic+double well, 4=logarithmic+double obstacle)","",ctx.energy,&ctx.energy,NULL);
89: ctx.tol = 1.0e-8;
90: PetscOptionsGetReal(NULL,"-tol",&ctx.tol,NULL);
91: ctx.theta = .001;
92: ctx.theta_c = 1.0;
93: PetscOptionsGetReal(NULL,"-theta",&ctx.theta,NULL);
94: PetscOptionsGetReal(NULL,"-theta_c",&ctx.theta_c,NULL);
95: ctx.truncation = 1;
96: PetscOptionsInt("-truncation","order of log truncation (1=cubic, 2=quadratic)","",ctx.truncation,&ctx.truncation,NULL);
97: PetscOptionsHasName(NULL,"-mymonitor",&mymonitor);
98: PetscViewerDrawSetBounds(PETSC_VIEWER_DRAW_(PETSC_COMM_WORLD),1,vbounds);
99: PetscViewerDrawResize(PETSC_VIEWER_DRAW_(PETSC_COMM_WORLD),800,600);
101: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102: Create distributed array (DMDA) to manage parallel grid and vectors
103: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
104: DMDACreate1d(PETSC_COMM_WORLD, DMDA_BOUNDARY_PERIODIC, -10,1,2,NULL,&da);
105: DMDASetFieldName(da,0,"Biharmonic heat equation: u");
106: DMDAGetInfo(da,0,&Mx,0,0,0,0,0,0,0,0,0,0,0);
107: dt = 1.0/(10.*ctx.kappa*Mx*Mx*Mx*Mx);
109: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
110: Extract global vectors from DMDA; then duplicate for remaining
111: vectors that are the same types
112: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
113: DMCreateGlobalVector(da,&x);
114: VecDuplicate(x,&r);
116: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
117: Create timestepping solver context
118: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
119: TSCreate(PETSC_COMM_WORLD,&ts);
120: TSSetDM(ts,da);
121: TSSetProblemType(ts,TS_NONLINEAR);
122: TSSetRHSFunction(ts,NULL,FormFunction,&ctx);
123: DMCreateMatrix(da,MATAIJ,&J);
124: TSSetRHSJacobian(ts,J,J,FormJacobian,&ctx);
125: TSSetDuration(ts,maxsteps,.02);
126: TSSetExactFinalTime(ts,TS_EXACTFINALTIME_INTERPOLATE);
128: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129: Create matrix data structure; set Jacobian evaluation routine
131: Set Jacobian matrix data structure and default Jacobian evaluation
132: routine. User can override with:
133: -snes_mf : matrix-free Newton-Krylov method with no preconditioning
134: (unless user explicitly sets preconditioner)
135: -snes_mf_operator : form preconditioning matrix as set by the user,
136: but use matrix-free approx for Jacobian-vector
137: products within Newton-Krylov method
139: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
140: #if defined(f00)
141: {
142: SNES snes;
143: DMCreateColoring(da,IS_COLORING_GLOBAL,MATAIJ,&iscoloring);
144: MatFDColoringCreate(J,iscoloring,&matfdcoloring);
145: ISColoringDestroy(&iscoloring);
146: MatFDColoringSetFunction(matfdcoloring,(PetscErrorCode (*)(void))SNESTSFormFunction,ts);
147: MatFDColoringSetFromOptions(matfdcoloring);
148: TSGetSNES(ts,&snes);
149: SNESSetJacobian(snes,J,J,SNESComputeJacobianDefaultColor,matfdcoloring);
150: }
151: #endif
153: if (vi) {
154: VecDuplicate(x,&ul);
155: VecDuplicate(x,&uh);
156: VecSet(ul,-1.0);
157: VecSet(uh,1.0);
158: TSVISetVariableBounds(ts,ul,uh);
159: }
161: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162: Customize nonlinear solver
163: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
164: TSSetType(ts,TSCN);
166: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167: Set initial conditions
168: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
169: FormInitialSolution(da,x);
170: TSSetInitialTimeStep(ts,0.0,dt);
171: TSSetSolution(ts,x);
173: if (mymonitor) {
174: ctx.ports = NULL;
175: TSMonitorSet(ts,MyMonitor,&ctx,MyDestroy);
176: }
178: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179: Set runtime options
180: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
181: TSSetFromOptions(ts);
183: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184: Solve nonlinear system
185: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
186: TSSolve(ts,x);
187: wait = PETSC_FALSE;
188: PetscOptionsGetBool(NULL,"-wait",&wait,NULL);
189: if (wait) {
190: PetscSleep(-1);
191: }
192: TSGetTimeStepNumber(ts,&steps);
193: VecView(x,PETSC_VIEWER_BINARY_WORLD);
195: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
196: Free work space. All PETSc objects should be destroyed when they
197: are no longer needed.
198: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
199: if (vi) {
200: VecDestroy(&ul);
201: VecDestroy(&uh);
202: }
203: MatDestroy(&J);
204: #if defined(f00)
205: MatFDColoringDestroy(&matfdcoloring);
206: #endif
207: VecDestroy(&x);
208: VecDestroy(&r);
209: TSDestroy(&ts);
210: DMDestroy(&da);
212: PetscFinalize();
213: return(0);
214: }
215: /* ------------------------------------------------------------------- */
218: /*
219: FormFunction - Evaluates nonlinear function, F(x).
221: Input Parameters:
222: . ts - the TS context
223: . X - input vector
224: . ptr - optional user-defined context, as set by SNESSetFunction()
226: Output Parameter:
227: . F - function vector
228: */
229: PetscErrorCode FormFunction(TS ts,PetscReal ftime,Vec X,Vec F,void *ptr)
230: {
231: DM da;
233: PetscInt i,Mx,xs,xm;
234: PetscReal hx,sx;
235: PetscScalar *x,*f,c,r,l;
236: Vec localX;
237: UserCtx *ctx = (UserCtx*)ptr;
238: PetscReal tol = ctx->tol, theta=ctx->theta,theta_c=ctx->theta_c,a,b; /* a and b are used in the cubic truncation of the log function */
241: TSGetDM(ts,&da);
242: DMGetLocalVector(da,&localX);
243: DMDAGetInfo(da,PETSC_IGNORE,&Mx,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
244: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
246: hx = 1.0/(PetscReal)Mx; sx = 1.0/(hx*hx);
248: /*
249: Scatter ghost points to local vector,using the 2-step process
250: DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
251: By placing code between these two statements, computations can be
252: done while messages are in transition.
253: */
254: DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
255: DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
257: /*
258: Get pointers to vector data
259: */
260: DMDAVecGetArray(da,localX,&x);
261: DMDAVecGetArray(da,F,&f);
263: /*
264: Get local grid boundaries
265: */
266: DMDAGetCorners(da,&xs,NULL,NULL,&xm,NULL,NULL);
268: /*
269: Compute function over the locally owned part of the grid
270: */
271: for (i=xs; i<xs+xm; i++) {
272: if (ctx->degenerate) {
273: c = (1. - x[i]*x[i])*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
274: r = (1. - x[i+1]*x[i+1])*(x[i] + x[i+2] - 2.0*x[i+1])*sx;
275: l = (1. - x[i-1]*x[i-1])*(x[i-2] + x[i] - 2.0*x[i-1])*sx;
276: } else {
277: c = (x[i-1] + x[i+1] - 2.0*x[i])*sx;
278: r = (x[i] + x[i+2] - 2.0*x[i+1])*sx;
279: l = (x[i-2] + x[i] - 2.0*x[i-1])*sx;
280: }
281: f[i] = -ctx->kappa*(l + r - 2.0*c)*sx;
282: if (ctx->cahnhillard) {
283: switch (ctx->energy) {
284: case 1: /* double well */
285: f[i] += 6.*.25*x[i]*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (3.*x[i]*x[i] - 1.)*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
286: break;
287: case 2: /* double obstacle */
288: f[i] += -(x[i-1] + x[i+1] - 2.0*x[i])*sx;
289: break;
290: case 3: /* logarithmic + double well */
291: f[i] += 6.*.25*x[i]*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (3.*x[i]*x[i] - 1.)*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
292: if (ctx->truncation==2) { /* log function with approximated with a quadratic polynomial outside -1.0+2*tol, 1.0-2*tol */
293: if (PetscRealPart(x[i]) < -1.0 + 2.0*tol) f[i] += (.25*theta/(tol-tol*tol))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
294: else if (PetscRealPart(x[i]) > 1.0 - 2.0*tol) f[i] += (.25*theta/(tol-tol*tol))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
295: else f[i] += 2.0*theta*x[i]/((1.0-x[i]*x[i])*(1.0-x[i]*x[i]))*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (theta/(1.0-x[i]*x[i]))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
296: } else { /* log function is approximated with a cubic polynomial outside -1.0+2*tol, 1.0-2*tol */
297: a = 2.0*theta*(1.0-2.0*tol)/(16.0*tol*tol*(1.0-tol)*(1.0-tol));
298: b = theta/(4.0*tol*(1.0-tol)) - a*(1.0-2.0*tol);
299: if (PetscRealPart(x[i]) < -1.0 + 2.0*tol) f[i] += -1.0*a*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (-1.0*a*x[i] + b)*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
300: else if (PetscRealPart(x[i]) > 1.0 - 2.0*tol) f[i] += 1.0*a*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + ( a*x[i] + b)*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
301: else f[i] += 2.0*theta*x[i]/((1.0-x[i]*x[i])*(1.0-x[i]*x[i]))*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (theta/(1.0-x[i]*x[i]))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
302: }
303: break;
304: case 4: /* logarithmic + double obstacle */
305: f[i] += -theta_c*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
306: if (ctx->truncation==2) { /* quadratic */
307: if (PetscRealPart(x[i]) < -1.0 + 2.0*tol) f[i] += (.25*theta/(tol-tol*tol))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
308: else if (PetscRealPart(x[i]) > 1.0 - 2.0*tol) f[i] += (.25*theta/(tol-tol*tol))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
309: else f[i] += 2.0*theta*x[i]/((1.0-x[i]*x[i])*(1.0-x[i]*x[i]))*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (theta/(1.0-x[i]*x[i]))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
310: } else { /* cubic */
311: a = 2.0*theta*(1.0-2.0*tol)/(16.0*tol*tol*(1.0-tol)*(1.0-tol));
312: b = theta/(4.0*tol*(1.0-tol)) - a*(1.0-2.0*tol);
313: if (PetscRealPart(x[i]) < -1.0 + 2.0*tol) f[i] += -1.0*a*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (-1.0*a*x[i] + b)*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
314: else if (PetscRealPart(x[i]) > 1.0 - 2.0*tol) f[i] += 1.0*a*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + ( a*x[i] + b)*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
315: else f[i] += 2.0*theta*x[i]/((1.0-x[i]*x[i])*(1.0-x[i]*x[i]))*.25*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (theta/(1.0-x[i]*x[i]))*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
316: }
317: break;
318: }
319: }
321: }
323: /*
324: Restore vectors
325: */
326: DMDAVecRestoreArray(da,localX,&x);
327: DMDAVecRestoreArray(da,F,&f);
328: DMRestoreLocalVector(da,&localX);
329: return(0);
330: }
332: /* ------------------------------------------------------------------- */
335: /*
336: FormJacobian - Evaluates nonlinear function's Jacobian
338: */
339: PetscErrorCode FormJacobian(TS ts,PetscReal ftime,Vec X,Mat *A,Mat *B,MatStructure *str,void *ptr)
340: {
341: DM da;
343: PetscInt i,Mx,xs,xm;
344: MatStencil row,cols[5];
345: PetscReal hx,sx;
346: PetscScalar *x,vals[5];
347: Vec localX;
348: UserCtx *ctx = (UserCtx*)ptr;
351: TSGetDM(ts,&da);
352: DMGetLocalVector(da,&localX);
353: DMDAGetInfo(da,PETSC_IGNORE,&Mx,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
354: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
356: hx = 1.0/(PetscReal)Mx; sx = 1.0/(hx*hx);
358: /*
359: Scatter ghost points to local vector,using the 2-step process
360: DMGlobalToLocalBegin(),DMGlobalToLocalEnd().
361: By placing code between these two statements, computations can be
362: done while messages are in transition.
363: */
364: DMGlobalToLocalBegin(da,X,INSERT_VALUES,localX);
365: DMGlobalToLocalEnd(da,X,INSERT_VALUES,localX);
367: /*
368: Get pointers to vector data
369: */
370: DMDAVecGetArray(da,localX,&x);
372: /*
373: Get local grid boundaries
374: */
375: DMDAGetCorners(da,&xs,NULL,NULL,&xm,NULL,NULL);
377: /*
378: Compute function over the locally owned part of the grid
379: */
380: for (i=xs; i<xs+xm; i++) {
381: row.i = i;
382: if (ctx->degenerate) {
383: /*PetscScalar c,r,l;
384: c = (1. - x[i]*x[i])*(x[i-1] + x[i+1] - 2.0*x[i])*sx;
385: r = (1. - x[i+1]*x[i+1])*(x[i] + x[i+2] - 2.0*x[i+1])*sx;
386: l = (1. - x[i-1]*x[i-1])*(x[i-2] + x[i] - 2.0*x[i-1])*sx; */
387: } else {
388: cols[0].i = i - 2; vals[0] = -ctx->kappa*sx*sx;
389: cols[1].i = i - 1; vals[1] = 4.0*ctx->kappa*sx*sx;
390: cols[2].i = i ; vals[2] = -6.0*ctx->kappa*sx*sx;
391: cols[3].i = i + 1; vals[3] = 4.0*ctx->kappa*sx*sx;
392: cols[4].i = i + 2; vals[4] = -ctx->kappa*sx*sx;
393: }
394: MatSetValuesStencil(*B,1,&row,5,cols,vals,INSERT_VALUES);
396: if (ctx->cahnhillard) {
397: switch (ctx->energy) {
398: case 1: /* double well */
399: /* f[i] += 6.*.25*x[i]*(x[i+1] - x[i-1])*(x[i+1] - x[i-1])*sx + (3.*x[i]*x[i] - 1.)*(x[i-1] + x[i+1] - 2.0*x[i])*sx; */
400: break;
401: case 2: /* double obstacle */
402: /* f[i] += -(x[i-1] + x[i+1] - 2.0*x[i])*sx; */
403: break;
404: case 3: /* logarithmic + double well */
405: break;
406: case 4: /* logarithmic + double obstacle */
407: break;
408: }
409: }
411: }
413: /*
414: Restore vectors
415: */
416: DMDAVecRestoreArray(da,localX,&x);
417: DMRestoreLocalVector(da,&localX);
418: MatAssemblyBegin(*B,MAT_FINAL_ASSEMBLY);
419: MatAssemblyEnd(*B,MAT_FINAL_ASSEMBLY);
420: if (*A != *B) {
421: MatAssemblyBegin(*A,MAT_FINAL_ASSEMBLY);
422: MatAssemblyEnd(*A,MAT_FINAL_ASSEMBLY);
423: }
424: return(0);
425: }
426: /* ------------------------------------------------------------------- */
429: PetscErrorCode FormInitialSolution(DM da,Vec U)
430: {
431: PetscErrorCode ierr;
432: PetscInt i,xs,xm,Mx,N,scale;
433: PetscScalar *u;
434: PetscReal r,hx,x;
435: const PetscScalar *f;
436: Vec finesolution;
437: PetscViewer viewer;
440: DMDAGetInfo(da,PETSC_IGNORE,&Mx,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
441: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
443: hx = 1.0/(PetscReal)Mx;
445: /*
446: Get pointers to vector data
447: */
448: DMDAVecGetArray(da,U,&u);
450: /*
451: Get local grid boundaries
452: */
453: DMDAGetCorners(da,&xs,NULL,NULL,&xm,NULL,NULL);
455: /* InitialSolution.biharmonic is obtained by running
456: ./heat -square_initial -ts_monitor -snes_monitor -pc_type lu -snes_converged_reason -ts_type cn -da_refine 9 -ts_final_time 1.e-4 -ts_dt .125e-6 -snes_atol 1.e-25 -snes_rtol 1.e-25 -ts_max_steps 30
457: After changing the initial grid spacing to 10 and the stencil width to 2 in the DMDA create.
458: */
459: PetscViewerBinaryOpen(PETSC_COMM_WORLD,"InitialSolution.biharmonic",FILE_MODE_READ,&viewer);
460: VecCreate(PETSC_COMM_WORLD,&finesolution);
461: VecLoad(finesolution,viewer);
462: PetscViewerDestroy(&viewer);
463: VecGetSize(finesolution,&N);
464: scale = N/Mx;
465: VecGetArrayRead(finesolution,&f);
467: /*
468: Compute function over the locally owned part of the grid
469: */
470: for (i=xs; i<xs+xm; i++) {
471: x = i*hx;
472: r = PetscSqrtReal((x-.5)*(x-.5));
473: if (r < .125) u[i] = 1.0;
474: else u[i] = -.5;
476: /* With the initial condition above the method is first order in space */
477: /* this is a smooth initial condition so the method becomes second order in space */
478: /*u[i] = PetscSinScalar(2*PETSC_PI*x); */
479: u[i] = f[scale*i];
480: }
481: VecRestoreArrayRead(finesolution,&f);
482: VecDestroy(&finesolution);
484: /*
485: Restore vectors
486: */
487: DMDAVecRestoreArray(da,U,&u);
488: return(0);
489: }
493: /*
494: This routine is not parallel
495: */
496: PetscErrorCode MyMonitor(TS ts,PetscInt step,PetscReal time,Vec U,void *ptr)
497: {
498: UserCtx *ctx = (UserCtx*)ptr;
499: PetscDrawLG lg;
501: PetscScalar *u,l,r,c;
502: PetscInt Mx,i,xs,xm,cnt;
503: PetscReal x,y,hx,pause,sx,len,max,xx[4],yy[4],xx_netforce,yy_netforce,yup,ydown,y2,len2;
504: PetscDraw draw;
505: Vec localU;
506: DM da;
507: int colors[] = {PETSC_DRAW_YELLOW,PETSC_DRAW_RED,PETSC_DRAW_BLUE,PETSC_DRAW_PLUM,PETSC_DRAW_BLACK};
508: /*
509: const char *const legend[3][3] = {{"-kappa (\\grad u,\\grad u)","(1 - u^2)^2"},{"-kappa (\\grad u,\\grad u)","(1 - u^2)"},{"-kappa (\\grad u,\\grad u)","logarithmic"}};
510: */
511: PetscDrawAxis axis;
512: PetscDrawViewPorts *ports;
513: PetscReal tol = ctx->tol, theta=ctx->theta,theta_c=ctx->theta_c,a,b; /* a and b are used in the cubic truncation of the log function */
517: TSGetDM(ts,&da);
518: DMGetLocalVector(da,&localU);
519: DMDAGetInfo(da,PETSC_IGNORE,&Mx,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,
520: PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE,PETSC_IGNORE);
521: DMDAGetCorners(da,&xs,NULL,NULL,&xm,NULL,NULL);
522: hx = 1.0/(PetscReal)Mx; sx = 1.0/(hx*hx);
523: DMGlobalToLocalBegin(da,U,INSERT_VALUES,localU);
524: DMGlobalToLocalEnd(da,U,INSERT_VALUES,localU);
525: DMDAVecGetArray(da,localU,&u);
527: PetscViewerDrawGetDrawLG(PETSC_VIEWER_DRAW_(PETSC_COMM_WORLD),1,&lg);
528: PetscDrawLGGetDraw(lg,&draw);
529: PetscDrawCheckResizedWindow(draw);
530: if (!ctx->ports) {
531: PetscDrawViewPortsCreateRect(draw,1,3,&ctx->ports);
532: }
533: ports = ctx->ports;
534: PetscDrawLGGetAxis(lg,&axis);
535: PetscDrawLGReset(lg);
537: xx[0] = 0.0; xx[1] = 1.0; cnt = 2;
538: PetscOptionsGetRealArray(NULL,"-zoom",xx,&cnt,NULL);
539: xs = xx[0]/hx; xm = (xx[1] - xx[0])/hx;
541: /*
542: Plot the energies
543: */
544: PetscDrawLGSetDimension(lg,1 + (ctx->cahnhillard ? 1 : 0) + (ctx->energy == 3));
545: PetscDrawLGSetColors(lg,colors+1);
546: PetscDrawViewPortsSet(ports,2);
547: x = hx*xs;
548: for (i=xs; i<xs+xm; i++) {
549: xx[0] = xx[1] = xx[2] = x;
550: if (ctx->degenerate) yy[0] = PetscRealPart(.25*(1. - u[i]*u[i])*ctx->kappa*(u[i-1] - u[i+1])*(u[i-1] - u[i+1])*sx);
551: else yy[0] = PetscRealPart(.25*ctx->kappa*(u[i-1] - u[i+1])*(u[i-1] - u[i+1])*sx);
553: if (ctx->cahnhillard) {
554: switch (ctx->energy) {
555: case 1: /* double well */
556: yy[1] = .25*PetscRealPart((1. - u[i]*u[i])*(1. - u[i]*u[i]));
557: break;
558: case 2: /* double obstacle */
559: yy[1] = .5*PetscRealPart(1. - u[i]*u[i]);
560: break;
561: case 3: /* logarithm + double well */
562: yy[1] = .25*PetscRealPart((1. - u[i]*u[i])*(1. - u[i]*u[i]));
563: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) yy[2] = .5*theta*(2.0*tol*PetscLogReal(tol) + PetscRealPart(1.0-u[i])*PetscLogReal(PetscRealPart(1.-u[i])/2.0));
564: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) yy[2] = .5*theta*(PetscRealPart(1.0+u[i])*PetscLogReal(PetscRealPart(1.0+u[i])/2.0) + 2.0*tol*PetscLogReal(tol));
565: else yy[2] = .5*theta*(PetscRealPart(1.0+u[i])*PetscLogReal(PetscRealPart(1.0+u[i])/2.0) + PetscRealPart(1.0-u[i])*PetscLogReal(PetscRealPart(1.0-u[i])/2.0));
566: break;
567: case 4: /* logarithm + double obstacle */
568: yy[1] = .5*theta_c*PetscRealPart(1.0-u[i]*u[i]);
569: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) yy[2] = .5*theta*(2.0*tol*PetscLogReal(tol) + PetscRealPart(1.0-u[i])*PetscLogReal(PetscRealPart(1.-u[i])/2.0));
570: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) yy[2] = .5*theta*(PetscRealPart(1.0+u[i])*PetscLogReal(PetscRealPart(1.0+u[i])/2.0) + 2.0*tol*PetscLogReal(tol));
571: else yy[2] = .5*theta*(PetscRealPart(1.0+u[i])*PetscLogReal(PetscRealPart(1.0+u[i])/2.0) + PetscRealPart(1.0-u[i])*PetscLogReal(PetscRealPart(1.0-u[i])/2.0));
572: break;
573: }
574: }
575: PetscDrawLGAddPoint(lg,xx,yy);
576: x += hx;
577: }
578: PetscDrawGetPause(draw,&pause);
579: PetscDrawSetPause(draw,0.0);
580: PetscDrawAxisSetLabels(axis,"Energy","","");
581: /* PetscDrawLGSetLegend(lg,legend[ctx->energy-1]); */
582: PetscDrawLGDraw(lg);
584: /*
585: Plot the forces
586: */
587: PetscDrawLGSetDimension(lg,0 + (ctx->cahnhillard ? 2 : 0) + (ctx->energy == 3));
588: PetscDrawLGSetColors(lg,colors+1);
589: PetscDrawViewPortsSet(ports,1);
590: PetscDrawLGReset(lg);
591: x = xs*hx;
592: max = 0.;
593: for (i=xs; i<xs+xm; i++) {
594: xx[0] = xx[1] = xx[2] = xx[3] = x;
595: xx_netforce = x;
596: if (ctx->degenerate) {
597: c = (1. - u[i]*u[i])*(u[i-1] + u[i+1] - 2.0*u[i])*sx;
598: r = (1. - u[i+1]*u[i+1])*(u[i] + u[i+2] - 2.0*u[i+1])*sx;
599: l = (1. - u[i-1]*u[i-1])*(u[i-2] + u[i] - 2.0*u[i-1])*sx;
600: } else {
601: c = (u[i-1] + u[i+1] - 2.0*u[i])*sx;
602: r = (u[i] + u[i+2] - 2.0*u[i+1])*sx;
603: l = (u[i-2] + u[i] - 2.0*u[i-1])*sx;
604: }
605: yy[0] = PetscRealPart(-ctx->kappa*(l + r - 2.0*c)*sx);
606: yy_netforce = yy[0];
607: max = PetscMax(max,PetscAbs(yy[0]));
608: if (ctx->cahnhillard) {
609: switch (ctx->energy) {
610: case 1: /* double well */
611: yy[1] = PetscRealPart(6.*.25*u[i]*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (3.*u[i]*u[i] - 1.)*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
612: break;
613: case 2: /* double obstacle */
614: yy[1] = -PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx;
615: break;
616: case 3: /* logarithmic + double well */
617: yy[1] = PetscRealPart(6.*.25*u[i]*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (3.*u[i]*u[i] - 1.)*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
618: if (ctx->truncation==2) { /* quadratic */
619: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) yy[2] = (.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx;
620: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) yy[2] = (.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx;
621: else yy[2] = PetscRealPart(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
622: } else { /* cubic */
623: a = 2.0*theta*(1.0-2.0*tol)/(16.0*tol*tol*(1.0-tol)*(1.0-tol));
624: b = theta/(4.0*tol*(1.0-tol)) - a*(1.0-2.0*tol);
625: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) yy[2] = PetscRealPart(-1.0*a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (-1.0*a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
626: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) yy[2] = PetscRealPart(1.0*a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + ( a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
627: else yy[2] = PetscRealPart(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
628: }
629: break;
630: case 4: /* logarithmic + double obstacle */
631: yy[1] = theta_c*PetscRealPart(-(u[i-1] + u[i+1] - 2.0*u[i]))*sx;
632: if (ctx->truncation==2) {
633: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) yy[2] = (.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx;
634: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) yy[2] = (.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx;
635: else yy[2] = PetscRealPart(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
636: }
637: else {
638: a = 2.0*theta*(1.0-2.0*tol)/(16.0*tol*tol*(1.0-tol)*(1.0-tol));
639: b = theta/(4.0*tol*(1.0-tol)) - a*(1.0-2.0*tol);
640: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) yy[2] = PetscRealPart(-1.0*a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (-1.0*a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
641: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) yy[2] = PetscRealPart(1.0*a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + ( a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
642: else yy[2] = PetscRealPart(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx);
643: }
644: break;
645: }
646: if (ctx->energy < 3) {
647: max = PetscMax(max,PetscAbs(yy[1]));
648: yy[2] = yy[0]+yy[1];
649: yy_netforce = yy[2];
650: } else {
651: max = PetscMax(max,PetscAbs(yy[1]+yy[2]));
652: yy[3] = yy[0]+yy[1]+yy[2];
653: yy_netforce = yy[3];
654: }
655: }
656: if (ctx->netforce) {
657: PetscDrawLGAddPoint(lg,&xx_netforce,&yy_netforce);
658: } else {
659: PetscDrawLGAddPoint(lg,xx,yy);
660: }
661: x += hx;
662: /*if (max > 7200150000.0) */
663: /* printf("max very big when i = %d\n",i); */
664: }
665: PetscDrawAxisSetLabels(axis,"Right hand side","","");
666: PetscDrawLGSetLegend(lg,NULL);
667: PetscDrawLGDraw(lg);
669: /*
670: Plot the solution
671: */
672: PetscDrawLGSetDimension(lg,1);
673: PetscDrawViewPortsSet(ports,0);
674: PetscDrawLGReset(lg);
675: x = hx*xs;
676: PetscDrawLGSetLimits(lg,x,x+(xm-1)*hx,-1.1,1.1);
677: PetscDrawLGSetColors(lg,colors);
678: for (i=xs; i<xs+xm; i++) {
679: xx[0] = x;
680: yy[0] = PetscRealPart(u[i]);
681: PetscDrawLGAddPoint(lg,xx,yy);
682: x += hx;
683: }
684: PetscDrawAxisSetLabels(axis,"Solution","","");
685: PetscDrawLGDraw(lg);
687: /*
688: Print the forces as arrows on the solution
689: */
690: x = hx*xs;
691: cnt = xm/60;
692: cnt = (!cnt) ? 1 : cnt;
694: for (i=xs; i<xs+xm; i += cnt) {
695: y = yup = ydown = PetscRealPart(u[i]);
696: c = (u[i-1] + u[i+1] - 2.0*u[i])*sx;
697: r = (u[i] + u[i+2] - 2.0*u[i+1])*sx;
698: l = (u[i-2] + u[i] - 2.0*u[i-1])*sx;
699: len = -.5*PetscRealPart(ctx->kappa*(l + r - 2.0*c)*sx)/max;
700: PetscDrawArrow(draw,x,y,x,y+len,PETSC_DRAW_RED);
701: if (ctx->cahnhillard) {
702: if (len < 0.) ydown += len;
703: else yup += len;
705: switch (ctx->energy) {
706: case 1: /* double well */
707: len = .5*PetscRealPart(6.*.25*u[i]*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (3.*u[i]*u[i] - 1.)*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max;
708: break;
709: case 2: /* double obstacle */
710: len = -.5*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx/max;
711: break;
712: case 3: /* logarithmic + double well */
713: len = .5*PetscRealPart(6.*.25*u[i]*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (3.*u[i]*u[i] - 1.)*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max;
714: if (len < 0.) ydown += len;
715: else yup += len;
717: if (ctx->truncation==2) { /* quadratic */
718: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) len2 = .5*(.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx/max;
719: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) len2 = .5*(.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx/max;
720: else len2 = PetscRealPart(.5*(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max);
721: } else { /* cubic */
722: a = 2.0*theta*(1.0-2.0*tol)/(16.0*tol*tol*(1.0-tol)*(1.0-tol));
723: b = theta/(4.0*tol*(1.0-tol)) - a*(1.0-2.0*tol);
724: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) len2 = PetscRealPart(.5*(-1.0*a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (-1.0*a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max);
725: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) len2 = PetscRealPart(.5*(a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + ( a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max);
726: else len2 = PetscRealPart(.5*(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max);
727: }
728: y2 = len < 0 ? ydown : yup;
729: PetscDrawArrow(draw,x,y2,x,y2+len2,PETSC_DRAW_PLUM);
730: break;
731: case 4: /* logarithmic + double obstacle */
732: len = -.5*theta_c*PetscRealPart(-(u[i-1] + u[i+1] - 2.0*u[i])*sx/max);
733: if (len < 0.) ydown += len;
734: else yup += len;
736: if (ctx->truncation==2) { /* quadratic */
737: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) len2 = .5*(.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx/max;
738: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) len2 = .5*(.25*theta/(tol-tol*tol))*PetscRealPart(u[i-1] + u[i+1] - 2.0*u[i])*sx/max;
739: else len2 = PetscRealPart(.5*(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max);
740: } else { /* cubic */
741: a = 2.0*theta*(1.0-2.0*tol)/(16.0*tol*tol*(1.0-tol)*(1.0-tol));
742: b = theta/(4.0*tol*(1.0-tol)) - a*(1.0-2.0*tol);
743: if (PetscRealPart(u[i]) < -1.0 + 2.0*tol) len2 = .5*PetscRealPart(-1.0*a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (-1.0*a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max;
744: else if (PetscRealPart(u[i]) > 1.0 - 2.0*tol) len2 = .5*PetscRealPart(a*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + ( a*u[i] + b)*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max;
745: else len2 = .5*PetscRealPart(2.0*theta*u[i]/((1.0-u[i]*u[i])*(1.0-u[i]*u[i]))*.25*(u[i+1] - u[i-1])*(u[i+1] - u[i-1])*sx + (theta/(1.0-u[i]*u[i]))*(u[i-1] + u[i+1] - 2.0*u[i])*sx)/max;
746: }
747: y2 = len < 0 ? ydown : yup;
748: PetscDrawArrow(draw,x,y2,x,y2+len2,PETSC_DRAW_PLUM);
749: break;
750: }
751: PetscDrawArrow(draw,x,y,x,y+len,PETSC_DRAW_BLUE);
752: }
753: x += cnt*hx;
754: }
755: DMDAVecRestoreArray(da,localU,&x);
756: DMRestoreLocalVector(da,&localU);
757: PetscDrawStringSetSize(draw,.2,.2);
758: PetscDrawFlush(draw);
759: PetscDrawSetPause(draw,pause);
760: PetscDrawPause(draw);
761: return(0);
762: }
766: PetscErrorCode MyDestroy(void **ptr)
767: {
768: UserCtx *ctx = *(UserCtx**)ptr;
772: PetscDrawViewPortsDestroy(ctx->ports);
773: return(0);
774: }