1 /*
2  * ***************************************************************************
3  * MC = < Manifold Code >
4  * Copyright (C) 1994-- Michael Holst
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * rcsid="$Id: mypde0.c,v 1.15 2010/08/12 05:17:24 fetk Exp $"
21  * ***************************************************************************
22  */
23 
24 /*
25  * ***************************************************************************
26  * File:     mypde0.c
27  *
28  * Purpose:  Class PDE: methods.
29  *
30  *     Problem type:          scalar nonlinear test problem
31  *     Spatial dimension:     2 or 3
32  *     Product dimension:     1
33  *     Differential operator: Linear or nonlinear potential equation
34  *                            with symmetric or nonsymmetric linear part
35  *     Boundary conditions:   zero or nonzero Dirichlet conditions
36  *                            nonzero or zero Neumann/Robin conditions
37  *
38  * Author:   Michael Holst
39  * ***************************************************************************
40  */
41 
42 #include "mypde.h"
43 
44 #define VEMBED(rctag) VPRIVATE const char* rctag; \
45     static void* use_rcsid=(0 ? &use_rcsid : (void*)&rcsid);
46 VEMBED(rcsid="$Id: mypde0.c,v 1.15 2010/08/12 05:17:24 fetk Exp $")
47 
48 const int P_DIR=0; /* 0=zero Dirichlet, 1=nonzero Dirichlet     */
49 const int P_NEU=0; /* 0=standard Neumann, 1=Robin term also     */
50 const int P_NSY=0; /* 0=symmetric, 1=non-symmetric              */
51 const int P_NON=0; /* 0=linear, 1=linear Helmholtz, 2=nonlinear */
52 const int P_NUN=1; /* 0=smooth solution, 1=highly non-uniform   */
53 
54 /*
55  * ***************************************************************************
56  * Problem-specific
57  * ***************************************************************************
58  */
59 
60 const double PP = -21.;
61 const double QQ = 0.05;
62 const double RR = 0.9;
63 const double C0 = 0.25;
64 const double C1 = 0.25;
65 const double C2 = 0.25;
66 
67 VPRIVATE double FAC(int d, int m, double *x);
68 VPRIVATE double FACx0(int d, int m, double *x);
69 VPRIVATE double FACx1(int d, int m, double *x);
70 VPRIVATE double FACx2(int d, int m, double *x);
71 VPRIVATE double FACxx0(int d, int m, double *x);
72 VPRIVATE double FACxx1(int d, int m, double *x);
73 VPRIVATE double FACxx2(int d, int m, double *x);
74 VPRIVATE double UU(int d, int m, double *x);
75 VPRIVATE double UUx0(int d, int m, double *x);
76 VPRIVATE double UUx1(int d, int m, double *x);
77 VPRIVATE double UUx2(int d, int m, double *x);
78 VPRIVATE double UUxx0(int d, int m, double *x);
79 VPRIVATE double UUxx1(int d, int m, double *x);
80 VPRIVATE double UUxx2(int d, int m, double *x);
81 VPRIVATE double Ux0(int d, int m, double *x);
82 VPRIVATE double Ux1(int d, int m, double *x);
83 VPRIVATE double Ux2(int d, int m, double *x);
84 VPRIVATE double Uxx0(int d, int m, double *x);
85 VPRIVATE double Uxx1(int d, int m, double *x);
86 VPRIVATE double Uxx2(int d, int m, double *x);
87 VPRIVATE void my_D(int d, int m, double *x, double *f);
88 VPRIVATE double my_A(int d, int m, double *x);
89 VPRIVATE double my_B(int d, int m, double A, double *DN, double *x,
90     double *u);
91 VPRIVATE double my_DB(int d, int m, double *x, double *u);
92 VPRIVATE double my_C(int d, int m, double *x);
93 VPRIVATE double my_GN(int d, int m, double A, double C, double *x);
94 VPRIVATE double my_US(int d, int m, double *x);
95 
FAC(int d,int m,double * x)96 VPRIVATE double FAC(int d, int m, double *x) {
97     double value;
98     if (!P_NUN) value = 1.;
99     else {
100         double dist = 0.;
101         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
102         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
103         value = QQ*VPOW(RR+dist,PP);
104     }
105     return value;
106 }
FACx0(int d,int m,double * x)107 VPRIVATE double FACx0(int d, int m, double *x) {
108     double value;
109     if (!P_NUN) value = 0.;
110     else {
111         double dist = 0.;
112         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
113         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
114         value = -2.*(C0-x[0])*PP*QQ*VPOW(RR+dist,PP-1);
115     }
116     return value;
117 }
FACx1(int d,int m,double * x)118 VPRIVATE double FACx1(int d, int m, double *x) {
119     double value;
120     if (!P_NUN) value = 0.;
121     else {
122         double dist = 0.;
123         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
124         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
125         value = -2.*(C1-x[1])*PP*QQ*VPOW(RR+dist,PP-1);
126     }
127     return value;
128 }
FACx2(int d,int m,double * x)129 VPRIVATE double FACx2(int d, int m, double *x) {
130     double value, dist = 0.;
131     if (!P_NUN) value = 0.;
132     else {
133         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
134         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
135         value = -2.*(C2-x[2])*PP*QQ*VPOW(RR+dist,PP-1);
136     }
137     return value;
138 }
FACxx0(int d,int m,double * x)139 VPRIVATE double FACxx0(int d, int m, double *x) {
140     double value, dist = 0.;
141     if (!P_NUN) value = 0.;
142     else {
143         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
144         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
145         value = 2.*PP*QQ*VPOW(RR+dist,PP-1)
146               + 4.*PP*QQ*(PP-1)*VSQR(C0-x[0])*VPOW(RR+dist,PP-2);
147     }
148     return value;
149 }
FACxx1(int d,int m,double * x)150 VPRIVATE double FACxx1(int d, int m, double *x) {
151     double value;
152     if (!P_NUN) value = 0.;
153     else {
154         double dist = 0.;
155         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
156         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
157         value = 2.*PP*QQ*VPOW(RR+dist,PP-1)
158               + 4.*PP*QQ*(PP-1)*VSQR(C1-x[1])*VPOW(RR+dist,PP-2);
159     }
160     return value;
161 }
FACxx2(int d,int m,double * x)162 VPRIVATE double FACxx2(int d, int m, double *x) {
163     double value, dist = 0.;
164     if (!P_NUN) value = 0.;
165     else {
166         if (d==2) dist = VSQR(C0-x[0])+VSQR(C1-x[1]);
167         else      dist = VSQR(C0-x[0])+VSQR(C1-x[1])+VSQR(C2-x[2]);
168         value = 2.*PP*QQ*VPOW(RR+dist,PP-1)
169               + 4.*PP*QQ*(PP-1)*VSQR(C2-x[2])*VPOW(RR+dist,PP-2);
170     }
171     return value;
172 }
UU(int d,int m,double * x)173 VPRIVATE double UU(int d, int m, double *x) {
174     double value;
175     if (d==2) {
176         if (P_DIR==0) value = VSIN(VPI*x[0])*VSIN(VPI*x[1]);
177         else          value = VCOS(VPI*x[0])*VCOS(VPI*x[1]);
178     } else { /* (d==3) */
179         if (P_DIR==0) value = VSIN(VPI*x[0])*VSIN(VPI*x[1])*VSIN(VPI*x[2]);
180         else          value = VCOS(VPI*x[0])*VCOS(VPI*x[1])*VCOS(VPI*x[2]);
181     }
182     return value;
183 }
UUx0(int d,int m,double * x)184 VPRIVATE double UUx0(int d, int m, double *x) {
185     double value;
186     if (d==2) {
187         if (P_DIR==0) value =  VPI*VCOS(VPI*x[0])*VSIN(VPI*x[1]);
188         else          value = -VPI*VSIN(VPI*x[0])*VCOS(VPI*x[1]);
189     } else { /* (d==3) */
190         if (P_DIR==0) value =  VPI*VCOS(VPI*x[0])*VSIN(VPI*x[1])*VSIN(VPI*x[2]);
191         else          value = -VPI*VSIN(VPI*x[0])*VCOS(VPI*x[1])*VCOS(VPI*x[2]);
192     }
193     return value;
194 }
UUx1(int d,int m,double * x)195 VPRIVATE double UUx1(int d, int m, double *x) {
196     double value;
197     if (d==2) {
198         if (P_DIR==0) value =  VPI*VSIN(VPI*x[0])*VCOS(VPI*x[1]);
199         else          value = -VPI*VCOS(VPI*x[0])*VSIN(VPI*x[1]);
200     } else { /* (d==3) */
201         if (P_DIR==0) value =  VPI*VSIN(VPI*x[0])*VCOS(VPI*x[1])*VSIN(VPI*x[2]);
202         else          value = -VPI*VCOS(VPI*x[0])*VSIN(VPI*x[1])*VCOS(VPI*x[2]);
203     }
204     return value;
205 }
UUx2(int d,int m,double * x)206 VPRIVATE double UUx2(int d, int m, double *x) {
207     double value;
208     if (d==2) {
209         value = 0.0;
210     } else { /* (d==3) */
211         if (P_DIR==0) value =  VPI*VSIN(VPI*x[0])*VSIN(VPI*x[1])*VCOS(VPI*x[2]);
212         else          value = -VPI*VCOS(VPI*x[0])*VCOS(VPI*x[1])*VSIN(VPI*x[2]);
213     }
214     return value;
215 }
UUxx0(int d,int m,double * x)216 VPRIVATE double UUxx0(int d, int m, double *x) {return -VPI*VPI*UU(d,m,x);}
UUxx1(int d,int m,double * x)217 VPRIVATE double UUxx1(int d, int m, double *x) {return -VPI*VPI*UU(d,m,x);}
UUxx2(int d,int m,double * x)218 VPRIVATE double UUxx2(int d, int m, double *x) {return -VPI*VPI*UU(d,m,x);}
Ux0(int d,int m,double * x)219 VPRIVATE double Ux0(int d, int m, double *x) {
220     return FACx0(d,m,x) * UU(d,m,x) + FAC(d,m,x) * UUx0(d,m,x);
221 }
Ux1(int d,int m,double * x)222 VPRIVATE double Ux1(int d, int m, double *x) {
223     return FACx1(d,m,x) * UU(d,m,x) + FAC(d,m,x) * UUx1(d,m,x);
224 }
Ux2(int d,int m,double * x)225 VPRIVATE double Ux2(int d, int m, double *x) {
226     if (d==2) return 0.;
227     else return FACx2(d,m,x) * UU(d,m,x) + FAC(d,m,x) * UUx2(d,m,x);
228 }
Uxx0(int d,int m,double * x)229 VPRIVATE double Uxx0(int d, int m, double *x) {
230     return FACxx0(d,m,x) * UU(d,m,x)
231          + 2. * FACx0(d,m,x) * UUx0(d,m,x)
232          + FAC(d,m,x) * UUxx0(d,m,x);
233 }
Uxx1(int d,int m,double * x)234 VPRIVATE double Uxx1(int d, int m, double *x) {
235     return FACxx1(d,m,x) * UU(d,m,x)
236          + 2. * FACx1(d,m,x) * UUx1(d,m,x)
237          + FAC(d,m,x) * UUxx1(d,m,x);
238 }
Uxx2(int d,int m,double * x)239 VPRIVATE double Uxx2(int d, int m, double *x) {
240     if (d==2) return 0.;
241     else return FACxx2(d,m,x) * UU(d,m,x)
242               + 2. * FACx2(d,m,x) * UUx2(d,m,x)
243               + FAC(d,m,x) * UUxx2(d,m,x);
244 }
245 
my_D(int d,int m,double * x,double * f)246 VPRIVATE void my_D(int d, int m, double *x, double *f) {
247     double magNsym = 1.0 * P_NSY;
248     f[0] = magNsym;
249     f[1] = magNsym;
250     if (d==3) f[2] = magNsym;
251     else      f[2] = 0.0;
252 }
my_A(int d,int m,double * x)253 VPRIVATE double my_A(int d, int m, double *x) { return 1.0; }
my_B(int d,int m,double A,double * DN,double * x,double * u)254 VPRIVATE double my_B(int d, int m, double A, double *DN, double *x,
255     double *u) {
256     double SRC;
257     double helm  = 0.0;
258     double helmt = 0.0;
259     if (P_NON==0) { /* linear potential operator */
260         helm  = 0.0;
261         helmt = 0.0;
262     } else if (P_NON==1) { /* linear helmholtz operator */
263         helm  = 100.0 * u[0];
264         helmt = 100.0 * my_US(d,m,x);
265     } else if (P_NON==2) { /* nonlinear operator */
266         helm  = VPOW(u[0],3.0);
267         helmt = VPOW(my_US(d,m,x),3.0);
268     }
269     SRC = - A * ( Uxx0(d,m,x) + Uxx1(d,m,x) + Uxx2(d,m,x) )
270           + DN[0]*Ux0(d,m,x) + DN[1]*Ux1(d,m,x) + DN[2]*Ux2(d,m,x)
271           + helmt;
272     return helm - SRC;
273 }
my_DB(int d,int m,double * x,double * u)274 VPRIVATE double my_DB(int d, int m, double *x, double *u) {
275     if (P_NON==0) /* linear potential operator */
276         return 0.0;
277     else if (P_NON==1) /* linear helmholtz operator */
278         return 100.0;
279     else if (P_NON==2) /* nonlinear operator */
280         return 3.0*VPOW(u[0],2.0);
281     return 0.0;
282 }
my_C(int d,int m,double * x)283 VPRIVATE double my_C(int d, int m, double *x) {
284     if (P_NEU==0) return 0.0; /* neumann condition */
285     else          return 1.0; /* robin condition */
286 }
my_GN(int d,int m,double A,double C,double * x)287 VPRIVATE double my_GN(int d, int m, double A, double C, double *x) {
288     double value, u, u_x0, u_x1, u_x2;
289     value = 0.0;
290     u = my_US(d, m, x);
291     u_x0 = Ux0(d, m, x);
292     u_x1 = Ux1(d, m, x);
293     u_x2 = Ux2(d, m, x);
294     if (d==2) {
295         if      (VABS( x[0] - 0.0 ) < VSMALL) value = - A*u_x0+C*u;
296         else if (VABS( x[0] - 1.0 ) < VSMALL) value =   A*u_x0+C*u;
297         else if (VABS( x[1] - 0.0 ) < VSMALL) value = - A*u_x1+C*u;
298         else if (VABS( x[1] - 1.0 ) < VSMALL) value =   A*u_x1+C*u;
299         else {
300             /* Vnm_print(2, "my_GN: there is some problem...\n"); */
301             /* Vnm_print(2, "my_GN: x=(%e,%e,%e)\n", x[0], x[1], x[2]); */
302             value = 0.;
303         }
304     } else { /* (d==3) */
305         if      (VABS( x[0] - 0.0 ) < VSMALL) value = - A*u_x0+C*u;
306         else if (VABS( x[0] - 1.0 ) < VSMALL) value =   A*u_x0+C*u;
307         else if (VABS( x[1] - 0.0 ) < VSMALL) value = - A*u_x1+C*u;
308         else if (VABS( x[1] - 1.0 ) < VSMALL) value =   A*u_x1+C*u;
309         else if (VABS( x[2] - 0.0 ) < VSMALL) value = - A*u_x2+C*u;
310         else if (VABS( x[2] - 1.0 ) < VSMALL) value =   A*u_x2+C*u;
311         else {
312             /* Vnm_print(2, "my_GN: there is some problem...\n"); */
313             /* Vnm_print(2, "my_GN: x=(%e,%e,%e)\n", x[0], x[1], x[2]); */
314             value = 0.;
315         }
316     }
317     return value;
318 }
my_US(int d,int m,double * x)319 VPRIVATE double my_US(int d, int m, double *x) {
320     return FAC(d,m,x) * UU(d,m,x);
321 }
322 
323 /*
324  * ***************************************************************************
325  * Local data
326  * ***************************************************************************
327  */
328 
329 VPRIVATE double xq[3], nvec[3], vx[4][3], U[MAXV], dU[MAXV][3];
330 
331 VPRIVATE double A, B, DB, C, GN, DN[3];
332 VPRIVATE int sType, fType;
333 
334 /*
335  * ***************************************************************************
336  * Routine:  initAssemble
337  *
338  * Purpose:  Do once-per-assembly initialization.
339  *
340  * Input:    PDE = pointer to the PDE object
341  *           ip  = integer parameters for the assembly
342  *           rp  = real parameters for the assembly
343  *
344  * Output:   None
345  *
346  * Speed:    This function is called by MC just before a full assembly,
347  *           and does not have to be particularly fast.
348  *
349  * Author:   Michael Holst
350  * ***************************************************************************
351  */
initAssemble(PDE * thee,int ip[],double rp[])352 VPUBLIC void initAssemble(PDE *thee, int ip[], double rp[])
353 {
354 }
355 
356 /*
357  * ***************************************************************************
358  * Routine:  initElement
359  *
360  * Purpose:  Do once-per-element initialization.
361  *
362  * Input:    PDE         = pointer to the PDE object
363  *           elementType = type of this element (various material types)
364  *           chart       = chart in which vertex coordinates are provided
365  *           tvx[][3]    = coordinates of all the vertices of the element
366  *
367  * Output:   None
368  *
369  * Speed:    This function is called by MC just before assembling a single
370  *           element, and needs to be as fast as possible.
371  *
372  * Author:   Michael Holst
373  * ***************************************************************************
374  */
initElement(PDE * thee,int elementType,int chart,double tvx[][3],void * data)375 VPUBLIC void initElement(PDE *thee, int elementType,
376     int chart, double tvx[][3], void *data)
377 {
378     int i, j;
379 
380     /* vertex locations of this simplex */
381     for (i=0; i<thee->dim+1; i++)
382         for (j=0; j<thee->dim; j++)
383             vx[i][j] = tvx[i][j];
384 
385     /* save the element type */
386     sType = elementType;
387 }
388 
389 /*
390  * ***************************************************************************
391  * Routine:  initFace
392  *
393  * Purpose:  Do once-per-face initialization.
394  *
395  * Input:    PDE      = pointer to the PDE object
396  *           faceType = type of this face (interior or various boundary types)
397  *           chart    = chart in which normal vector coordinates are provided
398  *           tnvec[]  = coordinates of the outward normal vector to this face
399  *
400  * Output:   None
401  *
402  * Speed:    This function is called by MC just before assembling a single
403  *           element face, and needs to be as fast as possible.
404  *
405  * Author:   Michael Holst
406  * ***************************************************************************
407  */
initFace(PDE * thee,int faceType,int chart,double tnvec[])408 VPUBLIC void initFace(PDE *thee, int faceType,
409     int chart, double tnvec[])
410 {
411     int i;
412 
413     /* unit normal vector of this face */
414     for (i=0; i<thee->dim; i++)
415         nvec[i] = tnvec[i];
416 
417     /* save the face type */
418     fType = faceType;
419 }
420 
421 /*
422  * ***************************************************************************
423  * Routine:  initPoint
424  *
425  * Purpose:  Do once-per-point initialization.
426  *
427  * Input:    PDE       = pointer to the PDE object
428  *           pointType = type of this point (interior or boundary)
429  *           chart     = chart in which the point coordinates are provided
430  *           txq[]     = coordinates of the point
431  *           tU[]      = current solution at the point
432  *           tdU[]     = current solution gradient at the point
433  *
434  * Output:   None
435  *
436  * Speed:    This function is called by MC for every quadrature point
437  *           during an assmebly, and needs to be as fast as possible.
438  *
439  * Author:   Michael Holst
440  * ***************************************************************************
441  */
initPoint(PDE * thee,int pointType,int chart,double txq[],double tU[],double tdU[][3])442 VPUBLIC void initPoint(PDE *thee, int pointType,
443     int chart, double txq[],
444     double tU[], double tdU[][3])
445 {
446     int i, j;
447 
448     /* the point, and the solution value and gradient at the point */
449     for (i=0; i<thee->vec; i++) {
450         U[i] = tU[i];
451         for (j=0; j<thee->dim; j++) {
452             if (i==0) xq[j] = txq[j];
453             dU[i][j] = tdU[i][j];
454         }
455     }
456 
457     /* interior form case */
458     if (pointType == 0) {
459 
460         A = my_A(thee->dim, thee->vec, xq);
461         my_D(thee->dim, thee->vec, xq, DN);
462         B = my_B(thee->dim, thee->vec, A, DN, xq, U);
463         DB = my_DB(thee->dim, thee->vec, xq, U);
464 
465     /* boundary form case */
466     } else { /* (pointType == 1) */
467 
468         A = my_A(thee->dim, thee->vec, xq);
469         C = my_C(thee->dim, thee->vec, xq);
470         GN = my_GN(thee->dim, thee->vec, A, C, xq);
471 
472     }
473 }
474 
475 /*
476  * ***************************************************************************
477  * Routine:  Fu
478  *
479  * Purpose:  Evaluate the strong form of the differential operator F(u)
480  *           at the single point x.  This is your nonlinear strong form:
481  *
482  *            [   b(u)^i - a(u)^{iq}_{~;q},   if t = 0
483  *     F(u) = [   c(u)^i + a(u)^{iq} n_q,     if t = 1
484  *            [   0      + a(u)^{iq} n_q,     if t = 2
485  *
486  * Input:    PDE   = pointer to the PDE object
487  *           key   = piece to evaluate (0=interior, 1=boundary, 2=int-bndry)
488  *
489  * Output:   F[]   = operator piece evaluated at the point given to initPoint
490  *
491  * Speed:    This function is called by MC only when using error
492  *           estimation based on strong residuals.  The speed of this
493  *           function will impact the speed of the error estimator.
494  *
495  * Author:   Michael Holst
496  * ***************************************************************************
497  */
Fu(PDE * thee,int key,double F[])498 VPUBLIC void Fu(PDE *thee, int key, double F[])
499 {
500     int j;
501 
502     /* element residual case */
503     if (key == 0) {
504         F[0] = 0.;
505 
506     /* neumann face residual case */
507     } else if (key == 1) {
508         F[0] = C * U[0] - GN;
509         for (j=0; j<thee->dim; j++)
510             F[0] += dU[0][j] * nvec[j];
511 
512     /* interior face residual case */
513     } else { /* (key == 2) */
514         F[0] = 0.;
515         for (j=0; j<thee->dim; j++)
516             F[0] += dU[0][j] * nvec[j];
517     }
518 }
519 
520 /*
521  * ***************************************************************************
522  * Routine:  Ju
523  *
524  * Purpose:  Evaluate the integrand J_k(u) of the energy functional J(u)
525  *           at the single point x.  This is your nonlinear energy
526  *           functional for which your weak form PDE below in Fu_v() is the
527  *           Euler condition.  (There may not be such a J(u) in all cases.)
528  *
529  *            /\              /\
530  *     J(u) = \  J_0(u) dx +  \  J_1(u) ds
531  *           \/m             \/dm
532  *
533  * Input:    PDE   = pointer to the PDE object
534  *           key   = integrand to evaluate (0=J_0, 1=J_1)
535  *
536  * Output:   Value of the integrand is returned
537  *
538  * Speed:    This function is called by MC once times for a single
539  *           quadrature point during assembly, and needs to be fast.
540  *
541  * Author:   Michael Holst
542  * ***************************************************************************
543  */
Ju(PDE * thee,int key)544 VPUBLIC double Ju(PDE *thee, int key)
545 {
546     double value = 0.;
547 
548     /* interior form case */
549     if (key == 0) {
550         value = 0.;
551 
552     /* boundary form case */
553     } else if (key == 1) {
554         value = 0.;
555 
556     } else { VASSERT(0); }
557 
558     return value;
559 }
560 
561 /*
562  * ***************************************************************************
563  * Routine:  Fu_v
564  *
565  * Purpose:  Evaluate the integrand F(u)(v) of the functional <F(u),v>
566  *           at the single point x.  This is your nonlinear weak form:
567  *
568  *                /\                 /\
569  *     <F(u),v> = \  F_0(u)(v) dx +  \  F_1(u)(v) ds
570  *               \/m                \/dm
571  *
572  *                /\
573  *              = \  g_{ij} ( a(u)^{iq} v^j_{~;q} + b(u)^i v^j ) dx
574  *               \/m
575  *
576  *                /\
577  *              + \  g_{ij} c(u)^i v^j ds
578  *               \/dm
579  *
580  *           or the data for the linearized adjoint (dual) problem:
581  *
582  *                          /\              /\
583  *     <F(u),v> = <psi,v> = \  F_2(v) dx +  \  F_3(v) ds
584  *                         \/m             \/dm
585  *
586  * Input:    PDE   = pointer to the PDE object
587  *           key   = integrand to evaluate (0=F_0, 1=F_1, 2=F_2, 3=F_3)
588  *           tV[]  = test function at the current point
589  *           tdV[] = test function gradient at the current point
590  *
591  * Output:   Value of the integrand is returned
592  *
593  * Speed:    This function is called by MC multiple times for a single
594  *           quadrature point during assembly, and needs to be as fast
595  *           as possible.
596  *
597  * Author:   Michael Holst
598  * ***************************************************************************
599  */
Fu_v(PDE * thee,int key,double V[],double dV[][3])600 VPUBLIC double Fu_v(PDE *thee, int key,
601     double V[], double dV[][3])
602 {
603     int i;
604     double value = 0.;
605 
606     /* interior form case */
607     if (key == 0) {
608         value = B * V[0];
609         for (i=0; i<thee->dim; i++)
610             value += ( A * dU[0][i] * dV[0][i] + DN[i] * dU[0][i] * V[0] );
611 
612     /* boundary form case */
613     } else if (key == 1) {
614         value = (C * U[0] - GN) * V[0];
615 
616     /* DUAL: interior form case */
617     } else if (key == 2) {
618         value = 0.0;
619 
620     /* DUAL: boundary form case */
621     } else if (key == 3) {
622         value = 0.0;
623 
624     } else { VASSERT(0); }
625 
626     return value;
627 }
628 
629 /*
630  * ***************************************************************************
631  * Routine:  DFu_wv
632  *
633  * Purpose:  Evaluate the integrand DF(u)(w,v) of the functional <DF(u)w,v>
634  *           at the single point x.  This is your bilinear weak form:
635  *
636  *                  /\                    /\
637  *     <DF(u)w,v> = \  DF_0(u)(w,v) dx +  \  DF_1(u)(w,v) ds
638  *                 \/m                   \/dm
639  *
640  *                  /\
641  *                = \  d/dt F_0(u+tw)(v)|_{t=0} dx
642  *                 \/m
643  *
644  *                  /\
645  *                + \  d/dt F_1(u+tw)(v)|_{t=0} ds
646  *                 \/dm
647  *
648  *           or the bilinear weak form for the linearized dual problem:
649  *
650  *                                /\                    /\
651  *     <DF(u)w,v> = <A(u)^Tw,v> = \  DF_2(u)(w,v) dx +  \  DF_3(u)(w,v) ds
652  *                               \/m                   \/dm
653  *
654  * Input:    PDE   = pointer to the PDE object
655  *           key   = integrand to evaluate (0=DF_0, 1=DF_1, 2=DF_2, 3=DF_3)
656  *           tW[]  = trial function at the current point
657  *           tdW[] = trial function gradient at the current point
658  *           tV[]  = test function at the current point
659  *           tdV[] = test function gradient at the current point
660  *
661  * Output:   Value of the integrand is returned
662  *
663  * Speed:    This function is called by MC multiple times for a single
664  *           quadrature point during assembly, and needs to be as fast
665  *           as possible.
666  *
667  * Author:   Michael Holst
668  * ***************************************************************************
669  */
DFu_wv(PDE * thee,int key,double W[],double dW[][3],double V[],double dV[][3])670 VPUBLIC double DFu_wv(PDE *thee, int key,
671     double W[], double dW[][3],
672     double V[], double dV[][3])
673 {
674     int i;
675     double value = 0.;
676 
677     /* interior form case */
678     if (key == 0) {
679         value = DB * W[0] * V[0];
680         for (i=0; i<thee->dim; i++)
681             value += ( A * dW[0][i] * dV[0][i] + DN[i] * dW[0][i] * V[0] );
682 
683     /* boundary form case */
684     } else if (key == 1) {
685         value = C * W[0] * V[0];
686 
687     /* DUAL: interior form case */
688     } else if (key == 2) {
689         value = 0.0;
690 
691     /* DUAL: boundary form case */
692     } else if (key == 3) {
693         value = 0.0;
694 
695     } else { VASSERT(0); }
696 
697     return value;
698 }
699 
700 /*
701  * ***************************************************************************
702  * Routine:  p_wv
703  *
704  * Purpose:  Evaluate the integrand p(w,v) of the functional <p w,v> at
705  *           the single point x.  This is your bilinear mass form:
706  *
707  *               /\             /\
708  *     <p w,v> = \  p(w,v) dx = \  p(x) w(x) v(x) dx
709  *              \/m            \/m
710  *
711  * Input:    PDE   = pointer to the PDE object
712  *           key   = integrand to evaluate (0=p(w,v), 1=0)
713  *           tW[]  = trial function at the current point
714  *           tV[]  = test function at the current point
715  *
716  * Output:   Value of the integrand is returned
717  *
718  * Speed:    This function is called by MC one time for a single
719  *           quadrature point during assembly, and needs to be as fast
720  *           as possible.
721  *
722  * Author:   Michael Holst
723  * ***************************************************************************
724  */
p_wv(PDE * thee,int key,double W[],double V[])725 VPUBLIC double p_wv(PDE *thee, int key, double W[], double V[])
726 {
727     double value = 0.;
728 
729     /* interior form case */
730     if (key == 0) {
731         value = W[0] * V[0];
732 
733     /* boundary form case */
734     } else if (key == 1) {
735         value = 0.0;
736 
737     /* DUAL: interior form case */
738     } else if (key == 2) {
739         value = 0.0;
740 
741     /* DUAL: boundary form case */
742     } else if (key == 3) {
743         value = 0.0;
744 
745     } else { VASSERT(0); }
746 
747     return value;
748 }
749 
750 /*
751  * ***************************************************************************
752  * Routine:  delta
753  *
754  * Purpose:  At the single given point x, evaluate a delta function
755  *           source term (if one is present): delta = g(x).
756  *
757  * Input:    PDE   = pointer to the PDE object
758  *           chart = chart in which the point coordinates are provided
759  *           txq[] = coordinates of the point
760  *
761  * Output:   F[]   = resulting function values
762  *
763  * Speed:    This function is called by MC once for each node in the mesh,
764  *           just after a full element-wise assembly, so it should be fast.
765  *
766  * Author:   Michael Holst
767  * ***************************************************************************
768  */
delta(PDE * thee,int type,int chart,double txq[],void * data,double F[])769 VPUBLIC void delta(PDE *thee, int type,
770     int chart, double txq[], void *data,
771     double F[])
772 {
773     F[0] = 0;
774 }
775 
776 /*
777  * ***************************************************************************
778  * Routine:  u_D
779  *
780  * Purpose:  At the single given point x, evaluate the dirichlet boundary
781  *           function: u_D = g(x).
782  *
783  * Input:    PDE   = pointer to the PDE object
784  *           chart = chart in which the point coordinates are provided
785  *           txq[] = coordinates of the point
786  *
787  * Output:   F[]   = resulting function values
788  *
789  * Speed:    This function is called by MC just before a full assembly,
790  *           and does not have to be particularly fast.
791  *
792  * Author:   Michael Holst
793  * ***************************************************************************
794  */
u_D(PDE * thee,int type,int chart,double txq[],double F[])795 VPUBLIC void u_D(PDE *thee, int type,
796     int chart, double txq[], double F[])
797 {
798     F[0] = my_US(thee->dim, thee->vec, txq);
799 }
800 
801 /*
802  * ***************************************************************************
803  * Routine:  u_T
804  *
805  * Purpose:  At the single given point x, evaluate the true solution
806  *           function (if you have one for your problem): u_T = u(x).
807  *
808  * Input:    PDE   = pointer to the PDE object
809  *           chart = chart in which the point coordinates are provided
810  *           txq[] = coordinates of the point
811  *
812  * Output:   F[]   = resulting function values
813  *
814  * Speed:    This function is called by MC just before a full assembly,
815  *           and does not have to be particularly fast.
816  *
817  * Author:   Michael Holst
818  * ***************************************************************************
819  */
u_T(PDE * thee,int type,int chart,double txq[],double F[])820 VPUBLIC void u_T(PDE *thee, int type,
821     int chart, double txq[], double F[])
822 {
823     F[0] = my_US(thee->dim, thee->vec, txq);
824 }
825 
826 /*
827  * ***************************************************************************
828  * Routine:  bisectEdge
829  *
830  * Purpose:  Define the way manifold edges are bisected.
831  *
832  * Input:    The input parameters have the following interpretations:
833  *
834  *               dim                  = intrinsic dimension of the manifold
835  *               dimII                = imbedding dimension of the manifold
836  *               edgeType             = edge type being refined
837  *               chart[0]             = manifold chart for 0th vertex of edge
838  *               chart[1]             = manifold chart for 1st vertex of edge
839  *               vx[0][0,...,dimII-1] = 1st vertex coordinates w.r.t. chart[0]
840  *               vx[1][0,...,dimII-1] = 2nd vertex coordinates w.r.t. chart[1]
841  *
842  * Output:   The output parameters have the following interpretations:
843  *
844  *               chart[2]             = manifold chart for NEW 3rd vertex
845  *               vx[2][0,...,dimII-1] = 3rd vertex coordinates w.r.t. chart[2]
846  *
847  * Speed:    This function is called by MC every time an edge must be
848  *           bisected for simplex refinement, and needs to be as fast as
849  *           possible.
850  *
851  * Notes:    The chart numbers are the charts you associated with your
852  *           vertices in your input manifold.  If the coordinates of the two
853  *           vertices of the edge had been given w.r.t. different charts, then
854  *           you need to call your "oneChart" routine to produce a single
855  *           chart to represent both vertices for the bisection, and then pass
856  *           back both the coordinates of the new bisection point and the
857  *           associated chart number.  The idea is that other than your
858  *           initial chart assignment you made in your input manifold
859  *           specification, the oneChart function that you provide along with
860  *           this bisectEdge function is the only place where coordinate
861  *           transformations occur, and they are entirely under your control.
862  *
863  *           For a non-flat Riemannian 2- or 3-manifold, you should be
864  *           producing something as close as possible to the bisection of a
865  *           geodesic on your manifold, with the distance to the midpoint
866  *           measured using the Riemannian metric for your manifold.  If you
867  *           can do this, then the bisection of every generalized simplex on
868  *           the manifold by longest "geodesic" is guaranteed to remain
869  *           non-degenerate (in the metric measure).  This is a simple
870  *           extension of the planar result to a Riemannian manifold.  In the
871  *           case of a 3-manifold, it is open whether this produces
872  *           non-degenerate generalized simplices (because it is still an open
873  *           question for bisection of tetrahedra in R^3 by their longest
874  *           edge), but emperically it appears to be non-degenerate.
875  *
876  * Example:  In the simple case of a single orthogonal cartesian coordinate
877  *           system in R^2 or R^3, the bisection should be simply the midpoint
878  *           of the edge as follows:
879  *
880  *               Input parameters (set by MC before the call):
881  *
882  *                   dim                  = d;
883  *                   dimII                = d;
884  *                   edgeType             = (determined from face types you
885  *                                           specified in input manfold)
886  *                   chart                = 0;
887  *                   vx[0][0,...,dimII-1] = (coordinates of vx[0] w.r.t. chart)
888  *                   vx[1][0,...,dimII-1] = (coordinates of vx[1] w.r.t. chart)
889  *
890  *               bisectEdge rule:
891  *
892  *                   for (i=0; i<dimII; i++)
893  *                       vx[2][i] = .5 * ( vx[0][i] + vx[1][i] );
894  *
895  *               Output parameters:
896  *
897  *                   vx[2][0,...,dimII] = (coordinates of vx[2] w.r.t. chart)
898  *
899  * Author:   Michael Holst
900  * ***************************************************************************
901  */
bisectEdge(int dim,int dimII,int edgeType,int chart[],double vx[][3])902 VPUBLIC void bisectEdge(int dim, int dimII,
903     int edgeType, int chart[], double vx[][3])
904 {
905     int i;
906     for (i=0; i<dimII; i++)
907         vx[2][i] = .5 * (vx[0][i] + vx[1][i]);
908 }
mapBoundary(int dim,int dimII,int vertexType,int chart,double vx[3])909 VPUBLIC void mapBoundary(int dim, int dimII,
910     int vertexType, int chart, double vx[3])
911 {
912 }
913 
914 /*
915  * ***************************************************************************
916  * Routine:  markSimplex
917  *
918  * Purpose:  User-provided error estimator which allows the user to define
919  *           his own refinement strategies rather than using the builtin
920  *           a posteriori error estimators.
921  *
922  * Input:    The input parameters have the following interpretations:
923  *
924  *               dim                  = intrinsic dimension of the manifold
925  *               dimII                = imbedding dimension of the manifold
926  *               simplexType          = simplex type being refined
927  *               chart[0,...,d+1]     = manifold charts for all d+1 vertices
928  *               vx[0][0,...,dimII-1] = vx[0] coordinates w.r.t. chart
929  *                       ...
930  *               vx[d][0,...,dimII-1] = vx[d] coordinates w.r.t. chart
931  *
932  * Output:   The output parameters have the following interpretations:
933  *
934  *               return value         = 0 if the simplex is not to be refined
935  *                                    = 1 if the simplex is to be refined
936  *
937  * Speed:    This function is called by MC for every element during error
938  *           estimation, if the user-provided error estimator is requested.
939  *           Therefore, it should be pretty fast or the error estimation
940  *           phase will be slow.
941  *
942  * Notes:    The user can use this routine to define, for example,
943  *           a completely geometry-based error estimator.
944  *
945  * Example:  In the case that the user-provided error estimator is never
946  *           requested, then this routine can simply return any value.
947  *
948  * Author:   Michael Holst
949  * ***************************************************************************
950  */
markSimplex(int dim,int dimII,int simplexType,int faceType[4],int vertexType[4],int chart[],double vx[][3],void * data)951 VPUBLIC int markSimplex(int dim, int dimII,
952     int simplexType, int faceType[4], int vertexType[4],
953     int chart[], double vx[][3], void *data)
954 {
955     int j, k, less, more;
956     double radius, d[4];
957 
958     /* radius = radius of a refinement sphere for testing */
959     radius = 0.1; /* must be > 0 */
960     less = 0;
961     more = 0;
962     for (j=0; j<dim+1; j++) {
963         d[j] = 0.0;
964         for (k=0; k<3; k++)
965             d[j] += VSQR( vx[j][k] );
966         d[j] = VSQRT( d[j] );
967         if (d[j] <= radius+VSMALL) less = 1;
968         else more = 1;
969     }
970 
971     /* return true if simplex touches or stradles surface of sphere */
972     return ( less && more );
973 }
974 
975 /*
976  * ***************************************************************************
977  * Routine:  oneChart
978  *
979  * Purpose:  Select a single unified chart for a set of two or more vertices
980  *           whose coordinates may be given with respect to different charts.
981  *           Then transform all of the coordinates of the vertex set to be
982  *           with respect to the single selected "unified" chart.
983  *
984  * Input:    The input parameters have the following interpretations:
985  *
986  *               dim     = intrinsic dimension of the manifold
987  *               dimII   = imbedding dimension of the manifold
988  *               dimV    = number of vertices in the simplex
989  *                           dimV=1 ==> v is a single vertex
990  *                           dimV=2 ==> v is part of an edge
991  *                           dimV=3 ==> v is part of a triangle (or face)
992  *                           dimV=4 ==> v is part of a tetrahedron
993  *               objType = object type of the simplex
994  *                   dimV=1:
995  *                       type=?        ==> any interpretation
996  *                   dimV=2:
997  *                       type=0        ==> object is an interior edge
998  *                       type>0 & ODD  ==> object is a dirichlet edge
999  *                       type>0 & EVEN ==> object is a neumann edge
1000  *                   dimV=3:
1001  *                       dim=2
1002  *                           type=anything ==> material type of triangle
1003  *                       dim=3
1004  *                           type=0        ==> object is an interior face
1005  *                           type>0 & ODD  ==> object is a dirichlet face
1006  *                           type>0 & EVEN ==> object is a neumann face
1007  *                   dimV=4:
1008  *                       type=anything ==> material type of tetrahedron
1009  *               chart[] = current manifold chart numbers for input vertices
1010  *               vx[][3] = original vertex coordinates w.r.t. charts in chart[]
1011  *
1012  * Output:   The output parameters have the following interpretations:
1013  *
1014  *               chart[] = selected (unified) output charts (all the same)
1015  *               vx[][3] = new vertex coordinates w.r.t. new unified char
1016  *
1017  * Speed:    This function is called by MC whenever it encounters an element
1018  *           with vertices having coordinates in different charts.  If you
1019  *           many charts and thus many elements which stradle chart
1020  *           boundaries, then you will want to make this as fast as possible.
1021  *
1022  * Notes:    In this routine, you define how to move from one chart to
1023  *           another, which means you implicitly define how to change
1024  *           coordinate systems.  The code allows the user to select the
1025  *           appropriate chart for the vertices of an object in order to give
1026  *           the user maximal control over how he uses coordinate systems to
1027  *           represent a manifold.
1028  *
1029  *           The caller gives the user a set of vertex coordinates for two or
1030  *           more vertices that are a part of some simplex (edge, triangle,
1031  *           or tet).  The vertex coordinates may be given with respect to
1032  *           different charts if the simplex happens to stradle two charts.
1033  *
1034  *           Our job is to select the most appropriate chart based on the
1035  *           simplex class (edge, tri, or tet) and the simplex type (interior
1036  *           or boundary for edges, or triangular boundary faces, or some
1037  *           material type for triangles or tets), and then transform all
1038  *           of the vertex coordinates to the single coordinate system
1039  *           you have selected.  You then pass back the chart number that you
1040  *           have selected, along with the new coordinates.
1041  *
1042  * Example:  In the simple case of a single chart covering the manifold,
1043  *           this routine can simply return without modifying either
1044  *           chart or vx.
1045  *
1046  * Author:   Michael Holst
1047  * ***************************************************************************
1048  */
oneChart(int dim,int dimII,int objType,int chart[],double vx[][3],int dimV)1049 VPUBLIC void oneChart(int dim, int dimII, int objType,
1050     int chart[], double vx[][3], int dimV)
1051 {
1052     VASSERT( (2 <= dim)   && (dim   <= 3) );
1053     VASSERT( (2 <= dimII) && (dimII <= 3) );
1054     VASSERT( (1 <= dimV)  && (dimV  <= 4) );
1055     VASSERT( (0 <= objType) );
1056 }
1057 
1058 /*
1059  * ***************************************************************************
1060  * Class PDE: Inlineable methods
1061  * ***************************************************************************
1062  */
1063 #if !defined(VINLINE_PDE)
1064 #endif /* if !defined(VINLINE_PDE) */
1065 
1066 /*
1067  * ***************************************************************************
1068  * Class PDE: Non-inlineable methods
1069  * ***************************************************************************
1070  */
1071 
1072 /*
1073  * ***************************************************************************
1074  * Routine:  PDE_ctor
1075  *
1076  * Purpose:  Construct the differential equation object.
1077  *
1078  * Speed:    This function is called by MC one time during setup,
1079  *           and does not have to be particularly fast.
1080  *
1081  * Author:   Michael Holst
1082  * ***************************************************************************
1083  */
myPDE_ctor(void)1084 VPUBLIC PDE* myPDE_ctor(void)
1085 {
1086     int i;
1087     PDE *thee = VNULL;
1088 
1089     /* create some space for the pde object */
1090     thee = Vmem_malloc( VNULL, 1, sizeof(PDE) );
1091 
1092     /* PDE-specific parameters and function pointers */
1093     thee->initAssemble = initAssemble;  /* once-per-assembly initialization */
1094     thee->initElement  = initElement;   /* once-per-element initialization  */
1095     thee->initFace     = initFace;      /* once-per-face initialization     */
1096     thee->initPoint    = initPoint;     /* once-per-point initialization    */
1097     thee->Fu           = Fu;            /* nonlinear strong form            */
1098     thee->Ju           = Ju;            /* nonlinear energy functional      */
1099     thee->Fu_v         = Fu_v;          /* nonlinear weak form              */
1100     thee->DFu_wv       = DFu_wv;        /* bilinear linearization weak form */
1101     thee->p_wv         = p_wv;          /* bilinear mass density form       */
1102     thee->delta        = delta;         /* delta function source term       */
1103     thee->u_D          = u_D;           /* dirichlet func and initial guess */
1104     thee->u_T          = u_T;           /* analytical soln for testing      */
1105     thee->vec          = 1;             /* unknowns per spatial point;      */
1106     thee->sym[0][0]    = 0;             /* symmetries of bilinear form      */
1107     thee->est[0]       = 1.0;           /* error estimator weights          */
1108     for (i=0; i<VMAX_BDTYPE; i++)       /* boundary type remappings         */
1109         thee->bmap[0][i] = i;
1110 
1111     /* Manifold-specific function pointers */
1112     thee->bisectEdge  = bisectEdge;  /* edge bisection rule                 */
1113     thee->mapBoundary = mapBoundary; /* boundary recovery rule              */
1114     thee->markSimplex = markSimplex; /* simplex marking rule                */
1115     thee->oneChart    = oneChart;    /* coordinate transformations          */
1116 
1117     /* Element-specific function pointers */
1118     thee->simplexBasisInit = simplexBasisInit; /* initialization of bases   */
1119     thee->simplexBasisForm = simplexBasisForm; /* form trial & test bases   */
1120 
1121     /* return the new pde object */
1122     return thee;
1123 }
1124 
1125 /*
1126  * ***************************************************************************
1127  * Routine:  PDE_dtor
1128  *
1129  * Purpose:  Destroy the differential equation object.
1130  *
1131  * Speed:    This function is called by MC one time during shutdown,
1132  *           and does not have to be particularly fast.
1133  *
1134  * Author:   Michael Holst
1135  * ***************************************************************************
1136  */
myPDE_dtor(PDE ** thee)1137 VPUBLIC void myPDE_dtor(PDE **thee)
1138 {
1139     VASSERT( (*thee) != VNULL );
1140     if ((*thee) != VNULL) {
1141 
1142         Vmem_free( VNULL, 1, sizeof(PDE), (void**)thee );
1143 
1144         (*thee) = VNULL;
1145     }
1146 }
1147 
1148