root/trunk/matml/src/ternary/spinodal.c

Revision 505, 14.4 kB (checked in by powell, 2 years ago)

Bug fix: if numsegs=0, don't realloc thesegs

  • Property svn:eol-style set to native
Line 
1/***************************************
2  $Header$
3
4  The spinodal calculation function.
5  ***************************************/
6
7
8#include "freenergy.h"
9#include "gibbs.h"
10#include <stdlib.h>  /*+ For malloc() and free() +*/
11
12
13/*++++++++++++++++++++++++++++++++++++++
14  This function uses dissection and secant iteration to estimate the root of
15  the spinodal function on a line in ternary space between points A and B.
16
17  inline double interpolate Returns the fraction B whose weighted average with
18  A minimizes the spinodal function.
19
20  double A2 C2 variable at point A.
21
22  double A3 C3 variable at point A.
23
24  double AS Spinodal function value at point A.
25
26  double A2 C2 variable at point B.
27
28  double A3 C3 variable at point B.
29
30  double AS Spinodal function value at point B.
31
32  double T Temperature.
33
34  double P Pressure (ignored for now).
35
36  energy_params *eparams Energy parameter functions (all of them).
37
38  int efunc Index indicating which energy function to calculate the spinodal
39  for.
40
41  int iters Number of secant iterations to perform.
42++++++++++++++++++++++++++++++++++++++*/
43
44static inline double interpolate
45(double A2, double A3, double AS, double B2, double B3, double BS,
46 double T, double P, energy_params *eparams, int efunc, int iters)
47{
48  int i, binaries=10;
49  energy_point X;
50  double interp=0., intfrac=1., XS;
51
52  for (i=0; i<binaries; i++)
53    {
54      X.C2 = 0.5 * (A2+B2);
55      X.C3 = 0.5 * (A3+B3);
56      X.efunc = -1;
57      free_energies (&X, 1, T,P, eparams,efunc, WITH_DERIVATIVES_NO_INFINITY);
58      XS = X.G22 * X.G33 - X.G23 * X.G23;
59
60      if (AS * XS > 0)
61        {
62          interp += intfrac * 0.5;
63          intfrac *= 0.5;
64          A2 = X.C2;
65          A3 = X.C3;
66          AS = XS;
67        }
68      else
69        {
70          intfrac *= 0.5;
71          B2 = X.C2;
72          B3 = X.C3;
73          BS = XS;
74        }
75    }
76
77  for (i=0; i<iters; i++)
78    {
79      X.C2 = (AS*B2 - BS*A2) / (AS - BS);
80      X.C3 = (AS*B3 - BS*A3) / (AS - BS);
81      X.efunc = -1;
82      free_energies (&X, 1, T,P, eparams,efunc, WITH_DERIVATIVES_NO_INFINITY);
83      XS = X.G22 * X.G33 - X.G23 * X.G23;
84
85      if (AS * XS > 0)
86        {
87          interp += intfrac * AS / (AS - BS);
88          intfrac *= -BS / (AS-BS);
89          A2 = X.C2;
90          A3 = X.C3;
91          AS = XS;
92        }
93      else
94        {
95          intfrac *= AS / (AS-BS);
96          B2 = X.C2;
97          B3 = X.C3;
98          BS = XS;
99        }
100    }
101
102  return interp + intfrac * AS / (AS - BS);
103}
104
105
106/*++++++++++++++++++++++++++++++++++++++
107  This calculates and returns all of the spinodal curves for a set of points
108  and triangles.  The spinodal criterion in a ternary system is:
109  +latex+\begin{equation}
110  +latex+  \label{eq:ternaryspinodal}
111  +latex+  G_{22}G_{33} - G_{23}^2 = 0.
112  +latex+\end{equation}
113  +latex+where $G_{ij}$ is the second derivative of free energy with respect
114  +latex+to the $i$ and $j$ concentration variables.
115  +html+ <center><i>G</i><sub>22</sub> <i>G</i><sub>33</sub> -
116  +html+ <i>G</i><sub>23</sub><sup>2</sup>,</center>
117  +html+ where <i>G<sub>ij</sub></i> is the second derivative of free energy
118  +html+ with respect to the <i>i</i> and <i>j</i> concentration variables.
119  This calculates the criterion at each vertex of every triangle, and if it
120  crosses zero on a line segment in the triangle, adds any new end points to
121  the points list, and the line to the returned phase boundary structure.
122  +latex+\par
123  +html+ <p>
124  This is second order accurate in the initial resolution, which might not be
125  sufficient for some applications.  To improve the resolution, one can call
126  this with
127  +latex+{\tt spinreturn} set to {\tt NULL},
128  +html+ <tt>spinreturn</tt> set to <tt>NULL</tt>,
129  so it generates new points close to the spinodal, any number of times, then
130  call it with a pointer to a valid
131  +latex+{\tt phase\_boundary}
132  +html+ <tt>phase_boundary</tt>
133  structure to get a "final" spinodal estimate.  This is analogous to secant
134  method iteration.
135
136  int calc_spinodal It returns zero or an error code.
137
138  energy_point **points Points in the array, this function adds points to the
139  end of the array.
140
141  int *n_points Pointer to the number of points in the points array, which this
142  function modifies as it adds new points.
143
144  int *triangle_indices 3*n_triangles integers indicating the points which make
145  up each triangle.
146
147  int n_triangles Number of triangles.
148
149  double T Temperature.
150
151  double P Pressure (ignored for now).
152
153  energy_params *eparams Free energy function parameters (all of them).
154
155  int efunc Index indicating which set of energy parameters to use.
156
157  phase_boundary *spinreturn Pointer to phase boundary structure which contains
158  the spinodal on return, NULL to simply add points close to the spinodal.
159  ++++++++++++++++++++++++++++++++++++++*/
160
161int calc_spinodal
162(energy_point **points, int *n_points, int *triangle_indices, int n_triangles,
163 double T, double P, energy_params *eparams, int efunc,
164 phase_boundary *spinreturn)
165{
166  int tri, numsegs=0, thesegs_size=20, *thesegs, numnewpoints=0,
167    thenewpoints_size=20;
168  energy_point *thenewpoints;
169
170  //printf ("Calculating free energies at all points\n");
171  //if (tri = free_energies (*points, *n_points, T, P, eparams, efunc,
172  //                       WITH_DERIVATIVES_NO_INFINITY))
173  //  return tri;
174
175  // Initial allocation of new points and segments
176#ifdef DEBUG
177  printf ("Allocating new points and segments arrays\n");
178#endif
179  if (!(thenewpoints = (energy_point *) malloc
180        (thenewpoints_size * sizeof (energy_point))))
181    return 1;
182  if (!(thesegs = (int *) malloc (2 * thesegs_size * sizeof (int))))
183    { free (thenewpoints); return 1; }
184
185  // Main loop over triangles to calculate spinodal intersections with each
186#ifdef DEBUG
187  printf ("Entering main spinodal loop, %d points, first triangle points %d, %d, %d\n", *n_points, triangle_indices[0], triangle_indices[1], triangle_indices[2]);
188#endif
189  for (tri=0; tri<n_triangles; tri++)
190    {
191      double spin[3], A2=-1, A3, B2=-1, B3, interp;
192      int total;
193      energy_point P0=(*points)[triangle_indices[3*tri]],
194        P1=(*points)[triangle_indices[3*tri+1]],
195        P2=(*points)[triangle_indices[3*tri+2]];
196
197      if (P0.efunc == efunc)
198        {
199          // The spinodal criterion
200          spin[0] = P0.G22 * P0.G33 - P0.G23 * P0.G23;
201          spin[1] = P1.G22 * P1.G33 - P1.G23 * P1.G23;
202          spin[2] = P2.G22 * P2.G33 - P2.G23 * P2.G23;
203
204          /*+ For each triangle, this sets a "total" score according to the
205            signs of the spinodal criterion values at the corners. +*/
206          total = ((spin[0] > 0.) ? 2 : ((spin[0] == 0.) ? 1 : 0));
207          total += ((spin[1] > 0.) ? 8 : ((spin[1] == 0.) ? 4 : 0));
208          total += ((spin[2] > 0.) ? 32 : ((spin[2] == 0.) ? 16 : 0));
209
210#ifdef DEBUG
211          printf ("Tri %d (%d,%d,%d): spins %g,%g,%g; total %d\n",
212                  tri, triangle_indices[3*tri], triangle_indices[3*tri+1],
213                  triangle_indices[3*tri+2], spin[0],spin[1],spin[2], total);
214#endif
215        }
216      else
217        total = 0;
218
219      // First decide whether to allocate a new segment
220      switch (total)
221        {
222        case 0// neg neg neg
223        case 21: //  0   0   0  -- degenerate, probably flat
224        case 42: // pos pos pos
225        case 1//  0  neg neg
226        case 4// neg  0  neg
227        case 16: // neg neg  0
228        case 41: //  0  pos pos
229        case 38: // pos  0  pos
230        case 26: // pos pos  0
231        case 5// 0 0 neg
232        case 37: // 0 0 pos
233        case 17: // 0 neg 0
234        case 25: // 0 pos 0
235        case 20: // neg 0 0
236        case 22: // pos 0 0
237          {
238            A2 = B2 = -1; // No new points for these cases
239            break; // No new seg for these cases either
240          }
241        default:
242          {
243#ifdef DEBUG
244            printf ("Making a new segment...");
245#endif
246            if (++numsegs >= thesegs_size)
247              if (!(thesegs = (int *) realloc
248                    (thesegs, 2*(thesegs_size*=2)*sizeof (int))))
249                { free (thenewpoints); free (thesegs); return 1; }
250          }
251        }
252
253      switch (total)
254        {
255          // New Points between 0 and 1, 0 and 2
256        case 2// pos neg neg
257        case 40: // neg pos pos
258          {
259            interp = interpolate
260              (P0.C2,P0.C3,spin[0], P1.C2,P1.C3,spin[1], T,P,eparams,efunc, 9);
261            A2 = (1-interp) * P0.C2 + interp * P1.C2;
262            A3 = (1-interp) * P0.C3 + interp * P1.C3;
263            interp = interpolate
264              (P0.C2,P0.C3,spin[0], P2.C2,P2.C3,spin[2], T,P,eparams,efunc, 9);
265            B2 = (1-interp) * P0.C2 + interp * P2.C2;
266            B3 = (1-interp) * P0.C3 + interp * P2.C3;
267            thesegs[2*numsegs-2] = *n_points + numnewpoints;
268            thesegs[2*numsegs-1] = *n_points + numnewpoints + 1;
269#ifdef DEBUG
270            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
271#endif
272            break;
273          }
274          // New Points between 0 and 1, 1 and 2
275        case 8// neg pos neg
276        case 34: // pos neg pos
277          {
278            interp = interpolate
279              (P0.C2,P0.C3,spin[0], P1.C2,P1.C3,spin[1], T,P,eparams,efunc, 9);
280            A2 = (1-interp) * P0.C2 + interp * P1.C2;
281            A3 = (1-interp) * P0.C3 + interp * P1.C3;
282            interp = interpolate
283              (P1.C2,P1.C3,spin[1], P2.C2,P2.C3,spin[2], T,P,eparams,efunc, 9);
284            B2 = (1-interp) * P1.C2 + interp * P2.C2;
285            B3 = (1-interp) * P1.C3 + interp * P2.C3;
286            thesegs[2*numsegs-2] = *n_points + numnewpoints;
287            thesegs[2*numsegs-1] = *n_points + numnewpoints + 1;
288#ifdef DEBUG
289            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
290#endif
291            break;
292          }
293          // New Points between 0 and 2, 1 and 2
294        case 10: // pos pos neg
295        case 32: // neg neg pos
296          {
297            interp = interpolate
298              (P0.C2,P0.C3,spin[0], P2.C2,P2.C3,spin[2], T,P,eparams,efunc, 9);
299            A2 = (1-interp) * P0.C2 + interp * P2.C2;
300            A3 = (1-interp) * P0.C3 + interp * P2.C3;
301            interp = interpolate
302              (P1.C2,P1.C3,spin[1], P2.C2,P2.C3,spin[2], T,P,eparams,efunc, 9);
303            B2 = (1-interp) * P1.C2 + interp * P2.C2;
304            B3 = (1-interp) * P1.C3 + interp * P2.C3;
305            thesegs[2*numsegs-2] = *n_points + numnewpoints;
306            thesegs[2*numsegs-1] = *n_points + numnewpoints + 1;
307#ifdef DEBUG
308            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
309#endif
310            break;
311          }
312          /* Degenerate cases: two zeroes mean no new points
313        case 5:  // 0 0 neg
314        case 37: // 0 0 pos
315          {
316            A2 = B2 = -1;
317            thesegs[2*numsegs-2] = triangle_indices[3*tri];
318            thesegs[2*numsegs-1] = triangle_indices[3*tri+1];
319#ifdef DEBUG
320            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
321#endif
322            break;
323          }
324        case 17: // 0 neg 0
325        case 25: // 0 pos 0
326          {
327            A2 = B2 = -1;
328            thesegs[2*numsegs-2] = triangle_indices[3*tri];
329            thesegs[2*numsegs-1] = triangle_indices[3*tri+2];
330#ifdef DEBUG
331            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
332#endif
333            break;
334          }
335        case 20: // neg 0 0
336        case 22: // pos 0 0
337          {
338            A2 = B2 = -1;
339            thesegs[2*numsegs-2] = triangle_indices[3*tri+1];
340            thesegs[2*numsegs-1] = triangle_indices[3*tri+2];
341#ifdef DEBUG
342            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
343#endif
344            break;
345            }*/
346          // Degenerate cases: one zero one pos one neg means one new point
347        case 9// 0 pos neg
348        case 33: // 0 neg pos
349          {
350            interp = interpolate
351              (P1.C2,P1.C3,spin[1], P2.C2,P2.C3,spin[2], T,P,eparams,efunc, 9);
352            A2 = (1-interp) * P1.C2 + interp * P2.C2;
353            A3 = (1-interp) * P1.C3 + interp * P2.C3;
354            B2 = -1;
355            thesegs[2*numsegs-2] = triangle_indices[3*tri];
356            thesegs[2*numsegs-1] = *n_points + numnewpoints;
357#ifdef DEBUG
358            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
359#endif
360            break;
361          }
362        case 6// pos 0 neg
363        case 36: // neg 0 pos
364          {
365            interp = interpolate
366              (P0.C2,P0.C3,spin[0], P2.C2,P2.C3,spin[2], T,P,eparams,efunc, 9);
367            A2 = (1-interp) * P0.C2 + interp * P2.C2;
368            A3 = (1-interp) * P0.C3 + interp * P2.C3;
369            B2 = -1;
370            thesegs[2*numsegs-2] = triangle_indices[3*tri+1];
371            thesegs[2*numsegs-1] = *n_points + numnewpoints;
372#ifdef DEBUG
373            printf ("%d, %d\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1]);
374#endif
375            break;
376          }
377        case 18: // pos neg 0
378        case 24: // neg pos 0
379          {
380            interp = interpolate
381              (P0.C2,P0.C3,spin[0], P1.C2,P1.C3,spin[1], T,P,eparams,efunc, 9);
382            A2 = (1-interp) * P0.C2 + interp * P1.C2;
383            A3 = (1-interp) * P0.C3 + interp * P1.C3;
384            B2 = -1;
385            thesegs[2*numsegs-2] = triangle_indices[3*tri+2];
386            thesegs[2*numsegs-1] = *n_points + numnewpoints;
387#ifdef DEBUG
388            printf ("%d, %d (%d)\n", thesegs[2*numsegs-2], thesegs[2*numsegs-1], total);
389#endif
390            break;
391          }
392        }
393
394      if (A2 > -1.)
395        {
396          if (++numnewpoints >= thenewpoints_size)
397            if (!(thenewpoints = (energy_point *)
398                  realloc (thenewpoints,
399                           (thenewpoints_size*=2)*sizeof (energy_point))))
400              { free (thenewpoints); free (thesegs); return 1; }
401          thenewpoints [numnewpoints-1] .C2 = A2;
402          thenewpoints [numnewpoints-1] .C3 = A3;
403          thenewpoints [numnewpoints-1] .efunc = efunc;
404          thenewpoints [numnewpoints-1] .T = -1;
405          thenewpoints [numnewpoints-1] .P = -1;
406#ifdef DEBUG
407          printf ("New point A%d: %g, %g\n", *n_points+numnewpoints-1, A2, A3);
408#endif
409        }
410
411      if (B2 > -1.)
412        {
413          if (++numnewpoints >= thenewpoints_size)
414            if (!(thenewpoints = (energy_point *)
415                  realloc (thenewpoints,
416                           (thenewpoints_size*=2)*sizeof (energy_point))))
417              { free (thenewpoints); free (thesegs); return 1; }
418          thenewpoints [numnewpoints-1] .C2 = B2;
419          thenewpoints [numnewpoints-1] .C3 = B3;
420          thenewpoints [numnewpoints-1] .efunc = efunc;
421          thenewpoints [numnewpoints-1] .T = -1;
422          thenewpoints [numnewpoints-1] .P = -1;
423#ifdef DEBUG
424          printf ("New point B%d: %g, %g\n", *n_points+numnewpoints-1, B2, B3);
425#endif
426        }
427    }
428
429  // Calculate new points' free energies
430  if (tri=free_energies (thenewpoints, numnewpoints, T, P, eparams, efunc,
431                         WITH_DERIVATIVES_NO_INFINITY))
432    {
433      free(thenewpoints);
434      free(thesegs);
435      return tri;
436    }
437
438  // Look for duplicate segs and points among numnewpoints
439  // *** THIS IS A TODO... ***
440
441
442  // Add new points to old points array
443  if (!(*points = (energy_point *) realloc
444        (*points, (*n_points+numnewpoints)*sizeof (energy_point))))
445    { free (thenewpoints); free (thesegs); return 1; }
446  for (tri=0; tri<numnewpoints; tri++)
447    (*points) [*n_points+tri] = thenewpoints [tri];
448  *n_points += numnewpoints;
449
450  // Fill in spinreturn
451  if (spinreturn)
452    {
453      spinreturn->compos = 2;
454      if (numsegs)
455        {
456          if (!(thesegs = (int *) realloc
457                (thesegs, 2*numsegs*sizeof (int))))
458            { free (thenewpoints); free (thesegs); return 1; }
459        }
460      else
461        {
462          free (thesegs);
463          thesegs = NULL;
464        }
465      spinreturn->edges = thesegs;
466#ifdef DEBUG
467      printf ("First edge: %d %d\n", spinreturn->edges [0], spinreturn->edges [1]);
468#endif
469      spinreturn->n_edges = numsegs;
470      if (!(spinreturn->phases = (int *) malloc (sizeof (int))))
471        { free (thenewpoints); free (thesegs); return 1; }
472      *(spinreturn->phases) = efunc;
473      spinreturn->n_phases = 1;
474      spinreturn->type = SPINODAL;
475    }
476
477  free(thenewpoints);
478  return 0;
479}
Note: See TracBrowser for help on using the browser.