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