root/trunk/EBaporate/ebsurf.c

Revision 449, 32.3 kB (checked in by hazelsct, 6 years ago)

Separated shorts.c into new file, documented it. Other doc changes.

  • Property svn:keywords set to Author Date Id Revision
Line 
1/***************************************
2  $Header$
3
4  This is the brains of the dynamic evaporation estimator.  It is linked into
5  the ebsurfweb and ebaporate binaries.
6
7  It does a multi-cycle 1-D transient simulation of heat conduction down into
8  the melt as the beam scans over a spot on the surface, as described in:
9
10  A. Powell, J. Van Den Avyle, B. Damkroger, J. Szekely and U. Pal
11  ``Analysis of Multicomponent Evaporation in Electron Beam Melting and
12  Refining of Titanium Alloys,''
13  +latex+{\em Metall. Trans.} {\bf 38B}, 1227-1239 (1997).
14  -latex-Metall. Trans. 38B, 1227-1239 (1997).
15
16  Features include:
17  +latex+\begin{itemize} \item
18  +html+ <ul><li>
19  Multiple postscript graph outputs.
20  +latex+\item
21  +html+ </li><li>
22  Beam can hit once per cycle (default) or follow programmable patterns.
23  +latex+\item
24  +html+ </li><li>
25  Variable timestep size to accurately capture beam strike and efficiently
26  calculate relaxation.
27  +latex+\item
28  +html+ </li><li>
29  Calculation of beam penetration depth from voltage, using function given
30  by Schiller (see shorts.c for details).
31  +latex+\item
32  +html+ </li><li>
33  Volumetric heat generation due to beam through the penetration depth,
34  using functionality given by Schiller.
35  +latex+\item
36  +html+ </li><li>
37  Crank-Nicholson timestepping with Newton iteration for nonlinear boundary
38  conditions.
39  +latex+\end{itemize}
40  +html+ </ul>
41
42  This program uses cgs units and Kelvin.
43***************************************/
44
45
46#include "ebsurf.h"
47
48
49/*++++++++++++++++++++++++++++++++++++++
50  This function runs through multiple time-periodic cycles of timesteps until
51  steady-state is reached for the base temperature or surface temperature T0
52  (depending on whether the TSUR flag is set in m).  ``Steady-state'' is
53  determined by the difference between surface temperature at the start and end
54  of the cycle.
55
56  int cycles It returns zero or an error code.
57
58  double *X Pointer to storage for positions (depth below surface).
59
60  double *t Pointer to storage for time step times.
61
62  double *T Pointer to storage for temperature array.
63
64  double T0 Base, or average surface, temperature.
65
66  double *big Pointer to storage for tridiagonal matrix coefficients.
67
68  struct param *p EBsurf Parameter structure.
69
70  int m Flags: TSUR if T0 is to be the surface temperature, OUTPUT to print
71  graphs, GREET to print other information such as timestep completion.
72
73  double *ou Little array of output values.
74
75  FILE *fule File name for HTML template.
76
77  int pt Increment to use in ou array.
78  ++++++++++++++++++++++++++++++++++++++*/
79
80int cycles (double *X, double *t, double *T, double T0, double *big,
81            struct param *p, int m, double *ou, FILE *fule, int pt)
82{
83  double Gto, tmp;
84  double timestep(), radloss(), loss(), vloss(), vloss2(), gtnorm(), gtdisk();
85  int i=0,j=0,s;
86  char *a,inp[80];
87  void intcond(), output(), printto(), skipto();
88
89  /*+ First we call initcond() to fill in the depth (X), time (t), and initial
90    temperature (T) arrays, along with the generation (G) and tridiagonal
91    matrix arrays (A,B,C) stored in ``big'', etc. +*/
92  initcond (X, t, T, T0, (m&TSUR)?0.:p->Tsurf, big, p);
93  if (m & TSUR)
94    T0=T[0];
95
96  /*+ The big loop then runs the timesteps.  This can happen in one of two
97    ways: +*/
98  Gto = p->gtime (0., p);
99  if (p->cyc)
100    {
101      /*+ If the "cyc" member of the param structure p is nonzero, then it will
102        run through cyc beam scan cycles, storing all of those cycles for
103        post-processing. +*/
104      for (j=0;j<p->cyc;j++)
105        {
106          printf ("Cycle %d",j+1);
107          if (m)
108            printf (", timesteps:");
109          for (i=0;i<p->ts;i++)
110            Gto=timestep(*p,X,t,T+(p->zs+1)*i,big,Gto,i+1,m);
111          printf (" dT=%.3lf, Tnew=%.3lf\n", T[(p->zs+1)*p->ts] - T[0],
112                  T[(p->zs+1)*p->ts]);
113          T += p->ts*(p->zs+1);
114        }
115      T -= p->cyc*p->ts*(p->zs+1);
116    }
117  else
118    {
119      /*+ Otherwise, it will run through as many beam scan cycles as it takes
120        to converge the surface temperature to steady-state, storing only the
121        last cycle for post-processing. +*/
122      j=0;
123      do
124        {
125          if(j)
126            {
127              /* Calculate the "surface average temperature". */
128              tmp = (T[p->zs-1] * t[1] +
129                     T[(p->zs+1)*(p->ts+1)-2] * (p->tfin - t[p->ts-1])) * 0.5;
130              for (i=2; i<=p->ts; i++)
131                tmp += T[(p->zs+1)*i-2] * (t[i] - t[i-2])*0.5;
132              tmp = (tmp/p->tfin-T[p->zs])*p->w/(p->w-X[p->zs-1]) + T[p->zs];
133              if (m&TSUR)
134                {     /* Move 1st-step Ts so it's right */
135                  for (i=0; i<=p->zs; i++)
136                    T[i] = T[p->ts*(p->zs+1)+i] + T0-tmp;
137                }
138              else
139                {
140                  for (i=0; i<=p->zs; i++)
141                    T[i] = T[p->ts*(p->zs+1)+i];
142                }
143              printf ("<br>After cycle %d, surface average temperature=%g<sup>o</sup>C\n",
144                     j, tmp-273.15);
145            }
146          for (i=0; i<p->ts; i++)
147            {
148              Gto = timestep (*p, X, t, T+(p->zs+1)*i, big, Gto, i+1, m);
149            }
150        }
151      while(++j<MAXCYC && _ABS(T[0] - T[(p->zs+1)*p->ts]) > p->ct);
152    }
153
154  if (!p->cyc && _ABS(T[0] - T[(p->zs+1)*p->ts]) <= p->ct)
155    printf("<p><b>Converged</b> after ");
156  else
157    printf("Completed ");
158  printf("%d cycles.<p>",j);
159
160  s=(p->cyc?p->cyc-1:0)*p->ts;
161  if(m&TSUR)
162    T0= *(T+p->zs);
163  if(p->cyc)
164    T-=j*p->ts*(p->zs+1);
165  i=p->cyc;
166  if(p->cyc==0)
167    p->cyc=1;
168
169#define DELTAT ((j?*(t+j+1)-*(t+j-1):*(t+1)+p->tfin-*(t+p->ts-1))*0.5)
170  /* Calculate average temperature, evap (avg temp), evaporation */
171  ou[0] = ou[2*pt] = 0.;
172  for(j=0;j<p->ts;j++)
173    {
174      ou[0] += T[(s+j)*(p->zs+1)+p->zs-1] * DELTAT;
175      /* Replace vloss here with vloss2 for aluminum k'' calculation */
176      ou[2*pt] += vloss (T[(s+j)*(p->zs+1)], p->E, p->A, p->C, p->D) * DELTAT;
177    }
178  ou[0] = (ou[0]/p->tfin - T0) *p->w/(p->w-X[p->zs-1])+T0; /* p->Ma*10.* */
179  ou[2*pt] /= p->tfin;
180  ou[pt] = vloss(ou[0], p->E, p->A, p->C, p->D); /* here too! */
181
182  /* Report: results table: Tsurf, const T ev rate, real ev rate... */
183  printto (fule,"<TD>1</TD>");
184  printf ("<TD><A HREF=\"http://lyre.mit.edu/~powell/Software/outputs.html#tsurf\"><I>T</I><SUB>surf</SUB></A>=%g<SUP>o</SUP>C\n",ou[0] - 273.15);
185  printto (fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n", ou[pt] * p->Ma*1e4);
186  printto (fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n", ou[2*pt] * p->Ma*1e4);
187  printto (fule,"<TD>1</TD>");
188  printf ("<TD>%g%%</TD>\n", (ou[2*pt] - ou[pt])*100. / ou[pt]);
189
190  /* ..CT heat loss, real heat loss, CT & real rad loss, CT & real ev loss.. */
191  printto (fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",loss(*ou,p)/1.e7);
192  tmp=0.;
193  for (j=0; j<p->ts; j++)
194    tmp += loss (T[(s+j)*(p->zs+1)], p) * DELTAT;
195  printto (fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",tmp/p->tfin/1.e7);
196  printto (fule,"<TD>1</TD>");
197  printf ("<TD>%g%%</TD>\n",(tmp/p->tfin-loss(*ou,p))*100./loss(*ou,p));
198
199  printto (fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",radloss(*ou,p)/1.e7);
200  tmp=0.; for(j=0;j<p->ts;j++) tmp+=radloss(*(T+(s+j)*(p->zs+1)),p)*DELTAT;
201  printto(fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",tmp/p->tfin/1.e7);
202  printto(fule,"<TD>1</TD>");
203  printf("<TD>%g%%</TD>\n",(tmp/p->tfin-radloss(*ou,p))*100./radloss(*ou,p));
204
205  printto(fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",p->Q0*exp(p->S* *ou)*
206                                     vloss(*ou,p->E,p->A,p->C,p->D)/1.e7);
207  tmp=0.; for(j=0;j<p->ts;j++)
208    tmp+=p->Q0*exp(p->S* *(T+(s+j)*(p->zs+1)))*
209      vloss(*(T+(s+j)*(p->zs+1)),p->E,p->A,p->C,p->D)*DELTAT;
210  printto(fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",tmp/p->tfin/1.e7);
211  printto(fule,"<TD>1</TD>");
212  printf("<TD>%g%%</TD>\n",(tmp/p->tfin-
213                          p->Q0*exp(p->S* *ou)*vloss(*ou,p->E,p->A,p->C,p->D))
214         *100./(p->Q0*exp(p->S* *ou)*vloss(*ou,p->E,p->A,p->C,p->D)));
215
216  /* ...beam heat input, layer bottom temperature, conduction at bot, surf. */
217  printto(fule,"<TD>1</TD>"); /* Theoretical centerline heat flux */
218  printf("<TD>%g</TD>\n",p->pow*4./M_PI/p->spx/p->spy/1.e10);
219  printto(fule,"<TD>1</TD>"); /* Actual centerline heat flux = integral of */
220  tmp=*big; for(j=1;j<p->zs;j++) tmp+= *(big+j); /* q(z) dz */
221  printf("<TD>%g</TD>\n",tmp*p->k/p->al/1.e10);
222  printto(fule,"<TD>1</TD>"); if(p->gtime==gtnorm)
223    printf("<TD>%g</TD>\n", /* Theoretical beam heat input (sqrt(PI)/2) */
224           p->pow*2./sqrt(M_PI)/p->spx/p->spy*p->tres/p->tfin/1e7);
225  else printf("<TD>%g</TD>\n", /* Even distribution for liss & disk */
226              p->pow/p->plen/p->pwid/1e7*((p->gtime==gtdisk)?4./M_PI:1.));
227  tmp=0.; for(j=0;j<p->ts;j++) tmp+=(p->gtime)(*(t+j),p)*DELTAT;
228  printto(fule,"<TD>1</TD>"); /* Actual beam heat input */
229  printf("<TD>%g</TD>\n",p->pow*4/M_PI/p->spx/p->spy*tmp/p->tfin/1.e7);
230  printto(fule,"<TD>1</TD>"); printf("<TD>%g</TD>\n",T0-273.15); /* Layer bot temp */
231  tmp=0.; for(j=0;j<p->ts;j++) tmp+= *(T+(s+j+1)*(p->zs+1)-2)*DELTAT;
232  printto(fule,"<TD>1</TD>"); /* Conduction through bottom */
233  printf("<TD>%g</TD>\n",(tmp/p->tfin-T0)*p->k/(p->w-*(X+p->zs-1))/1.e7);
234  tmp=0.; for(j=0;j<p->ts;j++)
235    tmp+= (*(T+(s+j)*(p->zs+1))-*(T+(s+j)*(p->zs+1)+1))*DELTAT;
236  printto(fule,"<TD>1</TD>"); /* Approx. conduction through surface */
237  printf("<TD>%g</TD>\n",p->k*tmp/ *(X+1)/p->tfin/1.e7);
238#undef DELTAT
239
240  /* Output graphs (assumes last cycle is always output) */
241  if((m&OUTPUT)!=0) {
242    p->Tin=T0; printto(fule,"<TD>1</TD>");
243    if(!p->pout) skipto(fule,"<TD>1</TD>"); else {
244      output(p,X,t,big,PATT); printto(fule,"<TD>1</TD>"); } /* Pattern */
245    if(!p->gout) skipto(fule,"<TD>1</TD>"); else {
246      output(p,X,t,big,GENZ); printto(fule,"<TD>1</TD>"); } /* Heat gen func */
247    if(!p->gtou) skipto(fule,"<TD>1</TD>"); else {
248      output(p,X,t,big,GENT); printto(fule,"<TD>1</TD>"); } /* Heat gen hist */
249    if(!p->fout) skipto(fule,"<TD>1</TD>"); else {
250      output(p,X,t,T,FLUX); printto(fule,"<TD>1</TD>"); }   /* Bottom fluxes */
251    if(!p->sout) skipto(fule,"<TD>1</TD>"); else {
252      output(p,X,t,T,SURF); printto(fule,"<TD>1</TD>"); }  /* All surf temps */
253    if(!p->tout) skipto(fule,"<TD>1</TD>"); else {
254      output(p,X,t,T,TMPS); printto(fule,"<TD>1</TD>"); } /* All temps (3-D) */
255    output(p,X,t,T+(i?(i-1)*(p->zs+1)*(p->ts):0),ONEC);    /* Last cyc temps */
256  }
257  p->cyc=i;
258
259  return 0;
260}
261
262
263/*++++++++++++++++++++++++++++++++++++++
264  Perform one timestep in a cycle.
265
266  double timestep It returns a double equal to the heat generation per unit
267  area at the next timestep.
268
269  struct param p EBsurf parameter structure.
270
271  double *X The array of z-values.
272
273  double *t The array of time step times.
274
275  double *T The big array of temperatures, but pointing to this timestep.
276
277  double *big Array storing lots of different z-dependent parameters.
278
279  double Gto Heat generation rate per unit area at this timestep.
280
281  int s Timestep number for indexing into t array.
282
283  int m Flags, in particular GREET is important here.
284  ++++++++++++++++++++++++++++++++++++++*/
285
286double timestep(struct param p, double *X, double *t, double *T, double *big,
287                double Gto, int s, int m)
288{
289  int i;
290  void tridag();
291  double Gt,dt,Tb,*Gx,*A,*Ag,*At,*B,*Bg,*Bt,*C,*Cg,*Ct,*O,*R,loss(),lossder();
292
293  Gx = big;
294  A  = big + p.zs;
295  Ag = big + p.zs*2;
296  At = big + p.zs*3;
297  B  = big + p.zs*4;
298  Bg = big + p.zs*5;
299  Bt = big + p.zs*6;
300  C  = big + p.zs*7;
301  Cg = big + p.zs*8;
302  Ct = big + p.zs*9;
303  O  = big + p.zs*10;
304  R  = big + p.zs*11;
305
306  /*+ First it initializes the parameters, particularly the tridiagonal matrix,
307    according to the size of this timestep. +*/
308  dt = 1./(t[s]-t[s-1]);
309  Tb = (1.-p.th)/p.th;
310  Gt = (p.gtime)(t[s], &p);
311  for(i=0;i<p.zs;i++)
312    {
313      A[i] = Ag[i] + At[i]*dt;
314      B[i] = Bg[i] + Bt[i]*dt;
315      C[i] = Cg[i] + Ct[i]*dt;
316      O[i] = (Ag[i]*Tb - At[i]*dt) * T[i-1] + (Bg[i]*Tb - Bt[i]*dt)* T[i]+
317        (Cg[i]*Tb - Ct[i]*dt) * T[i+1] - ((1-p.th)*Gto+p.th*Gt) * Gx[i];
318      T[p.zs+1+i] = T[i];
319    }
320
321  if(m&GREET)
322    printf(" %d",s);
323
324  O[0] += p.al * (1-p.th) * loss (T[0], &p)/p.k;
325  T[2*p.zs+1] = T[p.zs];
326  T+=p.zs+1;
327  s=0;
328
329  /*+ Then it uses Newton-Raphson iteration to perform one semi-implicit
330    timestep, finishing when the difference between first and last temperature
331    is less than the "it" member of the param structure p. +*/
332  do
333    {
334      Tb = T[0];
335      B[0] = Bg[0] + Bt[0]*dt;
336      for(i=0;i<p.zs;i++)
337        R[i] = O[i] + (i ? (A[i]*T[i-1]) : 0) + B[i]*T[i] + C[i]*T[i+1];
338      R[0] += p.al * p.th * loss (T[0], &p)/p.k;
339      B[0] += p.al * p.th * lossder(T[0], &p)/p.k;
340      tridag (A, B, C, R, T, p.zs);
341    }
342  while(_ABS(*T-Tb)>p.it);
343
344  return Gt;
345}
346
347
348/*++++++++++++++++++++++++++++++++++++++
349  Tridiagonal matrix solver, solves (a,b,c)arr=r, and u+=arr.
350
351  double *a Subdiagonal in tridiagonal matrix.
352
353  double *b Diagonal in tridiagonal matrix.
354
355  double *c Superdiagonal in tridiagonal matrix.
356
357  double *r Right-hand side.
358
359  double *u Array to add the solution to.
360
361  int n Number of members in the array.
362  ++++++++++++++++++++++++++++++++++++++*/
363
364void tridag(double *a, double *b, double *c, double *r, double *u, int n)
365{
366  int j;
367  double bet,*gam,*arr;
368
369  if (!(gam=MALLOC(double,2*n)))
370    exit (printf ("No memory for gamma vector.\n"));
371  arr=gam+n;
372
373  if (b[0] == 0.0)
374    exit (printf ("Matrix has a zero corner.\n"));
375
376  arr[0] = r[0]/(bet=b[0]);
377  for (j=1; j<n; j++)
378    {
379      gam[j] = c[j-1]/bet;
380      bet = b[j] - a[j]*gam[j];
381      if (bet == 0.0)
382        exit( printf("Matrix has a zero pivot.\n"));
383      arr[j] = (r[j] - a[j]*arr[j-1]) / bet;
384    }
385
386  u[n-1] -= arr[n-1];
387  for (j=(n-2); j>=0; j--)
388    u[j] -= arr[j] -= gam[j+1]*arr[j+1];
389
390  free(gam);
391}
392
393
394/*++++++++++++++++++++++++++++++++++++++
395  Calculate the initial conditions, generation function, tridiagonal matrix
396  coefficients, etc.
397
398  double *X The array of z-values.
399
400  double *t The array of time step times.
401
402  double *T The big array of temperatures.
403
404  double T0 Initial base temperature.
405
406  double TS Initial surface temperature.
407
408  double *big Array storing lots of different z-dependent parameters.
409
410  struct param *p EBsurf parameter structure.
411  ++++++++++++++++++++++++++++++++++++++*/
412
413void initcond (double *X, double *t, double *T, double T0, double TS,
414               double *big, struct param *p)
415{
416  int i=0;
417  double tmp,*Gx,*Ag,*At,*Bg,*Bt,*Cg,*Ct;
418
419  /* Position of vectors within "big" */
420  Gx=big;
421  Ag=big+p->zs*2;
422  At=big+p->zs*3;
423  Bg=big+p->zs*5;
424  Bt=big+p->zs*6;
425  Cg=big+p->zs*8;
426  Ct=big+p->zs*9;
427
428  /* Make space and time grids */
429  X[0] = 0.;
430  X[p->zs] = p->w;
431  t[0] = 0.;
432  tmp = log(p->zr)/(p->zs-1);
433
434  for (i=1; i<=p->zs; i++)
435    X[i] = X[i-1] + exp(tmp*i);
436  tmp = p->w / X[p->zs];
437  for (i=1; i<p->zs; i++)
438    X[i] *= tmp;
439  X[p->zs] = p->w;
440
441  tmp = log (p->tr) / (p->ts-1);
442  for (i=1; i<=p->ts; i++)
443    t[i] = t[i-1] + exp(tmp*i);
444  tmp = p->tfin/t[p->ts];
445  for(i=1; i<p->ts; i++)
446    t[i] *= tmp;
447  t[p->ts] = p->tfin;
448
449  printf("Space  grid: ");
450  for(i=0;i<=p->zs;i++)
451    printf("%.5lf ", X[i]);
452  printf("\nTime  grid: ");
453  for(i=0;i<=p->ts;i++)
454    printf("%.5lf ", t[i]);
455  printf("\n");
456
457  /* tmp = flux/k = -dT/dx; make initial condition with this slope */
458  tmp = p->pow*4./p->k/M_PI/p->spx/p->spy; /* flux/k */
459  if (TS!=0.)
460    for (i=0;i<=p->zs;i++)
461      T[i] = TS + X[i] / p->w * (T0-TS);
462  else
463    {
464      for(i=0;i<=p->zs;i++)
465        T[i] = T0 + tmp * p->tres/p->tfin * sqrt (M_PI)/2. * (p->w - X[i]);
466    }
467
468  /* Calculate matrices: Ag,Bg,Cg, At,Bt,Ct (alpha*theta*stiffness, mass) */
469  for(i=1;i<p->zs;i++)
470    {
471      Ag[i] = -p->al*p->th/(*(X+i)-*(X+i-1));
472      At[i] = (X[i] - X[i-1]) / 6;
473      Bt[i] = (X[i+1] - X[i-1]) / 3;
474      Bg[i] = Bt[i] * 3 * Ag[i] / (X[i] - X[i+1]);
475      Cg[i] = -p->al*p->th/(X[i+1] - X[i]);
476      Ct[i] = (X[i+1] - X[i])/6;
477    }
478  Ag[0] = At[0] = 0;
479  Bg[0] = p->al*p->th / X[1];
480  Bt[0] = X[1] / 3;
481  Cg[0] = -Bg[0];
482  Ct[0] = Bt[0] / 2;
483
484  /* Calculate z-dependent generation function (tmp=flux/k from above).
485     This calculates the integral of the actual Gaussian-fit function q(z) over
486     the shapefunctions in each element phi_i(z), and scales that element's
487     generation function accordingly. */
488  printf ("Penetration depth=%g cm; Z1=%g cm; ratio=%g\n", p->dp, X[1],
489          p->dp / X[1]);
490  if (p->dp==0.)
491    {
492      *Gx=tmp*p->al;
493      for(i=1;i<p->zs;i++)
494        *(Gx+i)=0.;
495    }
496  else
497    {
498      Gx[0] = 0.;
499      tmp *= 1.364242002*p->al/p->dp;
500      for (i=0; i<p->zs; i++)
501        {
502#define Z0 (*(X+i-1))
503#define Z1 (*(X+i))
504          if(i)
505            Gx[i]= -tmp/(Z1-Z0)*p->dp*p->dp/8.* /*Integral over prev interval*/
506              (exp(-4*(Z1/p->dp-1./3.) * (Z1/p->dp-1./3.))-/*of phi_i(z)q(z)dz*/
507               exp(-4*(Z0/p->dp-1./3.) * (Z0/p->dp-1./3.)))+
508              tmp*p->dp/2.*(-Z0/(Z1-Z0)+p->dp/(Z1-Z0)/3.)* /*Erf error correct*/
509              sqrt(M_PI)/2.*(erf(2.*Z1/p->dp-2./3.)-erf(2.*Z0/p->dp-2./3.));
510#undef Z1
511#undef Z0
512#define Z0 (*(X+i))
513#define Z1 (*(X+i+1))
514          Gx[i] += tmp/(Z1-Z0)*p->dp*p->dp/8.*  /*Integral over next interval*/
515            (exp(-4*(Z1/p->dp-1./3.) * (Z1/p->dp-1./3.))- /*of phi_i(z)q(z)dz*/
516             exp(-4*(Z0/p->dp-1./3.) * (Z0/p->dp-1./3.)))+
517            tmp*p->dp/2.*(Z1/(Z1-Z0)-p->dp/(Z1-Z0)/3.)*   /*Erf error correct*/
518            sqrt(M_PI)/2.*(erf(2.*Z1/p->dp-2./3.)-erf(2.*Z0/p->dp-2./3.));
519#undef Z1
520#undef Z0
521        }
522    }
523}
524
525
526/*++++++++++++++++++++++++++++++++++++++
527  Little replacement for strcmp (forgot what was wrong with the old one).
528
529  int mystrcmp Returns 0 if equal, something else otherwise.
530
531  char *string1 First string to compare.
532
533  char *string2 Second string to compare.
534  ++++++++++++++++++++++++++++++++++++++*/
535
536int mystrcmp (char *string1, char *string2)
537{
538  int i=0;
539  while (i<MIN(strlen(string1),strlen(string2)) && string2[i]==string1[i])
540    i++;
541  return string2[i] && string1[i];
542}
543
544
545/*++++++++++++++++++++++++++++++++++++++
546  Ancillary function for paraminp, scans ``field'' string for ``search'' key
547  string presence and returns the int following it.
548
549  int inputint Returns the integer inputted.
550
551  char *field String to search.
552
553  char *search Key to search for.
554
555  int ret Default value to return if nothing is found.
556  ++++++++++++++++++++++++++++++++++++++*/
557
558int inputint (char *field, char *search, int ret)
559{
560  int i;
561  if (field)
562    for (i=0; i+strlen(search) <= strlen(field); i++)
563      if (!(mystrcmp (field+i, search)))
564        sscanf (field+strlen(search)+i, "%d", &ret);
565  return ret;
566}
567
568
569/*++++++++++++++++++++++++++++++++++++++
570  Ancillary function for paraminp, scans ``field'' string for ``search'' key
571  string presence and returns the floating point number following it.
572
573  double inputdouble Returns the floating point number in double form.
574
575  char *field String to search.
576
577  char *search Key to search for.
578
579  double ret Default value to return if nothing is found.
580  ++++++++++++++++++++++++++++++++++++++*/
581
582double inputdouble (char *field, char *search, double ret)
583{
584  int i;
585  if (field)
586    for (i=0; i+strlen(search) <= strlen(field); i++)
587      if (!(mystrcmp (field+i, search)))
588        sscanf (field+strlen(search)+i, "%lf", &ret);
589  return ret;
590}
591
592
593/*++++++++++++++++++++++++++++++++++++++
594  Scan qstring for parameters and store them in ebsurf parameter structure p,
595  using defaults for those not found.
596
597  char *paraminp It returns NULL.
598
599  struct param *p Parameter structure in which to place parameters.
600
601  char *qstring String to scan for parameters.
602  ++++++++++++++++++++++++++++++++++++++*/
603
604char *paraminp(struct param *p, char *qstring)
605{
606  int i=0,j=0;
607  double a,t,t2;
608  FILE *status;
609  static char gfile[80],hfile[80],tfile[80],sfile[80],lfile[80],ffile[80],
610  pfile[80];
611
612  /* Material properties, defaults for liquid titanium */
613  p->al=inputdouble(qstring,"alpha=", 0.1);
614  p->k=inputdouble(qstring,"cond=", 30.)*1e5;
615  p->Ma=inputdouble(qstring,"molmas=", 47.88);
616  p->eps=inputdouble(qstring,"epsilon=", 0.5);            /* Rad. emissivity */
617  p->Tenv=inputdouble(qstring,"envtemp=",20.)+273.15;    /* Cels->internal K */
618  p->rho=inputdouble(qstring,"density=", 4.1);
619  p->mu=inputdouble(qstring,"viscos=", 0.052);
620  p->dsig=inputdouble(qstring,"dsigdT=", 2.6e-4);
621  p->Q0=inputdouble(qstring,"q0vap=", 482.)*1e10;   /* Q0 in kJ/mol->erg/mol */
622  p->S=-inputdouble(qstring,"qSvap=", 4.64e-5);       /* S in inverse Kelvin */
623  p->A=inputdouble(qstring,"ccA=",23200.)*log(10.);    /* Claus-Clap log->ln */
624  p->B=inputdouble(qstring,"ccB=", 11.74)*log(10.);
625  p->C=inputdouble(qstring,"ccC=",-0.66)-0.5;   /* -0.5 for the sqrt in lang */
626  p->D=inputdouble(qstring,"ccD=",0.)*log(10.)/1000.;
627  p->E=TORR2CGSP*exp(p->B)/sqrt(2*M_PI*GR*p->Ma);          /* Langmuir coeff */
628
629  /* Operating parameters */
630  p->pow=inputdouble(qstring,"power=", 200.)*1e10;   /* Net power, kW->erg/s */
631  p->volt=inputdouble(qstring,"voltage=", 30.);                        /* kV */
632  if((p->dp=inputdouble(qstring,"pendep=", 0.)/10000.)==0.)
633    p->dp=depvolt(p->volt)/p->rho; /* Microns penetr, default calc from volt */
634  p->tfin=inputdouble(qstring,"mfreq=", 200.); /* for now tfin has frequency */
635  p->xfrq=inputdouble(qstring,"xfreq=", 6.);   /* X and y freqs for patterns */
636  p->yfrq=inputdouble(qstring,"yfreq=", 7.);
637  if(p->tfin==0.)
638    errer("Zero frequency not permitted.");
639  p->plen=inputdouble(qstring,"patlen=", 240.);       /* Beam pattern length */
640  p->pwid=inputdouble(qstring,"patwid=", 7.5);         /* Width for patterns */
641  p->x=inputdouble(qstring,"locX=", 0.);       /* X and y where we calculate */
642  p->y=inputdouble(qstring,"locY=", 0.);
643  p->vb=inputdouble(qstring,"vbeam=",   /* Default velocity=patlen*frequency */
644                    p->plen*p->tfin);
645  if(p->vb==0.)
646    errer("Zero beam velocity and pattern length not permitted.");
647  p->tfin=1./p->tfin;                               /* Now tfin holds period */
648  if((p->spx=inputdouble(qstring,"spsize=", 2.5))==0.||
649     (p->spy=inputdouble(qstring,"spwid=", 2.5))==0.)
650    errer("Zero spot area not permitted.");
651  p->tres=inputdouble(qstring,"dwell=",     /* Default restime=size/velocity */
652                         p->spx/p->vb);
653  p->Tsurf=inputdouble(qstring,"Tsurf=", 1750.);
654  p->Tin=inputdouble(qstring,"Tbot=", 0.);
655  p->Tsurf += (p->Tsurf==0.) ? 0. : 273.15;           /* Celsius->internal K */
656  p->Tin += (p->Tin==0.) ? 0. : 273.15;
657
658  /* Simulation parameters */
659  p->zs=inputint(qstring,"zsteps=", 45);             /* Steps in z-direction */
660  p->zr=inputdouble(qstring,"zrat=", 1.);            /* Ratio max/min deltaz */
661  p->ap=inputdouble(qstring,"aleph=", 1.);/* Used for default timestep ratio */
662  p->bp=inputdouble(qstring,"bapheth=", 9.);   /* Used for default thickness */
663  p->w=inputdouble(qstring,"width=",         /* Thickness of layer simulated */
664                   p->dp + sqrt(p->bp*p->al*p->tfin));
665  p->b=inputdouble(qstring,"bsplit=", 5.);  /* Approx. timesteps during tres */
666  if((p->tr=inputdouble(qstring,"trat=",0.))==0.)    /* Ratio max/min deltat */
667    {
668      if (p->zr==1) /* calculate t, the smallest element size */
669        t=p->w/p->zs; /* straightforward if z ratio = 1 */
670      else
671        {
672          a=exp(log(p->zr)/(p->zs-1)); /* ratio of adjacent element sizes */
673          if(a<1.)
674            a=1./a; t=0;
675          for(j=0;j<p->zs;j++)
676            t+=pow(a,(double)j);
677          t=p->w/t;
678        }
679      if(p->tres/p->b > t*t/p->al/p->ap)
680        {
681          p->tr=1; /* stability criterion/aleph smaller than bsplit/tres */
682          if(p->ts==0)
683            p->ts=p->tfin/t/t*p->al*p->ap;
684        }
685      else
686        p->tr= (t*t/p->al/p->ap) / (p->tres/p->b);
687    }
688  if ((p->ts=inputint(qstring,"tsteps=", 0))==0)
689    {
690      t=1.;
691      for (p->ts=2; p->tfin/t>p->tres/p->b; p->ts*=2)
692        {
693          a=exp(log(p->tr)/(p->ts-1));
694          if (a<1.)
695            a=1./a;
696          t=0;
697          for (j=0;j<p->ts;j++)
698            t+=pow(a,(double)j);
699        }
700      for (i=p->ts/2; i>1; i/=2)
701        {
702          if(p->tfin/t>p->tres/p->b)
703            p->ts+=i;
704          else
705            p->ts-=i;
706          t=0;
707          a=exp(log(p->tr)/(p->ts-1));
708          for(j=0;j<p->ts;j++)
709            t+=pow(a,(double)j);
710        }
711      if(p->tfin/t>p->tres/p->b) p->ts++;
712    }
713  p->th=inputdouble(qstring,"theta=",0.5);   /* Implicitness, 0.5=Crank-Nich */
714  p->it=inputdouble(qstring,"itol=",0.0001); /* Max dT for timestep converge */
715  p->ct=inputdouble(qstring,"ctol=",0.01);     /* Max dT for cycles converge */
716  p->cyc=inputint(qstring,"cyc=", 0);/* Number of cycles to run, 0->converge */
717
718  /* Calculate real Aleph and Bapheth, in case tr and ts were set. */
719  if(p->zr==1)
720    t=p->w/p->zs;
721  else
722    {
723      a=exp(log(p->zr)/(p->zs-1));
724      if(a<1.)
725        a=1./a;
726      t=0;
727      for(j=0;j<p->zs;j++)
728        t+=pow(a,(double)j);
729      t=p->w/t;
730    }
731  if(p->tr==1)
732    t2=p->tfin/p->ts;
733  else
734    {
735      a=exp(log(p->tr)/(p->ts-1));
736      if(a>0.)
737        a=1./a;
738      t2=0;
739      for(j=0;j<p->ts;j++)
740        t2+=pow(a,(double)j);
741      t2=p->w/t2;
742    }
743  p->ap=p->w/t*p->w/t/p->al/p->tfin*t2;
744  p->bp=(p->w-p->dp)*(p->w-p->dp)/p->al/p->tfin;
745
746  /* Generation time function */
747  j=inputint(qstring,"pattype=", 0);
748  p->gtime=(j==1)?gtliss:((j==2)?gtdisk:gtnorm);
749  j=inputint(qstring,"xwave=", 1);
750  p->xpat=(j==0)?sino:((j==1)?triang:sawt);
751  j=inputint(qstring,"ywave=", 1);
752  p->ypat=(j==0)?sino:((j==1)?triang:sawt);
753
754  /* Set temporary file names */
755  sprintf(gfile,"gen%d.ps",getpid()); p->gout=gfile;
756  sprintf(hfile,"his%d.ps",getpid()); p->gtou=hfile;
757  sprintf(sfile,"surf%d.ps",getpid()); p->sout=sfile;
758  sprintf(lfile,"last%d.ps",getpid()); p->lout=lfile;
759  sprintf(ffile,"flux%d.ps",getpid()); p->fout=ffile;
760  if (p->gtime!=gtnorm)
761    {
762      sprintf(pfile,"patt%d.ps",getpid());
763      p->pout=pfile;
764    }
765  else
766    p->pout=NULL;
767  if (p->cyc)
768    {
769      sprintf(tfile,"temp%d.ps",getpid()); p->tout=tfile;
770    }
771  else
772    p->tout=NULL;
773
774  return NULL;
775}
776
777
778/*++++++++++++++++++++++++++++++++++++++
779  Search through template file for strang, printing as we search.
780
781  FILE *infile The template file.
782
783  char *strang String to search for.
784  ++++++++++++++++++++++++++++++++++++++*/
785
786void printto (FILE *infile, char *strang)
787{
788  char *a, temp[150];
789  do
790    {
791      a = fgets (temp, 150, infile);
792      if (a && mystrcmp (temp, strang))
793        printf ("%s", temp);
794    }
795  while (a && mystrcmp (temp, strang));
796}
797
798
799/*++++++++++++++++++++++++++++++++++++++
800  Search through file for strang.
801
802  FILE *infile File to search through.
803
804  char *strang String to search for.
805  ++++++++++++++++++++++++++++++++++++++*/
806
807void skipto (FILE *infile, char *strang)
808{
809  char *a, temp[150];
810  do
811    {
812      a = fgets (temp, 150, infile);
813    }
814  while (a && mystrcmp (temp, strang));
815}
816
817
818/*++++++++++++++++++++++++++++++++++++++
819  Generate a single postscript graph of various types.
820
821  struct param *p EBsurf parameter structure.
822
823  double *X The array of z-values.
824
825  double *t The array of time step times.
826
827  double *T The big array of temperatures.
828
829  int msg Which graph to generate.
830  ++++++++++++++++++++++++++++++++++++++*/
831
832void output (struct param *p, double *X, double *t, double *T, int msg)
833{
834  int i,j,cyc;
835  double tmp,maxi(),mini(),bot,top,time,scale;
836  FILE *outs,*inbo;
837  int steps,plots,flags;
838  char bigs[81],inf[80],fname[80]=OUTPUTDIR;
839
840  flags=((msg==TMPS||msg==ONEC)?1:0)|        /* 1: 3D */
841    ((msg==SURF||msg==TMPS||msg==ONEC)?2:0)| /* 2: y or z axis "temperature" */
842    ((msg==SURF||msg==FLUX||msg==GENT)?4:0); /* 4: first axis is time */
843  strcpy(inf,PSDIR);
844  if(flags&1)
845    strcat (inf,"onedim3.ps");
846  else
847    strcat (inf,"onedim.ps");
848
849  switch(msg)
850    {
851    case GENZ:
852      strcpy(bigs,"Beam heat generation <I>G(z)</I>");
853      strcat(fname,p->gout);
854      break;
855    case GENT:
856      strcpy(bigs,"Normalized beam history <I>G(t)</I>");
857      strcat(fname,p->gtou);
858      break;
859    case SURF:
860      strcpy(bigs,"Surface temperatures");
861      strcat(fname,p->sout);
862      break;
863    case TMPS:
864      strcpy(bigs,"All temperatures");
865      strcat(fname,p->tout);
866      break;
867    case ONEC:
868      strcpy(bigs,"Last cycle temperatures");
869      strcat(fname,p->lout);
870      break;
871    case FLUX:
872      strcpy(bigs,"Bottom heat flux");
873      strcat(fname,p->fout);
874      break;
875    case PATT:
876      strcpy(bigs,"Beam pattern");
877      strcat(fname,p->pout);
878      break;
879    }
880
881  printf("<TD><A HREF=\"%s%s\">%s</A></TD>\n", OUTPUTURLDIR,
882         fname+strlen(OUTPUTDIR), bigs);
883  if (!(outs=fopen(fname,"w")))
884    exit(printf("No output file.\n"));
885  if (!(inbo=fopen(inf,"r")))
886    exit(printf("No input file.\n"));
887
888  for (i=0;i<19;i++)
889    {
890      if (!(fgets(bigs,80,inbo)))
891        exit(printf("Input error\n"));
892      fputs(bigs,outs);
893    }
894  cyc = (p->cyc)?p->cyc:1;
895  fflush (outs);
896
897  steps = (flags&4) ? p->ts : p->zs; /* steps in each plot, plots=# of plots */
898  plots = ((flags&4)&&msg!=GENT) ? cyc :
899    1 + p->ts*((msg==TMPS) ? cyc : ((msg==GENZ||msg==GENT) ? 0 : 1));
900  scale = (msg==FLUX) ? p->k/(p->w-X[p->zs-1])/1.e7 :
901    ((msg==GENZ) ? p->k/p->al/1.e10 : 1);
902
903  top = scale * ((msg==FLUX) ? (maxi(T+p->zs-1,p->ts*cyc+1,p->zs+1)-p->Tin) :
904                 ((msg==GENT) ? 1 : maxi(T,(steps+1)*plots,1)));
905  bot = (flags&2) ? mini(T,top,(steps+1)*plots,1) : 0;/* Minimum, maximum of */
906                                                      /* abscissa */
907  time = p->tfin * ((msg==GENT||msg==ONEC) ? 1 : cyc);
908  if (flags&2)
909    {
910      top-=273.15; bot-=273.15;
911    }
912  if (msg==PATT)
913    {
914      time=p->plen; bot=-.5*p->pwid; top=.5*p->pwid;
915    }
916
917  if (flags&1)
918    {
919      fprintf (outs,"/lims { 0 %lf 0 %lf %lf %lf } def\n", time, p->w, bot,
920               top-bot);
921      fprintf (outs,"/labels { (t, sec) (z, cm) (Temperature, C) } def\n");
922    }
923  else
924    {
925      fprintf (outs,"/lims { %lf %lf %le %le } def\n",
926              (msg==PATT)?-.5*time:0.,(msg==GENZ)?p->w:time,bot,top-bot);
927      fprintf (outs,"/labels { (%s) (%s) } def\n",
928               (msg==GENZ)?"depth, cm":"time, s",
929               (msg==GENZ)?"Heat generation, kW/cm^2":
930               ((msg==GENT)?"Normalized heat flux":
931                ((msg==FLUX)?"Heat flux, W/cm^2":"Temperature, C")));
932    }
933  fflush (outs);
934
935  for (i=0; i < ((flags&1) ? 136 : 115); i++)
936    {
937      if (!(fgets(bigs,80,inbo)))
938        exit (printf ("Input read error...\n"));
939      fputs(bigs,outs);
940    }
941
942  if (msg==PATT)
943    { /* Create pattern graph */
944      if(p->gtime==gtdisk)
945        { /* Disk pattern */
946          fprintf(outs,"%lf %lf gom\n",.5*p->plen,0);
947          for(i=1;i<=p->ts;i++)
948            fprintf(outs,"%lf %lf gol\n",
949                    .25*p->plen*(cos(2.*M_PI*t[i]*p->xfrq) +
950                                 cos(2.*M_PI*t[i]*p->yfrq)),
951<