| | 679 | #ifndef MIN |
| | 680 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) |
| | 681 | #endif |
| | 682 | |
| | 683 | #ifndef MAX |
| | 684 | #define MAX(a,b) (((a) > (b)) ? (a) : (b)) |
| | 685 | #endif |
| | 686 | |
| | 687 | // For more than three points, qsort() is just easier! |
| | 688 | #define MIN3(a,b,c) (MIN (MIN (a,b), c)) |
| | 689 | #define MAX3(a,b,c) (MAX (MAX (a,b), c)) |
| | 690 | #define MID3(a,b,c) (((a) < (b)) ? \ |
| | 691 | (((a) < (c)) ? MIN (b,c) : (a)) : \ |
| | 692 | (((a) < (c)) ? (a) : MAX (b,c))) |
| | 693 | |
| | 694 | /*++++++++++++++++++++++++++++++++++++++ |
| | 695 | This distills a set of facets to a list of phase boundaries. |
| | 696 | |
| | 697 | int hullReturnPhaseBoundaries It returns zero or an error code. |
| | 698 | |
| | 699 | ternary_point *points Array of points in the ternary system. |
| | 700 | |
| | 701 | int n_points Number of points. |
| | 702 | |
| | 703 | hull_facet *facets Facets from which to create phase boundaries. |
| | 704 | |
| | 705 | int numfacets Number of facets. |
| | 706 | |
| | 707 | phase_boundary **boundaries Pointer to array of phase_boundary objects. This |
| | 708 | is changed by realloc() each time the function is called, so it should be |
| | 709 | NULL or a valid array. |
| | 710 | |
| | 711 | int *n_bounds Number of phase boundaries returned in boundaries. |
| | 712 | ++++++++++++++++++++++++++++++++++++++*/ |
| | 713 | |
| | 714 | int hullReturnPhaseBoundaries |
| | 715 | (ternary_point *points, int n_points, hull_facet *facets, int n_facets, |
| | 716 | phase_boundary **boundaries, int *n_bounds) |
| | 717 | { |
| | 718 | int f; |
| | 719 | *n_bounds = 0; |
| | 720 | |
| | 721 | printf ("Starting ReturnPhaseBoundaries with %d facets, %d points\n", |
| | 722 | n_facets, n_points); |
| | 723 | |
| | 724 | printf ("Last facet %d vertices %d, %d, %d\n", n_facets-1, |
| | 725 | facets[n_facets-1].vertex[0], facets[n_facets-1].vertex[1], |
| | 726 | facets[n_facets-1].vertex[2]); |
| | 727 | |
| | 728 | for (f=0; f<n_facets; f++) |
| | 729 | { |
| | 730 | int v0 = facets[f].vertex[0], v1 = facets[f].vertex[1], |
| | 731 | v2 = facets[f].vertex[2]; |
| | 732 | |
| | 733 | printf ("Last facet %d vertices %d, %d, %d\n", n_facets-1, |
| | 734 | facets[n_facets-1].vertex[0], facets[n_facets-1].vertex[1], |
| | 735 | facets[n_facets-1].vertex[2]); |
| | 736 | |
| | 737 | printf ("Facet %d: type %d, vertices %d ",f, facets [f].type, v0); |
| | 738 | fflush (stdout); |
| | 739 | printf ("(%d), %d (%d), %d(%d)\n", |
| | 740 | points[v0].efunc, v1, |
| | 741 | points[v1].efunc, v2, points[v2].efunc); |
| | 742 | fflush (stdout); |
| | 743 | |
| | 744 | switch (facets [f].type) |
| | 745 | { |
| | 746 | case ONE_PHASE: |
| | 747 | { |
| | 748 | printf ("Type 0 facet\n"); fflush (stdout); |
| | 749 | // Totally uninteresting |
| | 750 | break; |
| | 751 | } |
| | 752 | |
| | 753 | case THREE_PHASE: |
| | 754 | { |
| | 755 | int b=0, bound=-1, |
| | 756 | phase0 = MIN3 (points [v0].efunc, points [v1].efunc, |
| | 757 | points [v2].efunc), |
| | 758 | phase1 = MID3 (points [v0].efunc, points [v1].efunc, |
| | 759 | points [v2].efunc), |
| | 760 | phase2 = MAX3 (points [v0].efunc, points [v1].efunc, |
| | 761 | points [v2].efunc); |
| | 762 | |
| | 763 | // Check for a boundary with the same three phases |
| | 764 | for (b=0; b<*n_bounds; b++) |
| | 765 | if ((*boundaries) [b].n_phases == 3) |
| | 766 | if ((*boundaries) [b].compos = 3 && |
| | 767 | (*boundaries) [b].flags == TIE_SIMPLICES && |
| | 768 | (*boundaries) [b].phases[0] == phase0 && |
| | 769 | (*boundaries) [b].phases[1] == phase1 && |
| | 770 | (*boundaries) [b].phases[2] == phase2) |
| | 771 | bound = b; |
| | 772 | |
| | 773 | if (bound == -1) |
| | 774 | { |
| | 775 | // No boundary with these three phases, make a new one |
| | 776 | bound = *n_bounds; |
| | 777 | printf ("Making new three-phase boundary %d\n", *n_bounds); |
| | 778 | if (!(*boundaries = realloc |
| | 779 | (*boundaries, (*n_bounds+1) * sizeof (phase_boundary)))) |
| | 780 | return 1; |
| | 781 | |
| | 782 | (*boundaries) [bound].compos = 3; |
| | 783 | (*boundaries) [bound].n_phases = 3; |
| | 784 | if (!((*boundaries) [bound].phases = malloc (3*sizeof (int)))) |
| | 785 | return 1; |
| | 786 | (*boundaries) [bound].phases [0] = phase0; |
| | 787 | (*boundaries) [bound].phases [1] = phase1; |
| | 788 | (*boundaries) [bound].phases [2] = phase2; |
| | 789 | (*boundaries) [bound].flags = TIE_SIMPLICES; |
| | 790 | (*boundaries) [bound].n_edges = 1; |
| | 791 | if (!((*boundaries) [bound].edges = malloc (3*sizeof (int)))) |
| | 792 | return 1; |
| | 793 | (*boundaries) [bound].edges [0] = MIN3 (v0, v1, v2); |
| | 794 | (*boundaries) [bound].edges [1] = MID3 (v0, v1, v2); |
| | 795 | (*boundaries) [bound].edges [2] = MAX3 (v0, v1, v2); |
| | 796 | (*n_bounds) ++; |
| | 797 | } |
| | 798 | else |
| | 799 | { |
| | 800 | // Boundary found, add to it; it's likely to be rare enough |
| | 801 | // that we need not worry about the realloc overhead |
| | 802 | int e = (*boundaries) [bound].n_edges; |
| | 803 | printf ("Ternary boundary %d adding edge %d\n",bound,e); |
| | 804 | // All of these reallocs are pretty expensive... |
| | 805 | // Later use compos to store size of array, sort out later |
| | 806 | if (!((*boundaries) [bound].edges = realloc |
| | 807 | ((*boundaries) [bound].edges, (3*e+3)*sizeof (int)))) |
| | 808 | return 1; |
| | 809 | (*boundaries) [bound].edges [3*e] = MIN3 (v0, v1, v2); |
| | 810 | (*boundaries) [bound].edges [3*e+1] = MID3 (v0, v1, v2); |
| | 811 | (*boundaries) [bound].edges [3*e+2] = MAX3 (v0, v1, v2); |
| | 812 | (*boundaries) [bound].n_edges += 1; |
| | 813 | } |
| | 814 | |
| | 815 | // Need to handle the corner "boundaries" later... |
| | 816 | |
| | 817 | break; |
| | 818 | } |
| | 819 | |
| | 820 | case TWO_PHASE: |
| | 821 | { |
| | 822 | int b=0, bound=-1, p0, p1, p2, |
| | 823 | phase0 = MIN3 (points [v0].efunc, points [v1].efunc, |
| | 824 | points [v2].efunc), |
| | 825 | phase1 = MAX3 (points [v0].efunc, points [v1].efunc, |
| | 826 | points [v2].efunc); |
| | 827 | |
| | 828 | // Set p0 and p1 to index first and second vertices in same phase, |
| | 829 | // p2 to lone vertex in the other phase. |
| | 830 | if (points [v0].efunc == points [v1].efunc) |
| | 831 | { |
| | 832 | p0 = MIN (v0, v1); |
| | 833 | p1 = MAX (v0, v1); |
| | 834 | p2 = v2; |
| | 835 | // If all three are on the same phase, need to check midpoint |
| | 836 | // energies to determine which are one- and two-phase segments. |
| | 837 | // Punt for the default case later! |
| | 838 | if (points [v0].efunc == points [v2].efunc) |
| | 839 | { printf ("Miscibility gap!\n"); break; } |
| | 840 | } |
| | 841 | else if (points [v0].efunc == points [v2].efunc) |
| | 842 | { |
| | 843 | p0 = MIN (v0, v2); |
| | 844 | p1 = MAX (v0, v2); |
| | 845 | p2 = v1; |
| | 846 | } |
| | 847 | else |
| | 848 | { |
| | 849 | p0 = MIN (v1, v2); |
| | 850 | p1 = MAX (v1, v2); |
| | 851 | p2 = v0; |
| | 852 | } |
| | 853 | |
| | 854 | // Check for a boundary with tie lines between these two phases |
| | 855 | for (b=0; b<*n_bounds; b++) |
| | 856 | if ((*boundaries) [b].n_phases == 2 && |
| | 857 | (*boundaries) [b].flags == TIE_SIMPLICES && |
| | 858 | (*boundaries) [b].phases[0] == phase0 && |
| | 859 | (*boundaries) [b].phases[1] == phase1) |
| | 860 | bound = b; |
| | 861 | |
| | 862 | if (bound == -1) |
| | 863 | { |
| | 864 | bound = *n_bounds; |
| | 865 | // No boundary with these two phases, make a new one |
| | 866 | printf ("Making new tie-line boundary %d\n", *n_bounds); |
| | 867 | if (!(*boundaries = realloc |
| | 868 | (*boundaries, (*n_bounds+1) * sizeof (phase_boundary)))) |
| | 869 | return 1; |
| | 870 | |
| | 871 | (*boundaries) [bound].compos = 2; |
| | 872 | (*boundaries) [bound].n_phases = 2; |
| | 873 | if (!((*boundaries) [bound].phases = |
| | 874 | malloc (2*sizeof (int)))) |
| | 875 | return 1; |
| | 876 | (*boundaries) [bound].phases [0] = phase0; |
| | 877 | (*boundaries) [bound].phases [1] = phase1; |
| | 878 | (*boundaries) [bound].n_edges = 2; |
| | 879 | if (!((*boundaries) [bound].edges = |
| | 880 | malloc (4*sizeof (int)))) |
| | 881 | return 1; |
| | 882 | (*boundaries) [bound].edges [0] = MIN (p0, p2); |
| | 883 | (*boundaries) [bound].edges [1] = MAX (p0, p2); |
| | 884 | (*boundaries) [bound].edges [2] = MIN (p1, p2); |
| | 885 | (*boundaries) [bound].edges [3] = MAX (p1, p2); |
| | 886 | (*boundaries) [bound].flags = TIE_SIMPLICES; |
| | 887 | (*n_bounds) ++; |
| | 888 | } |
| | 889 | else |
| | 890 | { |
| | 891 | int e = (*boundaries) [bound].n_edges; |
| | 892 | printf ("Tie-line boundary %d adding edges %d,%d\n",bound,e,e+1); |
| | 893 | // All of these reallocs are pretty expensive... |
| | 894 | // Later use compos to store size of array, sort out later |
| | 895 | if (!((*boundaries) [bound].edges = realloc |
| | 896 | ((*boundaries) [bound].edges, (2*e+4)*sizeof (int)))) |
| | 897 | return 1; |
| | 898 | (*boundaries) [bound].edges [2*e] = MIN (p0, p2); |
| | 899 | (*boundaries) [bound].edges [2*e+1] = MAX (p0, p2); |
| | 900 | (*boundaries) [bound].edges [2*e+2] = MIN (p1, p2); |
| | 901 | (*boundaries) [bound].edges [2*e+3] = MAX (p1, p2); |
| | 902 | (*boundaries) [bound].n_edges += 2; |
| | 903 | } |
| | 904 | |
| | 905 | // Check for a boundary with an edge on the "majority" phase |
| | 906 | for (b=0, bound=-1; b<*n_bounds; b++) |
| | 907 | if ((*boundaries) [b].n_phases == 2 && |
| | 908 | (*boundaries) [b].flags == ONE_PHASE_EDGE && |
| | 909 | (*boundaries) [b].phases[0] == points[p0].efunc && |
| | 910 | (*boundaries) [b].phases[1] == points[p2].efunc) |
| | 911 | bound = b; |
| | 912 | |
| | 913 | if (bound == -1) |
| | 914 | { |
| | 915 | // No boundary with these two phases, make a new one |
| | 916 | bound = *n_bounds; |
| | 917 | printf ("Making new phase edge boundary %d\n", *n_bounds); |
| | 918 | if (!(*boundaries = realloc |
| | 919 | (*boundaries, (bound+1) * sizeof (phase_boundary)))) |
| | 920 | return 1; |
| | 921 | |
| | 922 | (*boundaries) [bound].compos = 2; |
| | 923 | (*boundaries) [bound].n_phases = 2; |
| | 924 | if (!((*boundaries) [bound].phases = |
| | 925 | malloc (2*sizeof (int)))) |
| | 926 | return 1; |
| | 927 | (*boundaries) [bound].phases [0] = points[p0].efunc; |
| | 928 | (*boundaries) [bound].phases [1] = points[p2].efunc; |
| | 929 | (*boundaries) [bound].n_edges = 1; |
| | 930 | if (!((*boundaries) [bound].edges = |
| | 931 | malloc (2*sizeof (int)))) |
| | 932 | return 1; |
| | 933 | (*boundaries) [bound].edges [0] = MIN (p0, p1); |
| | 934 | (*boundaries) [bound].edges [1] = MAX (p0, p1); |
| | 935 | (*boundaries) [bound].flags = ONE_PHASE_EDGE; |
| | 936 | (*n_bounds) ++; |
| | 937 | } |
| | 938 | else |
| | 939 | { |
| | 940 | int e = (*boundaries) [bound].n_edges; |
| | 941 | printf ("Phase edge boundary %d adding edge %d\n",bound,e); |
| | 942 | // All of these reallocs are pretty expensive... |
| | 943 | if (!((*boundaries) [bound].edges = realloc |
| | 944 | ((*boundaries) [bound].edges, (2*e+2)*sizeof (int)))) |
| | 945 | return 1; |
| | 946 | (*boundaries) [bound].edges [2*e] = MIN (p0, p1); |
| | 947 | (*boundaries) [bound].edges [2*e+1] = MAX (p0, p1); |
| | 948 | (*boundaries) [bound].n_edges += 1; |
| | 949 | } |
| | 950 | |
| | 951 | break; |
| | 952 | } |
| | 953 | |
| | 954 | default: |
| | 955 | { |
| | 956 | printf ("Default case: type %s, phases %d, %d, %d\n", |
| | 957 | (facets [f].type == ONE_PHASE) ? "1-phase" : |
| | 958 | ((facets [f].type == CRITICAL_POINT) ? "critical" : |
| | 959 | ((facets [f].type == TWO_PHASE) ? "2-phase" : |
| | 960 | ((facets [f].type == THREE_PHASE) ? "3-phase" : |
| | 961 | "unknown"))), points [facets[f].vertex[0]].efunc, |
| | 962 | points [facets[f].vertex[1]].efunc, |
| | 963 | points [facets[f].vertex[2]].efunc); |
| | 964 | } |
| | 965 | } |
| | 966 | } |
| | 967 | |
| | 968 | printf ("Done!\n"); |
| | 969 | return 0; |
| | 970 | } |
| | 971 | |
| | 972 | |