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

Revision 463, 36.4 kB (checked in by powell, 3 years ago)

Changed phase boundary "flags" to "type" which is more fitting.

  • Property svn:eol-style set to native
Line 
1/***************************************
2  $Header$
3
4  This file includes the function(s) which interact with qhull to compute the
5  convex hull of the free energy function points.
6  ***************************************/
7
8
9#include "freenergy.h"
10#include "gibbs.h"
11#include <qhull/qset.h>
12#include <qhull/qhull.h>
13#include <stdlib.h>
14#include <math.h>
15
16
17static int qhull_lock=0; /*+ Lock for qhull memory and structures +*/
18
19
20/*++++++++++++++++++++++++++++++++++++++
21  This determines the facet type from the minimum energy at the midpoint of
22  each edge.
23
24  facet_type facettype It returns the facet type.
25
26  coordT *corners Coordinates C2, C3, G of the three facet corners.
27
28  energy_params *eparams Collection of energy parameter structures.
29
30  int nparams Number of energy parameter structures in eparams.
31  ++++++++++++++++++++++++++++++++++++++*/
32
33static facet_type facettype (coordT *corners, energy_params *eparams,
34                             int nparams, double T, double P)
35{
36  int i, cparam, ret=0;
37  double edgenergy, edgecenter[3];
38
39  edgecenter[0] = (corners[0] + corners[3]) / 2.;
40  edgecenter[1] = (corners[1] + corners[4]) / 2.;
41  edgecenter[2] = (corners[2] + corners[5]) / 2.;
42  edgenergy = free_energy (edgecenter[0],edgecenter[1], T,P, eparams);
43  cparam=0;
44  for (i=1; i<nparams; i++)
45    if (edgenergy > free_energy (edgecenter[0],edgecenter[1], T,P, eparams+i))
46      {
47        edgenergy = free_energy (edgecenter[0],edgecenter[1], T,P, eparams+i);
48        cparam = i;
49      }
50  if (edgenergy > edgecenter[2])
51    ret++;
52#ifdef DEBUG
53  printf ("   Type: side 0-1 edge %g mid %g, ", edgenergy, edgecenter [2]);
54#endif
55     
56  edgecenter[0] = (corners[0] + corners[6]) / 2.;
57  edgecenter[1] = (corners[1] + corners[7]) / 2.;
58  edgecenter[2] = (corners[2] + corners[8]) / 2.;
59  edgenergy = free_energy (edgecenter[0],edgecenter[1], T,P, eparams);
60  cparam=0;
61  for (i=1; i<nparams; i++)
62    if (edgenergy > free_energy (edgecenter[0],edgecenter[1], T,P, eparams+i))
63      {
64        edgenergy = free_energy (edgecenter[0],edgecenter[1], T,P, eparams+i);
65        cparam = i;
66      }
67  if (edgenergy > edgecenter[2])
68    ret++;
69#ifdef DEBUG
70  printf ("0-2 edge %g mid %g, ", edgenergy, edgecenter [2]);
71#endif
72     
73  edgecenter[0] = (corners[3] + corners[6]) / 2.;
74  edgecenter[1] = (corners[4] + corners[7]) / 2.;
75  edgecenter[2] = (corners[5] + corners[8]) / 2.;
76  edgenergy = free_energy (edgecenter[0],edgecenter[1], T,P, eparams);
77  cparam=0;
78  for (i=1; i<nparams; i++)
79    if (edgenergy > free_energy (edgecenter[0],edgecenter[1], T,P, eparams+i))
80      {
81        edgenergy = free_energy (edgecenter[0],edgecenter[1], T,P, eparams+i);
82        cparam = i;
83      }
84  if (edgenergy > edgecenter[2])
85    ret++;
86#ifdef DEBUG
87  printf ("1-2 edge %g mid %g", edgenergy, edgecenter [2]);
88#endif
89
90  return (facet_type) ret;
91}
92
93
94/*++++++++++++++++++++++++++++++++++++++
95  This initializes qhull and uses it to calculate the convex hull of a set of
96  ternary compositions, clipping everything above the energy defined by the
97  three corners.  It then builds a set of hull_facet structures, and returns
98  those, along with the number of calculated facets.  It frees the qhull
99  structures before returning.
100
101  int hullCalculate It returns zero or an error code.
102
103  int dim Number of dimensions (should always be 3 for ternary).
104
105  energy_point *points Point information.
106
107  int numpoints Number of points of which to calculate the convex hull.
108
109  energy_params *eparams Collection of energy parameter structures.
110
111  int nparams Number of energy parameter structures in eparams.
112
113  double T Temperature.
114
115  double P Pressure.
116
117  hull_facet **tfacets Array of returned facets.  This is changed by realloc()
118  each time the function is called, so it should be NULL or a valid array.
119
120  int *numfacets Number of facets returned.
121++++++++++++++++++++++++++++++++++++++*/
122
123int hullCalculate
124(int dim, energy_point *points, int numpoints, energy_params *eparams,
125 int nparams, double T, double P, hull_facet **facets, int *numfacets)
126{
127  int i, corner1=-1, corner2=-1, corner3=-1;
128  double G1, G2, G3;
129  coordT *qpoints; /*+ Point coordinate storage for qhull +*/
130  facetT *facet; /*+ For the FORALLfacets loop +*/
131
132  if (dim != 3)
133    { printf ("qhullCalcHull: Non-3-D spaces not yet supported\n"); return -1; }
134
135  /*+ This first copies the points array for qhull, and clips the array at the
136    plane defined by the corners. +*/
137  if (!(qpoints = malloc (dim*numpoints*sizeof(coordT))))
138    { printf ("qhullCalcHull: could not allocate memory for points\n");
139      return -1; }
140
141  if (qhull_lock)
142    { printf ("Qhull use is currently locked\n"); return 1; }
143  qhull_lock=1;
144
145  for (i=0; i<numpoints; i++)
146    {
147      // Copy compositions into the new array
148      qpoints[3*i]   = (coordT) points[i].C2;
149      qpoints[3*i+1] = (coordT) points[i].C3;
150
151      // Find the top corners
152      if ((points[i].C2 == 0. && points[i].C3 == 0.) &&
153          (corner1 == -1 || points[i].G > G1))
154        {
155          corner1 = i;
156          G1 = points [corner1].G;
157        }
158      if ((points[i].C2 == 0. && points[i].C3 == 1.) &&
159          (corner2 == -1 || points[i].G > G2))
160        {
161          corner2 = i;
162          G2 = points [corner2].G;
163        }
164      if ((points[i].C2 == 1. && points[i].C3 == 0.) &&
165          (corner3 == -1 || points[i].G > G2))
166        {
167          corner3 = i;
168          G2 = points [corner3].G;
169        }
170    }
171#ifdef DEBUG
172  printf ("Corners: %d, %d, %d\n", corner1, corner2, corner3);
173#endif
174
175  // Copy free energy into the new array, clipping it at the plane given by the
176  // corners
177  for (i=0; i<numpoints; i++)
178    qpoints[3*i+2] = (coordT)
179      fmin (points[i].G, points[corner1].G +
180            (points[corner2].G-points[corner1].G) * points[i].C3 +
181            (points[corner3].G-points[corner1].G) * points[i].C2);
182
183  // Initialize qhull and calculate the hull
184  qh_init_A (stdin, stdout, stderr, 0, NULL);
185  qh_init_B (qpoints, numpoints, dim, False);
186  qh_qhull ();
187#ifdef DEBUG
188  qh_printsummary (stdout);
189#endif
190
191  /*+ After qhull runs, this reallocates the array pointed to by facets,
192    then fills it, eliminating non-simplical facets along the way (which seem
193    to typically be the binaries). +*/
194  *numfacets = qh num_facets;
195#ifdef DEBUG
196  printf ("%d total facets\n", *numfacets);
197  printf ("facets: pre-realloc 0x%lx, ", *facets);
198#endif
199  if (!(*facets = realloc (*facets, *numfacets * sizeof (hull_facet))))
200    {
201      printf ("hullReturnFacets: could not reallocate memory for vertices\n");
202      free (qpoints);
203      qh_freeqhull (True);
204      qhull_lock=0;
205      return -1;
206    }
207#ifdef DEBUG
208  printf ("  post-realloc 0x%lx\n", *facets);
209#endif
210
211  i=0;
212  FORALLfacets
213    {
214      int j=0;
215      coordT corners [9], projarea;
216      vertexT *vertex, **vertexp;
217
218#ifdef DEBUG
219      printf ("facet %d:\n", i);
220#endif
221
222      FOREACHvertex_(facet->vertices)
223        {
224          int thepoint = (vertex->point-qh first_point)/3;
225
226          if (j<3)
227            {
228              (*facets) [i].vertex[j] = thepoint;
229              corners [3*j]   = vertex->point[0];
230              corners [3*j+1] = vertex->point[1];
231              corners [3*j+2] = vertex->point[2];
232#ifdef DEBUG
233              printf ("  vertex %d, coords %g %g %g\n", thepoint,
234                      vertex->point[0], vertex->point[1], vertex->point[2]);
235#endif
236              j++;
237            }
238        }
239         
240      /*+ This calculates the area of the projection of each facet.
241        That area is given by the determinant of the matrix formed by the
242        vectors making up the edges of the facet divided by
243        (dimensionality-1)!.
244        +latex+For a ternary system, that's
245        +latex+$\frac12[(x_1-x_0)(y_2-y_0) - (x_2-x_0)(y_1-y_0)]$.
246        +*/
247      projarea =
248        (corners[3]-corners[0]) * (corners[7]-corners[1]) -
249        (corners[6]-corners[0]) * (corners[4]-corners[1]);
250#ifdef DEBUG
251      printf ("  xyarea %g\n", projarea);
252#endif
253
254      // Remove side facets (zero area) and facet with the three corners
255      if (projarea == 0. || fabs (projarea) == 1.)
256        (*numfacets)--;
257      else
258        {
259          (*facets) [i].type = facettype (corners, eparams, nparams, T, P);
260#ifdef DEBUG
261          printf ("  Type %d\n", (*facets) [i].type);
262#endif
263          i++;
264        }
265    }
266
267  free (qpoints);
268  qh_freeqhull (True);
269  qhull_lock=0;
270
271#ifdef DEBUG
272  printf ("After trimming loop: %d total facets, i=%d\n", *numfacets, i);
273  printf ("facets: pre-realloc 0x%lx, ", *facets);
274#endif
275  if (!(*facets = realloc (*facets, *numfacets * sizeof (hull_facet))))
276    { printf ("hullReturnFacets: could not reallocate memory for vertices\n");
277      return -1; }
278#ifdef DEBUG
279  printf ("  post-realloc 0x%lx\n", *facets);
280#endif
281
282  return 0;
283}
284
285
286/*++++++++++++++++++++++++++++++++++++++
287  This uses Newton iteration to refine a single point within a triangle.
288
289  coordT *point Composition coordinates of the starting point for refining; the
290  result will be returned here.
291
292  coordT *corners Coordinates (including energies) of the corners of this
293  facet, used for the facet normal: C21,C31,G1, C22,C32,G2, C23,C33,G3.
294
295  energy_params *eparams Collection of energy parameter structures.
296
297  double T Temperature.
298
299  double P Pressure.
300
301  int globaldims Dimensionality of the global space.
302
303  int localdims Dimensionality over which to do the refinement.
304
305  double newton_tolerance Stop Newton iterations when the vertex motion falls
306  below this value.
307++++++++++++++++++++++++++++++++++++++*/
308
309static void NewtonRefine
310(coordT *point, coordT *corners, energy_params *eparams, double T, double P,
311 int globaldims, int localdims, double newton_tolerance)
312{
313  int i;
314  double distance = 1., G, slope2, slope3;
315  energy_point current;
316
317  if (globaldims != 3)
318    { printf ("qhullCalcHull: Non-ternaries not yet supported\n"); return; }
319  current.C2 = point[0];
320  current.C3 = point[1];
321
322  G = free_energy (current.C2, current.C3, T, P, eparams);
323
324  /*+ Determine the slope(s) of the facet +*/
325  if (localdims==3)
326    {
327      double x2=corners[3]-corners[0], y2=corners[6]-corners[0],
328        x3=corners[4]-corners[1], y3=corners[7]-corners[1],
329        xG=corners[5]-corners[2], yG=corners[8]-corners[2];
330      double vG=x2*y3-x3*y2;
331      slope2 = (xG*y3-x3*yG)/vG;
332      slope3 = (x2*yG-xG*y2)/vG;
333
334#ifdef DEBUG
335      printf ("      corners: %g %g %g %g %g %g %g %g %g\n      slopes: %g %g\n",
336              corners[0], corners[1], corners[2],
337              corners[3], corners[4], corners[5],
338              corners[6], corners[7], corners[8], slope2, slope3);
339#endif
340    }
341  else
342    {
343      /*
344      // Slope for the binary case
345      if (side==0 || side==2) // C3=0 or C2+C3=1 sides: dG/dC2
346        slope2 = (corners[5]-corners[2])/(corners[3]-corners[0]);
347      else if (side==1) // C2=0 side: dG/dC3
348        slope2 = (corners[5]-corners[2])/(corners[4]-corners[1]);
349      */
350    }
351
352  while (distance >= newton_tolerance * newton_tolerance &&
353         current.C2>=0. && current.C3>=0. &&
354         current.C2<=1. && current.C3<=1.)
355    {
356      double dC2, dC3, det;
357
358      /*+ Calculate the energy derivatives +*/
359      free_energies (&current, 1, T, P, eparams, 0, WITH_DERIVATIVES);
360
361      /*+ Subtract the facet slopes from the energy derivatives +*/
362      current.G2 -= slope2;
363      current.G3 -= slope3;
364
365      /*+ Solve the linear system to estimate the vector to the minimum +*/
366      det = current.G22*current.G33 - current.G23*current.G23;
367      dC2 = (current.G3*current.G23 - current.G2*current.G33) / det;
368      dC3 = (current.G2*current.G23 - current.G3*current.G22) / det;
369
370      /*+ Add that vector to the point +*/
371      current.C2 += dC2;
372      current.C3 += dC3;
373      distance = dC2*dC2 + dC3*dC3;
374#ifdef DEBUG
375      printf ("        Added %g,%g (dist %g) to go to %g,%g\n", dC2,dC3,
376              sqrt(distance), current.C2,current.C3);
377#endif
378    }
379
380  /* Return the current point */
381  point[0] = current.C2;
382  point[1] = current.C3;
383}
384
385
386/*++++++++++++++++++++++++++++++++++++++
387  This refines each of the facets in the hull, using Newton-Raphson iteration
388  to refine down the vertices of multi-phase facets, and (optionally) adding a
389  new vertex at its centroid if in a one-phase region.
390
391  int hullRefine It returns zero or an error code.
392
393  energy_point **points Pointer to point list, which will be modified to
394  accommodate the new points created in the refining process.
395
396  int *numpoints Pointer to number of points of which to calculate the convex
397  hull, which will also be modified.
398
399  hull_facet **facets Pointer to the array of facets to be refined, which will
400  be modified to accommodate new facets created -- and some removed -- in the
401  refining process.
402
403  int *numfacets Pointer to the number of facets comprising the convex hull,
404  which will also be modified.
405
406  energy_params *eparams Collection of energy parameter structures.
407
408  int nparams Number of energy parameter structures in eparams.
409
410  double T Temperature.
411
412  double P Pressure.
413
414  double newton_tolerance Stop Newton iterations when the vertex motion falls
415  below this value.
416
417  double vertex_tolerance Minimum distance between two vertices.  When refining
418  a two-phase facet, two of the vertices should go to the same local minimum;
419  choose this parameter to indicate how close to consider them the "same"
420  point.
421
422  int one_phase_refine Non-zero if the one-phase regions should be refined.
423  ++++++++++++++++++++++++++++++++++++++*/
424
425int hullRefine
426(energy_point **points, int *numpoints, hull_facet **facets, int *numfacets,
427 energy_params *eparams, int nparams, double T,double P,
428 double newton_tolerance, double vertex_tolerance, int one_phase_refine)
429{
430  energy_point *newpoints;
431  int newpointsize=100, numnewpoints=0, i;
432
433  if (!(newpoints = (energy_point *) malloc (100*sizeof(energy_point))))
434    return 1;
435
436  /*+ Steps in the algorithm:
437    +latex+\begin{enumerate}
438    +latex+\item
439    +html+ <ol><li>
440    Loop over the facets
441    +html+ </li>
442    +*/
443
444  for (i=0; i<*numfacets; i++)
445    {
446      double corners [9], centroid [3], Gcenter;
447      int j=0, eparam [3] = {0,0,0}, thepoint [3];
448
449      /*+
450        +latex+\item
451        +html+ <li>
452        Copy vertex information into local variables: composition, energy,
453        closest energy function.
454        +html+ </li>
455        +*/
456      for (j=0; j<3; j++)
457        {
458          thepoint [j] = (*facets) [i].vertex[j];
459
460          corners [3*j]   = (*points) [thepoint[j]].C2;
461          corners [3*j+1] = (*points) [thepoint[j]].C3;
462          corners [3*j+2] = (*points) [thepoint[j]].G;
463          eparam [j] = (*points)[thepoint[j]].efunc;
464        }
465
466#ifdef DEBUG
467      printf ("Facet %d: %g,%g %g,%g %g,%g\n", i,
468              corners[0], corners[1], corners[3],
469              corners[4], corners[6], corners[7]);
470      printf ("  Curves: %d %d %d, points %d %d %d, Area: %g\n",
471              eparam[0], eparam[1], eparam[2],
472              /*thepoint[0], thepoint[1], thepoint[2],I dare you to uncomment!*/
473              fabs((corners[3]-corners[0])*(corners[7]-corners[1]) -
474                   (corners[6]-corners[0])*(corners[4]-corners[1])));
475#endif
476
477      /*+
478        +latex+\item
479        +html+ <li>
480        If this is a one-phase facet, optionally add the centroid as a new
481        vertex.
482        +html+ </li>
483        +*/
484      centroid[0] = (corners[0] + corners[3] + corners[6]) / 3.;
485      centroid[1] = (corners[1] + corners[4] + corners[7]) / 3.;
486      centroid[2] = (corners[2] + corners[5] + corners[8]) / 3.;
487
488      if ((*facets) [i].type == ONE_PHASE && one_phase_refine)
489        {
490#ifdef DEBUG
491          printf ("  Refining one-phase triangle at centroid\n");
492#endif
493          if (numnewpoints >= newpointsize)
494            newpoints = (energy_point *) realloc
495              (newpoints, (newpointsize+=100)*sizeof(energy_point));
496          newpoints [numnewpoints].C2 = centroid[0];
497          newpoints [numnewpoints].C3 = centroid[1];
498          newpoints [numnewpoints].G  = free_energy
499            (centroid[0], centroid[1], T, P, eparams+eparam [0]);
500          newpoints [numnewpoints].efunc = eparam [0];
501          numnewpoints++;
502        }
503
504      /*+
505        +latex+\item
506        +html+ <li>
507        Otherwise, use Newton-Raphson iteration to refine each vertex,
508        which consists of the remaining steps.
509        +html+ </li>
510        +*/
511      else if ((*facets) [i].type != ONE_PHASE)
512        {
513          coordT newcorners [9] = { corners[0], corners[1], corners[2],
514                                    corners[3], corners[4], corners[5],
515                                    corners[6], corners[7], corners[8]};
516
517#ifdef DEBUG
518          printf ("  Refining multi-phase triangle\n");
519#endif
520          /*+
521            +latex+\item
522            +html+ <li>
523            If the vertex is on a side of the phase diagram (one or more
524            composition variables is zero or they sum to one), refine generally
525            starting a bit closer to the others.
526            +html+ </li>
527            +*/
528          for (j=0; j<3; j++)
529            {
530              if (corners[3*j] == 0. || corners[3*j+1] == 0. ||
531                  corners[3*j] == 1. || corners[3*j+1] == 1. ||
532                  corners[3*j] + corners[3*j+1] == 1.)
533                {
534                  newcorners[3*j] =
535                    0.9999999998 * corners[3*j] +
536                    0.0000000001 * corners[3*((j+1)%3)] +
537                    0.0000000001 * corners[3*((j+2)%3)];
538                  newcorners[3*j+1] =
539                    0.9999999998 * corners[3*j+1] +
540                    0.0000000001 * corners[3*((j+1)%3)+1] +
541                    0.0000000001 * corners[3*((j+2)%3)+1];
542                  newcorners[3*j+2] =
543                    0.9999999998 * corners[3*j+2] +
544                    0.0000000001 * corners[3*((j+1)%3)+2] +
545                    0.0000000001 * corners[3*((j+2)%3)+2];
546                }
547            }
548
549          /*+
550            +latex+\item
551            +html+ <li>
552            Refine the vertices of the facet using
553            +latex+{\tt NewtonRefine()} (section
554            +latex+\ref{func_NewtonRefine_qhull.c} page
555            +latex+\pageref{func_NewtonRefine_qhull.c}).
556            +html+ <a href="#func-NewtonRefine"><tt>NewtonRefine()</tt></a>.</li>
557            +*/
558          for (j=0; j<3; j++)
559            {
560#ifdef DEBUG
561              printf ("    Refining point at %g, %g function %d\n",
562                      newcorners[3*j], newcorners[3*j+1], eparam[j]);
563#endif
564              NewtonRefine (newcorners+3*j, corners, eparams+eparam[j],
565                            T,P, 3,3, newton_tolerance);
566#ifdef DEBUG
567              printf ("    -> %g, %g\n", newcorners[3*j],
568                      newcorners[3*j+1]);
569#endif
570
571              newcorners[3*j+2] = free_energy
572                (newcorners[3*j],newcorners[3*j+1], T,P, eparams+eparam[j]);
573            }
574
575          /*+
576            +latex+\item
577            +html+ <li>
578            Add new points, leaving out duplicates in same place (closer
579            than vertex_tolerance).
580            +html+ </li>
581            +*/
582
583          // Don't add the point, reallocate the points array instead!
584          if (newcorners[0]>=0. && newcorners[1]>=0. &&
585              newcorners[0]<=1. && newcorners[1]<=1.)
586            {
587#ifdef DEBUG
588              printf ("    Adding new point %g,%g,%g\n",
589                      newcorners[0], newcorners[1], newcorners[2]);
590#endif
591              if (numnewpoints >= newpointsize)
592                newpoints = (energy_point *) realloc
593                  (newpoints, (newpointsize+=100)*sizeof(energy_point));
594              newpoints [numnewpoints].C2 = newcorners[0];
595              newpoints [numnewpoints].C3 = newcorners[1];
596              newpoints [numnewpoints].G  = newcorners[2];
597              newpoints [numnewpoints].efunc = eparam[0];
598              numnewpoints++;
599            }
600
601          if ((newcorners[3]-newcorners[0])*(newcorners[3]-newcorners[0]) +
602              (newcorners[4]-newcorners[1])*(newcorners[4]-newcorners[1]) +
603              (newcorners[5]-newcorners[2])*(newcorners[5]-newcorners[2]) >
604              vertex_tolerance * vertex_tolerance &&
605              newcorners[3]>=0. && newcorners[4]>=0. &&
606              newcorners[3]<=1. && newcorners[4]<=1.)
607            {
608#ifdef DEBUG
609              printf ("    Adding new point %g,%g,%g\n",
610                      newcorners[3], newcorners[4], newcorners[5]);
611#endif
612              if (numnewpoints >= newpointsize)
613                newpoints = (energy_point *) realloc
614                  (newpoints, (newpointsize+=100)*sizeof(energy_point));
615              newpoints [numnewpoints].C2 = newcorners[3];
616              newpoints [numnewpoints].C3 = newcorners[4];
617              newpoints [numnewpoints].G  = newcorners[5];
618              newpoints [numnewpoints].efunc = eparam[1];
619              numnewpoints++;
620            }
621
622          if ((newcorners[6]-newcorners[0])*(newcorners[6]-newcorners[0]) +
623              (newcorners[7]-newcorners[1])*(newcorners[7]-newcorners[1]) +
624              (newcorners[8]-newcorners[2])*(newcorners[8]-newcorners[2]) >
625              vertex_tolerance * vertex_tolerance &&
626              (newcorners[6]-newcorners[3])*(newcorners[6]-newcorners[3]) +
627              (newcorners[7]-newcorners[4])*(newcorners[7]-newcorners[4]) +
628              (newcorners[8]-newcorners[5])*(newcorners[8]-newcorners[5]) >
629              vertex_tolerance * vertex_tolerance &&
630              newcorners[6]>=0. && newcorners[7]>=0. &&
631              newcorners[6]<=1. && newcorners[7]<=1.)
632            {
633#ifdef DEBUG
634              printf ("    Adding new point %g,%g,%g\n",
635                      newcorners[6], newcorners[7], newcorners[8]);
636#endif
637              if (numnewpoints >= newpointsize)
638                newpoints = (energy_point *) realloc
639                  (newpoints, (newpointsize+=100)*sizeof(energy_point));
640              newpoints [numnewpoints].C2 = newcorners[6];
641              newpoints [numnewpoints].C3 = newcorners[7];
642              newpoints [numnewpoints].G  = newcorners[8];
643              newpoints [numnewpoints].efunc = eparam[2];
644              numnewpoints++;
645            }
646        }
647    }
648  /*+
649    +latex+\end{enumerate}
650    +html+ </ol>
651    +*/
652
653#ifdef DEBUG
654  printf ("Adding %d new points to hull\n", numnewpoints);
655#endif
656
657  if (!(*points = (energy_point *) realloc
658        (*points, (*numpoints + numnewpoints) * sizeof (energy_point))))
659    return 1;
660         
661  for (i=0; i<numnewpoints; i++)
662    {
663      pointT newcorners[3] =
664        { newpoints[i].C2, newpoints[i].C3, newpoints[i].G };
665      realT bestdist;
666      boolT isoutside;
667      facetT *facet;
668
669      (*points) [(*numpoints)+i] = newpoints [i];
670    }
671  *numpoints += numnewpoints;
672  free(newpoints);
673
674#ifdef DEBUG
675  printf ("Re-calculating hull using hullCalculate()\n");
676#endif
677
678  /*+ Finally, after generating the new points and adding them to the hull, it
679    re-calculates the convex hull using
680    +latex+{\tt hullCalculate()} (section
681    +latex+\ref{func_hullCalculate_qhull.c} page
682    +latex+\pageref{func_hullCalculate_qhull.c}).
683    +html+ <a href="#func-hullCalculate"><tt>hullCalculate()</tt></a>.</li>
684    +*/
685 
686  return hullCalculate (3, *points,*numpoints, eparams,nparams, T,P, facets,
687                        numfacets);
688}
689
690
691#ifndef MIN
692#define MIN(a,b) (((a) < (b)) ? (a) : (b))
693#endif
694
695#ifndef MAX
696#define MAX(a,b) (((a) > (b)) ? (a) : (b))
697#endif
698
699// For more than three points, qsort() is just easier!
700#define MIN3(a,b,c) (MIN (MIN (a,b), c))
701#define MAX3(a,b,c) (MAX (MAX (a,b), c))
702#define MID3(a,b,c) (((a) < (b)) ? \
703                     (((a) < (c)) ? MIN (b,c) : (a)) : \
704                     (((a) < (c)) ? (a) : MAX (b,c)))
705
706/*++++++++++++++++++++++++++++++++++++++
707  This distills a set of facets to a list of phase boundaries.
708
709  int hullReturnPhaseBoundaries It returns zero or an error code.
710
711  energy_point *points Array of points in the ternary system.
712
713  int n_points Number of points.
714
715  hull_facet *facets Facets from which to create phase boundaries.
716
717  int numfacets Number of facets.
718
719  energy_params *eparams Collection of energy parameter structures.
720
721  double T Temperature.
722
723  double P Pressure.
724
725  phase_boundary **boundaries Pointer to array of phase_boundary objects.  This
726  is changed by realloc() each time the function is called, so it should be
727  NULL or a valid array.
728
729  int *n_bounds Number of phase boundaries returned in boundaries.
730  ++++++++++++++++++++++++++++++++++++++*/
731
732int hullReturnPhaseBoundaries
733(energy_point *points, int n_points, hull_facet *facets, int n_facets,
734 energy_params *eparams, double T, double P,
735 phase_boundary **boundaries, int *n_bounds)
736{
737  int f;
738  *n_bounds = 0;
739
740#ifdef DEBUG
741  printf ("Starting ReturnPhaseBoundaries with %d facets, %d points\n",
742          n_facets, n_points);
743#endif
744
745  for (f=0; f<n_facets; f++)
746    {
747      int v0 = facets[f].vertex[0], v1 = facets[f].vertex[1],
748        v2 = facets[f].vertex[2];
749
750#ifdef DEBUG
751      printf ("Facet %d: type %d, vertices %d ",f, facets [f].type, v0);
752      printf ("(%d), %d (%d), %d(%d)\n",
753              points[v0].efunc, v1,
754              points[v1].efunc, v2, points[v2].efunc);
755      fflush (stdout);
756#endif
757
758      switch (facets [f].type)
759        {
760        case ONE_PHASE:
761          // Totally uninteresting
762          break;
763
764        case THREE_PHASE:
765          {
766            int b=0, bound=-1,
767              phase0 = MIN3 (points [v0].efunc, points [v1].efunc,
768                             points [v2].efunc),
769              phase1 = MID3 (points [v0].efunc, points [v1].efunc,
770                             points [v2].efunc),
771              phase2 = MAX3 (points [v0].efunc, points [v1].efunc,
772                             points [v2].efunc);
773
774            // Check for a boundary with the same three phases
775            for (b=0; b<*n_bounds; b++)
776              if ((*boundaries) [b].n_phases == 3)
777                if ((*boundaries) [b].compos == 3 &&
778                    (*boundaries) [b].type == TIE_SIMPLICES &&
779                    (*boundaries) [b].phases[0] == phase0 &&
780                    (*boundaries) [b].phases[1] == phase1 &&
781                    (*boundaries) [b].phases[2] == phase2)
782                  bound = b;
783
784            if (bound == -1)
785              {
786                // No boundary with these three phases, make a new one
787                bound = *n_bounds;
788#ifdef DEBUG
789                printf ("Making new three-phase boundary %d\n", *n_bounds);
790#endif
791                if (!(*boundaries = realloc
792                      (*boundaries, (*n_bounds+1) * sizeof (phase_boundary))))
793                  return 1;
794
795                (*boundaries) [bound].compos = 3;
796                (*boundaries) [bound].n_phases = 3;
797                if (!((*boundaries) [bound].phases = malloc (3*sizeof (int))))
798                  return 1;
799                (*boundaries) [bound].phases [0] = phase0;
800                (*boundaries) [bound].phases [1] = phase1;
801                (*boundaries) [bound].phases [2] = phase2;
802                (*boundaries) [bound].type = TIE_SIMPLICES;
803                (*boundaries) [bound].n_edges = 1;
804                if (!((*boundaries) [bound].edges = malloc (3*sizeof (int))))
805                  return 1;
806                (*boundaries) [bound].edges [0] = MIN3 (v0, v1, v2);
807                (*boundaries) [bound].edges [1] = MID3 (v0, v1, v2);
808                (*boundaries) [bound].edges [2] = MAX3 (v0, v1, v2);
809                (*n_bounds) ++;
810#ifdef DEBUG
811                printf ("  Corners for bound %d edge 0: %d, %d, %d\n", bound,
812                        (*boundaries) [bound].edges [0],
813                        (*boundaries) [bound].edges [1],
814                        (*boundaries) [bound].edges [2]);
815#endif
816              }
817            else
818              {
819                // Boundary found, add to it; it's likely to be rare enough
820                // that we need not worry about the realloc overhead
821                int e = (*boundaries) [bound].n_edges;
822#ifdef DEBUG
823                printf ("Ternary boundary %d adding edge %d\n",bound,e);
824#endif
825                // All of these reallocs are pretty expensive...
826                // Later use compos to store size of array, sort out later
827                if (!((*boundaries) [bound].edges = realloc
828                      ((*boundaries) [bound].edges, (3*e+3)*sizeof (int))))
829                  return 1;
830                (*boundaries) [bound].edges [3*e]   = MIN3 (v0, v1, v2);
831                (*boundaries) [bound].edges [3*e+1] = MID3 (v0, v1, v2);
832                (*boundaries) [bound].edges [3*e+2] = MAX3 (v0, v1, v2);
833                (*boundaries) [bound].n_edges += 1;
834#ifdef DEBUG
835                printf ("  Corners for bound %d edge %d: %d, %d, %d\n", bound,e,
836                        (*boundaries) [bound].edges [3*e],
837                        (*boundaries) [bound].edges [3*e+1],
838                        (*boundaries) [bound].edges [3*e+2]);
839#endif
840              }
841
842            // Need to handle the corner "boundaries" later...
843
844            break;
845          }
846
847        case TWO_PHASE:
848          {
849            int b=0, bound=-1, p0, p1, p2, nphases=2, e=0,
850              phase0 = MIN3 (points [v0].efunc, points [v1].efunc,
851                             points [v2].efunc),
852              phase1 = MAX3 (points [v0].efunc, points [v1].efunc,
853                             points [v2].efunc);
854
855            // Set p0 and p1 to index first and second vertices in same phase,
856            // p2 to lone vertex in the other phase.
857            if (points [v0].efunc == points [v1].efunc)
858              {
859                p0 = MIN (v0, v1);
860                p1 = MAX (v0, v1);
861                p2 = v2;
862
863                // If all three are on the same phase, this is a miscibility
864                // gap facet, set nphases to 1 and figure out which vertex is
865                // across from the other two
866                if (points [v0].efunc == points [v2].efunc)
867                  {
868                    double C02 = points [v0].C2, C03 = points [v0].C3,
869                      C12 = points [v1].C2, C13 = points [v1].C3,
870                      C22 = points [v2].C2, C23 = points [v2].C3,
871                      G0 = points [v0].G, G1 = points [v1].G,
872                      G2 = points [v2].G;
873
874#ifdef DEBUG
875                    printf ("Misc gap: ");
876#endif
877                    nphases=1;
878
879                    if (free_energy ((C02+C12)/2,(C03+C13)/2,T,P,eparams+phase0) <
880                        (G0+G1)/2)
881                      {
882#ifdef DEBUG
883                        printf ("v2 alone, Gav=%g, Gmid=%g\n",
884                                (G0+G1)/2, free_energy ((C02+C12)/2,(C03+C13)/2,T,P,eparams+phase0));
885#endif
886                        p0 = MIN (v0, v1);
887                        p1 = MAX (v0, v1);
888                        p2 = v2;
889                      }
890                    else if (free_energy ((C02+C22)/2,(C03+C23)/2,T,P,eparams+phase0) <
891                             (G0+G2)/2)
892                      {
893#ifdef DEBUG
894                        printf ("v1 alone, Gav=%g, Gmid=%g\n",
895                                (G0+G2)/2, free_energy ((C02+C22)/2,(C03+C23)/2,T,P,eparams+phase0));
896#endif
897                        p0 = MIN (v0, v2);
898                        p1 = MAX (v0, v2);
899                        p2 = v1;
900                      }
901                    else
902                      {
903#ifdef DEBUG
904                        printf ("v0 alone, Gav=%g, Gmid=%g\n",
905                                (G1+G2)/2, free_energy ((C22+C12)/2,(C23+C13)/2,T,P,eparams+phase0));
906#endif
907                        p0 = MIN (v1, v2);
908                        p1 = MAX (v1, v2);
909                        p2 = v0;
910                      }
911                  }
912              }
913            else if (points [v0].efunc == points [v2].efunc)
914              {
915                p0 = MIN (v0, v2);
916                p1 = MAX (v0, v2);
917                p2 = v1;
918              }
919            else
920              {
921                p0 = MIN (v1, v2);
922                p1 = MAX (v1, v2);
923                p2 = v0;
924              }
925
926            // Check for a boundary with tie lines between these two phases
927            for (b=0; b<*n_bounds; b++)
928              if ((*boundaries) [b].n_phases == nphases &&
929                  (*boundaries) [b].type == TIE_SIMPLICES &&
930                  (*boundaries) [b].phases[0] == phase0 &&
931                  (*boundaries) [b].phases[nphases-1] == phase1)
932                bound = b;
933
934            if (bound == -1)
935              {
936                bound = *n_bounds;
937                // No boundary with these two phases, make a new one
938#ifdef DEBUG
939                printf ("Making new tie-line boundary %d\n", *n_bounds);
940#endif
941                if (!(*boundaries = realloc
942                      (*boundaries, (*n_bounds+1) * sizeof (phase_boundary))))
943                  return 1;
944
945                (*boundaries) [bound].compos = 2;
946                (*boundaries) [bound].n_phases = nphases;
947                if (!((*boundaries) [bound].phases =
948                      malloc (nphases*sizeof (int))))
949                  return 1;
950                (*boundaries) [bound].phases [0] = phase0;
951                (*boundaries) [bound].phases [nphases-1] = phase1;
952                (*boundaries) [bound].n_edges = 0;
953                (*boundaries) [bound].edges = NULL;
954                (*boundaries) [bound].type = TIE_SIMPLICES;
955                (*n_bounds) ++;
956                e=0;
957              }
958            else
959              e = (*boundaries) [bound].n_edges;
960
961            // All of these reallocs are pretty expensive...
962            // Later use compos to store size of array, sort out later
963            if (!((*boundaries) [bound].edges = realloc
964                  ((*boundaries) [bound].edges, (2*e+4) * sizeof (int))))
965              return 1;
966            (*boundaries) [bound].n_edges += 2;
967
968            (*boundaries) [bound].edges [2*e]   = MIN (p0, p2);
969            (*boundaries) [bound].edges [2*e+1] = MAX (p0, p2);
970            (*boundaries) [bound].edges [2*e+2] = MIN (p1, p2);
971            (*boundaries) [bound].edges [2*e+3] = MAX (p1, p2);
972#ifdef DEBUG
973            printf ("  Corners for bound %d edges %d,%d: %d,%d, %d,%d\n",
974                    bound, e, e+1, (*boundaries) [bound].edges [2*e],
975                    (*boundaries) [bound].edges [2*e+1],
976                    (*boundaries) [bound].edges [2*e+2],
977                    (*boundaries) [bound].edges [2*e+3]);
978#endif
979
980            // Check for a boundary with an edge on the "majority" phase
981            for (b=0, bound=-1; b<*n_bounds; b++)
982              if ((*boundaries) [b].n_phases == nphases &&
983                  (*boundaries) [b].type == ONE_PHASE_EDGE &&
984                  (*boundaries) [b].phases[0] == points[p0].efunc &&
985                  (*boundaries) [b].phases[nphases-1] == points[p2].efunc)
986                bound = b;
987
988            if (bound == -1)
989              {
990                // No boundary with these two phases, make a new one
991                bound = *n_bounds;
992#ifdef DEBUG
993                printf ("Making new phase edge boundary %d\n", *n_bounds);
994#endif
995                if (!(*boundaries = realloc
996                      (*boundaries, (bound+1) * sizeof (phase_boundary))))
997                  return 1;
998
999                (*boundaries) [bound].compos = 2;
1000                (*boundaries) [bound].n_phases = nphases;
1001                if (!((*boundaries) [bound].phases =
1002                      malloc (nphases*sizeof (int))))
1003                  return 1;
1004                (*boundaries) [bound].phases [0] = points[p0].efunc;
1005                (*boundaries) [bound].phases [nphases-1] = points[p2].efunc;
1006                (*boundaries) [bound].n_edges = 0;
1007                (*boundaries) [bound].edges = NULL;
1008                (*boundaries) [bound].type = ONE_PHASE_EDGE;
1009                (*n_bounds) ++;
1010                e=0;
1011              }
1012            else
1013              e = (*boundaries) [bound].n_edges;
1014
1015            // All of these reallocs are pretty expensive...
1016            if (!((*boundaries) [bound].edges = realloc
1017                  ((*boundaries) [bound].edges, (2*e+2)*sizeof (int))))
1018              return 1;
1019            (*boundaries) [bound].edges [2*e]   = MIN (p0, p1);
1020            (*boundaries) [bound].edges [2*e+1] = MAX (p0, p1);
1021            (*boundaries) [bound].n_edges += 1;
1022#ifdef DEBUG
1023            printf ("  Corners for bound %d edge %d: %d,%d\n",
1024                    bound, e, (*boundaries) [bound].edges [2*e],
1025                    (*boundaries) [bound].edges [2*e+1]);
1026#endif
1027
1028            break;
1029          }
1030
1031        case CRITICAL_POINT:
1032          {
1033#ifdef DEBUG
1034            printf ("Critical point case\n");
1035#endif
1036            int b=0, bound=-1, p0, p1, p2, e=0, phase = points [v0].efunc;
1037            double C02 = points [v0].C2, C03 = points [v0].C3,
1038              C12 = points [v1].C2, C13 = points [v1].C3,
1039              C22 = points [v2].C2, C23 = points [v2].C3,
1040              G0 = points [v0].G, G1 = points [v1].G,
1041              G2 = points [v2].G;
1042
1043            if (free_energy ((C02+C12)/2,(C03+C13)/2,T,P,eparams+phase) >
1044                (G0+G1)/2)
1045              {
1046#ifdef DEBUG
1047                printf ("v2 alone, Gav=%g, Gmid=%g\n",
1048                        (G0+G1)/2, free_energy ((C02+C12)/2,(C03+C13)/2,T,P,eparams+phase));
1049#endif
1050                p0 = MIN (v0, v1);
1051                p1 = MAX (v0, v1);
1052                p2 = v2;
1053              }
1054            else if (free_energy ((C02+C22)/2,(C03+C23)/2,T,P,eparams+phase) >
1055                     (G0+G2)/2)
1056              {
1057#ifdef DEBUG
1058                printf ("v1 alone, Gav=%g, Gmid=%g\n",
1059                        (G0+G2)/2, free_energy ((C02+C22)/2,(C03+C23)/2,T,P,eparams+phase));
1060#endif
1061                p0 = MIN (v0, v2);
1062                p1 = MAX (v0, v2);
1063                p2 = v1;
1064              }
1065            else
1066              {
1067#ifdef DEBUG
1068                printf ("v0 alone, Gav=%g, Gmid=%g\n",
1069                        (G1+G2)/2, free_energy ((C22+C12)/2,(C23+C13)/2,T,P,eparams+phase));
1070#endif
1071                p0 = MIN (v1, v2);
1072                p1 = MAX (v1, v2);
1073                p2 = v0;
1074              }
1075
1076            // Check for a boundary with tie lines across this miscibility gap
1077            for (b=0; b<*n_bounds; b++)
1078              if ((*boundaries) [b].n_phases == 1 &&
1079                  (*boundaries) [b].type == TIE_SIMPLICES &&
1080                  (*boundaries) [b].phases[0] == phase)
1081                bound = b;
1082
1083            if (bound == -1)
1084              {
1085                bound = *n_bounds;
1086                // No tie-line boundary in this phase, make a new one
1087#ifdef DEBUG
1088                printf ("Making new tie-line boundary %d\n", *n_bounds);
1089#endif
1090                if (!(*boundaries = realloc
1091                      (*boundaries, (*n_bounds+1) * sizeof (phase_boundary))))
1092                  return 1;
1093
1094                (*boundaries) [bound].compos = 2;
1095                (*boundaries) [bound].n_phases = 1;
1096                if (!((*boundaries) [bound].phases = malloc (sizeof (int))))
1097                  return 1;
1098                (*boundaries) [bound].phases [0] = phase;
1099                (*boundaries) [bound].n_edges = 0;
1100                (*boundaries) [bound].edges = NULL;
1101                (*boundaries) [bound].type = TIE_SIMPLICES;
1102                (*n_bounds) ++;
1103                e=0;
1104              }
1105            else
1106              e = (*boundaries) [bound].n_edges;
1107
1108            // All of these reallocs are pretty expensive...
1109            // Later use compos to store size of array, sort out later
1110            if (!((*boundaries) [bound].edges = realloc
1111                  ((*boundaries) [bound].edges, (2*e+2) * sizeof (int))))
1112              return 1;
1113            (*boundaries) [bound].n_edges += 1;
1114
1115            (*boundaries) [bound].edges [2*e]   = MIN (p0, p1);
1116            (*boundaries) [bound].edges [2*e+1] = MAX (p0, p1);
1117#ifdef DEBUG
1118            printf ("  Corners for bound %d edges %d,%d: %d,%d\n",
1119                    bound, e, e+1, (*boundaries) [bound].edges [2*e],
1120                    (*boundaries) [bound].edges [2*e+1]);
1121#endif
1122
1123            // Check for a boundary with an edge on this miscibility gap
1124            for (b=0, bound=-1; b<*n_bounds; b++)
1125              if ((*boundaries) [b].n_phases == 1 &&
1126                  (*boundaries) [b].type == ONE_PHASE_EDGE &&
1127                  (*boundaries) [b].phases[0] == phase)
1128                bound = b;
1129
1130            if (bound == -1)
1131              {
1132                // No edge boundary with this miscibility gap, make a new one
1133                bound = *n_bounds;
1134#ifdef DEBUG
1135                printf ("Making new phase edge boundary %d\n", *n_bounds);
1136#endif
1137                if (!(*boundaries = realloc
1138                      (*boundaries, (bound+1) * sizeof (phase_boundary))))
1139                  return 1;
1140
1141                (*boundaries) [bound].compos = 2;
1142                (*boundaries) [bound].n_phases = 1;
1143                if (!((*boundaries) [bound].phases = malloc (sizeof (int))))
1144                  return 1;
1145                (*boundaries) [bound].phases [0] = phase;
1146                (*boundaries) [bound].n_edges = 0;
1147                (*boundaries) [bound].edges = NULL;
1148                (*boundaries) [bound].type = ONE_PHASE_EDGE;
1149                (*n_bounds) ++;
1150                e=0;
1151              }
1152            else
1153              e = (*boundaries) [bound].n_edges;
1154
1155            // All of these reallocs are pretty expensive...
1156            if (!((*boundaries) [bound].edges = realloc
1157                  ((*boundaries) [bound].edges, (2*e+4)*sizeof (int))))
1158              return 1;
1159            (*boundaries) [bound].edges [2*e]   = MIN (p0, p2);
1160            (*boundaries) [bound].edges [2*e+1] = MAX (p0, p2);
1161            (*boundaries) [bound].edges [2*e+2] = MIN (p1, p2);
1162            (*boundaries) [bound].edges [2*e+3] = MAX (p1, p2);
1163            (*boundaries) [bound].n_edges += 2;
1164#ifdef DEBUG
1165            printf ("  Corners for bound %d edge %d: %d,%d, %d,%d\n",
1166                    bound, e, (*boundaries) [bound].edges [2*e],
1167                    (*boundaries) [bound].edges [2*e+1],
1168                    (*boundaries) [bound].edges [2*e+2],
1169                    (*boundaries) [bound].edges [2*e+3]);
1170#endif
1171
1172            break;
1173          }
1174
1175        default:
1176          {
1177            printf ("Default case: type %s, phases %d, %d, %d\n",
1178                    (facets [f].type == ONE_PHASE) ? "1-phase" :
1179                    ((facets [f].type == CRITICAL_POINT) ? "critical" :
1180                     ((facets [f].type == TWO_PHASE) ? "2-phase" :
1181                      ((facets [f].type == THREE_PHASE) ? "3-phase" :
1182                       "unknown"))), points [facets[f].vertex[0]].efunc,
1183                    points [facets[f].vertex[1]].efunc,
1184                    points [facets[f].vertex[2]].efunc);
1185            printf ("  Coords: %d %g,%g, %d %g,%g, %d %g,%g\n",
1186                    facets[f].vertex[0], points [facets[f].vertex[0]].C2,
1187                    points [facets[f].vertex[0]].C3,
1188                    facets[f].vertex[1], points [facets[f].vertex[1]].C2,
1189                    points [facets[f].vertex[1]].C3,
1190                    facets[f].vertex[2], points [facets[f].vertex[2]].C2,
1191                    points [facets[f].vertex[2]].C3);
1192          }
1193        }
1194    }
1195
1196  return 0;
1197}
1198
1199
1200/*++++++++++++++++++++++++++++++++++++++
1201  This is what it sounds like.
1202
1203  char *hullQHullVersion It returns the QHull version string.
1204  ++++++++++++++++++++++++++++++++++++++*/
1205
1206char *hullQHullVersion ()
1207{
1208  return qh_version;
1209}
Note: See TracBrowser for help on using the browser.