/* Copyright (C) 1993 by David H. Gay and Andrew L. Rohl dgay@ricx.royal-institution.ac.uk andrew@ricx.royal-institution.ac.uk Modified 2003 by Sean David Fleming sean@ivec.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The GNU GPL can also be found at http://www.gnu.org */ #include #include #include #include #include "gdis.h" #include "coords.h" #include "model.h" #include "matrix.h" #include "numeric.h" #include "vector.h" #include "space.h" #include "surface.h" #include "zone.h" #include "interface.h" typedef gchar boolean; /**********************/ /* debugging routines */ /**********************/ void surface_print_planes(GSList *planes) { GSList *list; struct plane_pak *plane; for (list=planes ; list ; list=g_slist_next(list)) { plane = list->data; printf("[%2d %2d %2d]", plane->index[0], plane->index[1], plane->index[2]); printf(" : %f , %f", plane->esurf[0], plane->eatt[0]); printf("\n"); } } /*********************/ /* duplicate a shift */ /*********************/ gpointer shift_dup(struct shift_pak *src) { struct shift_pak *dest; dest = g_malloc(sizeof(struct shift_pak)); memcpy(dest, src, sizeof(struct shift_pak)); return(dest); } /********************************************/ /* allocate for new shift & init for safety */ /********************************************/ gpointer shift_new(gdouble shift) { struct shift_pak *sdata; /* alloc */ sdata = g_malloc(sizeof(struct shift_pak)); sdata->locked = FALSE; sdata->shift = shift; sdata->dipole_computed = FALSE; sdata->dipole = 99.9; sdata->region[0] = 1; sdata->region[1] = 1; sdata->esurf[0] = 0.0; sdata->esurf[1] = 0.0; sdata->eatt[0] = 0.0; sdata->eatt[1] = 0.0; sdata->bbpa = 0.0; sdata->gnorm = -1.0; sdata->procfile = NULL; return(sdata); } /*********************/ /* duplicate a plane */ /*********************/ gpointer plane_dup(struct plane_pak *src) { GSList *list; struct plane_pak *dest; struct shift_pak *shift; dest = g_malloc(sizeof(struct plane_pak)); memcpy(dest, src, sizeof(struct plane_pak)); dest->shifts = NULL; for (list=src->shifts ; list ; list=g_slist_next(list)) { shift = shift_dup(list->data); dest->shifts = g_slist_prepend(dest->shifts, shift); } return(dest); } /****************************************/ /* determines surface and depth vectors */ /****************************************/ /* TODO - call this to eliminate redundancy in generate_surface() */ #define DEBUG_GENSURF 0 void plane_compute_vectors(struct plane_pak *plane, struct model_pak *src) { vector normal; vector lattice_vector[3]; /* lattice vectors */ vector t_mat[3]; /* transformation matrix */ vector work_lat[3]; /* transformed lattice vectors */ vector rec_work_lat[3]; /* reciprocal transformed lattice vectors */ vector s[3]; /* space we are trying to fill */ vector tempvec; vector *sv; vector a, *v_a=NULL, *v_b=NULL; boolean s2_found = FALSE; gint c, i, j, h, k, l, flag; gint GCD(gint, gint); gint vector_compare(vector *, vector *); gdouble inv_denom; gdouble depth, depth_min; gdouble gcd, cd, x; gdouble tmat[9], norm[3], va[3]; gdouble wlat[9]; gdouble sign, vec[3], vec2[3], dn[3]; GSList *list1, *list2, *sv_list1, *sv_list2; g_assert(src != NULL); /* find surface normal from the miller index and lattice vectors*/ V_ZERO(normal); /* setup */ h = plane->m[0]; k = plane->m[1]; l = plane->m[2]; /* acquire lattice vectors */ V_X(lattice_vector[0]) = src->latmat[0]; V_Y(lattice_vector[0]) = src->latmat[3]; V_Z(lattice_vector[0]) = src->latmat[6]; V_X(lattice_vector[1]) = src->latmat[1]; V_Y(lattice_vector[1]) = src->latmat[4]; V_Z(lattice_vector[1]) = src->latmat[7]; V_X(lattice_vector[2]) = src->latmat[2]; V_Y(lattice_vector[2]) = src->latmat[5]; V_Z(lattice_vector[2]) = src->latmat[8]; V_CROSS(tempvec, lattice_vector[1], lattice_vector[2]); V_QADD(normal, normal, +h*, tempvec); V_CROSS(tempvec, lattice_vector[2], lattice_vector[0]); V_QADD(normal, normal, +k*, tempvec); V_CROSS(tempvec, lattice_vector[0], lattice_vector[1]); V_QADD(normal, normal, +l*, tempvec); sv_list1 = NULL; c = (float) GCD(h, k); cd = c ? c : 1.0; V_2_ASSIGN(a, = k/cd * ,lattice_vector[0], - h/cd *, lattice_vector[1]); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list1 = g_slist_prepend(sv_list1, sv); } c = (float) GCD(h, l); cd = c ? c : 1.0; V_2_ASSIGN(a, = l/cd * ,lattice_vector[0], - h/cd *, lattice_vector[2]); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list1 = g_slist_prepend(sv_list1, sv); } c = (float) GCD(k, l); cd = c ? c : 1.0; V_2_ASSIGN(a, = l/cd * ,lattice_vector[1], - k/cd *, lattice_vector[2]); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list1 = g_slist_prepend(sv_list1, sv); } sv_list2 = NULL; list1 = sv_list1; for (i=0 ; idata; list1 = list2 = g_slist_next(list1); for (j=i+1 ; jdata; list2 = g_slist_next(list2); V_2_ASSIGN(a, = , *v_a, + , *v_b); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list2 = g_slist_prepend(sv_list2, sv); } V_2_ASSIGN(a, = , *v_a, - , *v_b); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list2 = g_slist_prepend(sv_list2, sv); } } } sv_list1 = g_slist_concat(sv_list1, sv_list2); sv_list1 = g_slist_sort(sv_list1, (gpointer) vector_compare); /* set v_a to first (shortest now sorted) vector */ list1 = sv_list1; v_a = (vector *) list1->data; list1 = g_slist_next(list1); /* loop over remaining vectors - find shortest w/ orthogonal component */ while (list1) { v_b = (vector *) list1->data; V_CROSS(a, *v_a, *v_b); x = V_MAGSQ(a); if (x > EPSILON) { s2_found = TRUE; break; } list1 = g_slist_next(list1); } /* checks */ if (!s2_found) { printf("Failed to find surface vectors.\n"); return; } g_assert(v_a != NULL); g_assert(v_b != NULL); /* calculate transformation matrix */ V_SCALER(t_mat[2], (1.0/V_MAG(normal)), normal); V_SCALER(t_mat[0], (1.0/V_MAG(*v_a)), *v_a); V_CROSS(t_mat[1], t_mat[2], t_mat[0]); ARR3SET(norm, normal.element); ARR3SET(va, v_a->element); normalize(norm, 3); normalize(va, 3); ARR3SET(&tmat[6], norm); ARR3SET(&tmat[0], va); crossprod(&tmat[3], norm, va); /* calculate transformed lattice vectors */ for (j = 0; j < 3; j++) for (i = 0; i < 3; i++) V_E(work_lat[j], i) = V_DOT(t_mat[i], lattice_vector[j]); /* calculate reciprocal transformed lattice vectors */ V_CROSS(tempvec, work_lat[1], work_lat[2]); inv_denom = 1.0 / V_DOT(tempvec, work_lat[0]); V_CROSS(tempvec, work_lat[1], work_lat[2]); V_SCALER(rec_work_lat[0], inv_denom, tempvec); V_CROSS(tempvec, work_lat[2], work_lat[0]); V_SCALER(rec_work_lat[1], inv_denom, tempvec); V_CROSS(tempvec, work_lat[0], work_lat[1]); V_SCALER(rec_work_lat[2], inv_denom, tempvec); /* calculate transformed surface vectors */ for (i = 0; i < 3; i++) V_E(s[0], i) = V_DOT(t_mat[i], *v_a); for (i = 0; i < 3; i++) V_E(s[1], i) = V_DOT(t_mat[i], *v_b); /* return transformed surface vectors data in dest->latmat */ /* NB: latmat[0..8] is a 3x3 matrix so, */ /* | a b 0 | | c d 0 | | 0 0 0 | goes into latmat rows first ie a,b,0,c,d,0,0,0,0 */ /* set s[2] = to depths */ /* depth = 1.0e10; */ /* surface specification */ gcd = GCD(GCD(h, k), GCD(k, l)); VEC3SET(vec, h, k, l); vecmat(src->rlatmat, vec); /* calculate the Dhkl */ /* NB: we want the correct dspacing wrt hkl, ie DON'T remove the GCD */ /* dest->surface.dspacing = 1.0/VEC3MAG(vec); */ /* actual cut depth */ /* NB: done wrt the FULL depth (ie remove the gcd) */ depth = gcd/VEC3MAG(vec); /* dest->surface.depth = depth; */ /* round off the shift */ /* shift = decimal_round(dest->surface.shift, 4); */ /* store the work lattice */ for (i=0 ; i<3 ; i++) { wlat[3*i+0] = V_E(work_lat[0], i); wlat[3*i+1] = V_E(work_lat[1], i); wlat[3*i+2] = V_E(work_lat[2], i); } /* transfer surface vectors */ VEC3SET(&plane->lattice[0], V_X(s[0]), V_X(s[1]), 0.0); VEC3SET(&plane->lattice[3], V_Y(s[0]), V_Y(s[1]), 0.0); VEC3SET(&plane->lattice[6], 0.0, 0.0, 1.0); /* construct the depth vector */ /* compute the surface normal */ VEC3SET(vec, V_X(s[0]), V_Y(s[0]), 0.0); VEC3SET(vec2, V_X(s[1]), V_Y(s[1]), 0.0); crossprod(dn, vec, vec2); /* search for a vector with the smallest */ /* orthogonal component to the surface vectors */ flag=0; depth_min=0; for (i=0 ; i<3 ; i++) { /* ignore zero indices, which yield 0 orthogonal component */ if (plane->m[i]) { /* dotproduct of vector with normal */ ARR3SET(vec, dn); vec[0] *= wlat[i]; vec[1] *= wlat[i+3]; vec[2] *= wlat[i+6]; depth = fabs(vec[0] + vec[1] + vec[2]); if (flag) { if (depth > depth_min) continue; } else flag++; depth_min = depth; /* ensure the depth vector is pointing in the +ve z direction */ if (wlat[i+6] < 0.0) sign = -1.0; else sign = 1.0; plane->lattice[2] = sign*wlat[i]; plane->lattice[5] = sign*wlat[i+3]; plane->lattice[8] = sign*wlat[i+6]; } } } /********************************************/ /* allocate for new plane & init for safety */ /********************************************/ #define DEBUG_CREATE_PLANE 0 gpointer plane_new(gdouble *m, struct model_pak *model) { gint h, k, l, n; gdouble len, vec[3]; struct plane_pak *plane=NULL; /* checks */ g_return_val_if_fail(model != NULL, NULL); plane = g_malloc(sizeof(struct plane_pak)); #if DEBUG_CREATE_PLANE printf("creating: %d %d %d -> ", (gint) m[0], (gint) m[1], (gint) m[2]); #endif /* save orig */ h = m[0]; k = m[1]; l = m[2]; n = 2; /* absence testing function */ if (!model->surface.ignore_symmetry) { while (surf_sysabs(model, h, k, l) && n<100) { h = n*m[0]; k = n*m[1]; l = n*m[2]; n++; } } if (n == 100) { printf("WARNING: screwed up systematic absence check.\n"); h /= 99; k /= 99; l /= 99; n = 2; } #if DEBUG_CREATE_PLANE printf("%d %d %d (x %d)\n", h, k, l, n-1); #endif /* init */ VEC3SET(plane->m, (gdouble) h, (gdouble) k, (gdouble) l); VEC3SET(plane->norm, (gdouble) h, (gdouble) k, (gdouble) l); VEC3SET(plane->index, h, k, l); plane->shifts = NULL; plane->vertices = NULL; /* calc Dhkl */ VEC3SET(vec, h, k, l); vecmat(model->rlatmat, vec); plane->dhkl = 1.0/VEC3MAG(vec); /* morphology prediction */ len = VEC3MAG(vec); if (len > FRACTION_TOLERANCE) { VEC3MUL(vec, 1.0/len); VEC3MUL(vec, -1.0); ARR3SET(plane->x, vec); } else { gui_text_show(WARNING, "plane of zero length created.\n"); } plane->esurf_shift = 0.0; plane->eatt_shift = 0.0; plane->bbpa_shift = 0.0; plane->esurf[0] = 0.0; plane->esurf[1] = 0.0; plane->eatt[0] = 0.0; plane->eatt[1] = 0.0; plane->bbpa = 0.0; plane->area = 0.0; plane->f[0] = 0.0; plane->f[1] = 0.0; /* calc? */ plane->multiplicity = 1; plane->present = TRUE; plane->visible = FALSE; plane->primary = TRUE; plane->parent = NULL; VEC3SET(plane->rx, 0.0, 0.0, 0.0); /* NEW */ plane_compute_vectors(plane, model); return(plane); } /************************************************************/ /* determine if a particular plane has already been created */ /************************************************************/ gpointer plane_find(gdouble *hkl, struct model_pak *model) { GSList *list; struct plane_pak *comp, *plane; /* get corrected (sys absent!) miller index */ comp = plane_new(hkl, model); g_return_val_if_fail(comp != NULL, NULL); for (list=model->planes ; list ; list=g_slist_next(list)) { plane = list->data; if (facet_equiv(model, comp->index, plane->index)) return(plane); } g_free(comp); return(NULL); } /***********************************/ /* generate symmetry related faces */ /***********************************/ /* FIXME - handle duplication */ #define DEBUG_SURF_SYMMETRY_GENERATE 0 void surf_symmetry_generate(struct model_pak *model) { gint i; gint *sym_hkl; gdouble m[3]; GSList *list, *new_planes, *sym_equiv, *sym_list; struct plane_pak *plane=NULL, *new_plane=NULL; #if DEBUG_SURF_SYMMETRY_GENERATE printf("======\n"); printf("BEFORE\n"); printf("======\n"); surface_print_planes(model->planes); #endif /* compute symmetry related faces */ new_planes = NULL; for (list=model->planes ; list ; list=g_slist_next(list)) { plane = list->data; sym_equiv = get_facet_equiv(model, plane->index); #if DEBUG_SURF_SYMMETRY_GENERATE printf(" *** {%d %d %d}\n", plane->index[0], plane->index[1], plane->index[2]); #endif /* check each non-primary face for extinctions */ i=0; sym_list = g_slist_next(sym_equiv); while (sym_list) { /* get the (normalized) hkl's */ sym_hkl = sym_list->data; ARR3SET(m, sym_hkl); new_plane = plane_new(m, model); if (plane) { new_plane->primary = FALSE; new_plane->parent = plane; new_plane->esurf[0] = plane->esurf[0]; new_plane->esurf[1] = plane->esurf[1]; new_plane->eatt[0] = plane->eatt[0]; new_plane->eatt[1] = plane->eatt[1]; new_plane->bbpa = plane->bbpa; new_plane->area = plane->area; #if DEBUG_SURF_SYMMETRY_GENERATE printf(" > (%d %d %d)\n", new_plane->index[0], new_plane->index[1], new_plane->index[2]); #endif new_planes = g_slist_prepend(new_planes, new_plane); } sym_list = g_slist_next(sym_list); i++; } free_slist(sym_equiv); } /* use the new list with the symmetry adjusted planes */ model->planes = g_slist_concat(model->planes, new_planes); model->num_planes = g_slist_length(model->planes); #if DEBUG_SURF_SYMMETRY_GENERATE printf("======\n"); printf("ADDING\n"); printf("======\n"); surface_print_planes(new_planes); #endif } /***********************/ /* free a single shift */ /***********************/ void shift_free(gpointer data) { struct shift_pak *shift = data; g_assert(shift != NULL); /* FIXME - this may create a memory leak, but it'll be */ /* the users fault */ if (shift->locked) { printf("ERROR: shift is locked.\n"); return; } g_free(shift); } /*************************************/ /* free the data in a list of shifts */ /*************************************/ void shift_data_free(GSList *shifts) { GSList *list; struct shift_pak *shift; /* free a list of shift pak structures */ list = shifts; while (list) { shift = list->data; list = g_slist_next(list); shift_free(shift); } } /***********************/ /* free a single plane */ /***********************/ void plane_free(gpointer data) { struct plane_pak *plane = data; shift_data_free(plane->shifts); g_slist_free(plane->shifts); /* FIXME - is this good enough? */ g_slist_free(plane->vertices); g_free(plane); } /*************************************/ /* free the data in a list of planes */ /*************************************/ void plane_data_free(GSList *planes) { GSList *list; struct plane_pak *plane; /* free a list of plane pak structures */ list = planes; while (list) { plane = list->data; list = g_slist_next(list); plane_free(plane); } } /* DEBUG */ void print_shell_offset(gint flag, struct model_pak *model) { gdouble x[3]; GSList *list; struct core_pak *core; struct shel_pak *shell; printf("-----------------------------------------------\n"); printf("model: %p\n", model); P3MAT("latmat:", model->latmat); printf("-----------------------------------------------\n"); for (list=model->cores ; list ; list=g_slist_next(list)) { core = list->data; if (core->shell) { shell = core->shell; ARR3SET(x, shell->x); ARR3SUB(x, core->x); if (flag) vecmat(model->latmat, x); printf("[r = %f] ", VEC3MAG(x)); P3VEC(" + ", x); } } } /**********************************************/ /* refresh plane energy data for all surfaces */ /**********************************************/ void plane_refresh_all(struct model_pak *model) { /* refresh primary planes */ /* refresh symmetry related planes */ } /****************************************/ /* uses marvin code to create a surface */ /****************************************/ #define DEBUG_GENSURF 0 gint generate_surface(struct model_pak *src, struct model_pak *dest) { vector normal; vector lattice_vector[3]; /* lattice vectors */ vector t_mat[3]; /* transformation matrix */ vector work_lat[3]; /* transformed lattice vectors */ vector rec_work_lat[3]; /* reciprocal transformed lattice vectors */ vector s[3]; /* space we are trying to fill */ vector rec_s[2]; /* reciprocal of s[0] and s[1] */ vector tempvec; vector *sv; vector a, *v_a=NULL, *v_b=NULL; boolean s2_found = FALSE; gint c, i, j, h, k, l, flag; gint ia, ib, ic; gint amin, amax, bmin, bmax, cmin, cmax; gint ob, sb; gint GCD(gint, gint); gint vector_compare(vector *, vector *); gdouble inv_denom; gdouble shift, depth, depth_1, depth_2, depth_min; gdouble gcd, cd, x; gdouble z1_max, z1_min, z2_max, z2_min; gdouble tempfloat; gdouble tmat[9], norm[3], va[3]; gdouble wlat[9], temp[9], lpm[9]; gdouble sign, vec[3], vec2[3], dn[3]; GSList *list1, *list2, *sv_list1, *sv_list2; GSList *list, *clist, *slist, *mlist; struct core_pak *core; struct shel_pak *shel; struct mol_pak *mol; /* checks */ g_assert(src != NULL); g_assert(dest != NULL); /* find surface normal from the miller index and lattice vectors*/ V_ZERO(normal); /* setup */ h = dest->surface.miller[0]; k = dest->surface.miller[1]; l = dest->surface.miller[2]; /* acquire lattice vectors */ V_X(lattice_vector[0]) = src->latmat[0]; V_Y(lattice_vector[0]) = src->latmat[3]; V_Z(lattice_vector[0]) = src->latmat[6]; V_X(lattice_vector[1]) = src->latmat[1]; V_Y(lattice_vector[1]) = src->latmat[4]; V_Z(lattice_vector[1]) = src->latmat[7]; V_X(lattice_vector[2]) = src->latmat[2]; V_Y(lattice_vector[2]) = src->latmat[5]; V_Z(lattice_vector[2]) = src->latmat[8]; V_CROSS(tempvec, lattice_vector[1], lattice_vector[2]); V_QADD(normal, normal, +h*, tempvec); V_CROSS(tempvec, lattice_vector[2], lattice_vector[0]); V_QADD(normal, normal, +k*, tempvec); V_CROSS(tempvec, lattice_vector[0], lattice_vector[1]); V_QADD(normal, normal, +l*, tempvec); sv_list1 = NULL; c = (float) GCD(h, k); cd = c ? c : 1.0; V_2_ASSIGN(a, = k/cd * ,lattice_vector[0], - h/cd *, lattice_vector[1]); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list1 = g_slist_prepend(sv_list1, sv); } c = (float) GCD(h, l); cd = c ? c : 1.0; V_2_ASSIGN(a, = l/cd * ,lattice_vector[0], - h/cd *, lattice_vector[2]); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list1 = g_slist_prepend(sv_list1, sv); } c = (float) GCD(k, l); cd = c ? c : 1.0; V_2_ASSIGN(a, = l/cd * ,lattice_vector[1], - k/cd *, lattice_vector[2]); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list1 = g_slist_prepend(sv_list1, sv); } sv_list2 = NULL; list1 = sv_list1; for (i=0 ; idata; list1 = list2 = g_slist_next(list1); for (j=i+1 ; jdata; list2 = g_slist_next(list2); V_2_ASSIGN(a, = , *v_a, + , *v_b); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list2 = g_slist_prepend(sv_list2, sv); } V_2_ASSIGN(a, = , *v_a, - , *v_b); if (V_MAGSQ(a) > EPSILON) { sv = g_malloc(sizeof(vector)); V_EQUATE(*sv, a); sv_list2 = g_slist_prepend(sv_list2, sv); } } } sv_list1 = g_slist_concat(sv_list1, sv_list2); sv_list1 = g_slist_sort(sv_list1, (gpointer) vector_compare); /* set v_a to first (shortest now sorted) vector */ list1 = sv_list1; v_a = (vector *) list1->data; list1 = g_slist_next(list1); /* loop over remaining vectors - find shortest w/ orthogonal component */ while (list1) { v_b = (vector *) list1->data; V_CROSS(a, *v_a, *v_b); x = V_MAGSQ(a); if (x > EPSILON) { s2_found = TRUE; break; } list1 = g_slist_next(list1); } /* checks */ if (!s2_found) { gui_text_show(ERROR, "Failed to find surface vectors.\n"); return(1); } g_assert(v_a != NULL); g_assert(v_b != NULL); /* calculate transformation matrix */ V_SCALER(t_mat[2], (1.0/V_MAG(normal)), normal); V_SCALER(t_mat[0], (1.0/V_MAG(*v_a)), *v_a); V_CROSS(t_mat[1], t_mat[2], t_mat[0]); ARR3SET(norm, normal.element); ARR3SET(va, v_a->element); normalize(norm, 3); normalize(va, 3); ARR3SET(&tmat[6], norm); ARR3SET(&tmat[0], va); crossprod(&tmat[3], norm, va); /* calculate transformed lattice vectors */ for (j = 0; j < 3; j++) for (i = 0; i < 3; i++) V_E(work_lat[j], i) = V_DOT(t_mat[i], lattice_vector[j]); /* calculate reciprocal transformed lattice vectors */ V_CROSS(tempvec, work_lat[1], work_lat[2]); inv_denom = 1.0 / V_DOT(tempvec, work_lat[0]); V_CROSS(tempvec, work_lat[1], work_lat[2]); V_SCALER(rec_work_lat[0], inv_denom, tempvec); V_CROSS(tempvec, work_lat[2], work_lat[0]); V_SCALER(rec_work_lat[1], inv_denom, tempvec); V_CROSS(tempvec, work_lat[0], work_lat[1]); V_SCALER(rec_work_lat[2], inv_denom, tempvec); /* calculate transformed surface vectors */ for (i = 0; i < 3; i++) V_E(s[0], i) = V_DOT(t_mat[i], *v_a); for (i = 0; i < 3; i++) V_E(s[1], i) = V_DOT(t_mat[i], *v_b); /* return transformed surface vectors data in dest->latmat */ /* NB: latmat[0..8] is a 3x3 matrix so, */ /* | a b 0 | | c d 0 | | 0 0 0 | goes into latmat rows first ie a,b,0,c,d,0,0,0,0 */ /* set s[2] = to depths */ /* depth = 1.0e10; */ /* surface specification */ gcd = GCD(GCD(h, k), GCD(k, l)); VEC3SET(vec, h, k, l); vecmat(src->rlatmat, vec); /* calculate the Dhkl */ /* NB: we want the correct dspacing wrt hkl, ie DON'T remove the GCD */ dest->surface.dspacing = 1.0/VEC3MAG(vec); /* actual cut depth */ /* NB: done wrt the FULL depth (ie remove the gcd) */ depth = gcd/VEC3MAG(vec); dest->surface.depth = depth; /* round off the shift */ shift = decimal_round(dest->surface.shift, 4); #if DEBUG_GENSURF printf("\n----------------------------\n"); printf(" hkl: %d %d %d\n", h, k, l); printf(" gcd: %f \n", gcd); printf(" shift: %.20f\n", shift); printf("regions: %d %d\n", (gint) dest->surface.region[0], (gint) dest->surface.region[1]); printf(" Dhkl: %f\n", dest->surface.dspacing); printf(" depth: %f\n", depth); printf("----------------------------\n"); #endif /* NB: depth_1 is region[0], but depth_2 is region[0]+region[1] */ depth_1 = dest->surface.region[0] * depth; depth_2 = depth_1 + dest->surface.region[1] * depth; V_ZERO(s[2]); V_Z(s[2]) = -(depth_2); /* transfer surface vectors */ VEC3SET(&dest->latmat[0], V_X(s[0]), V_X(s[1]), 0.0); VEC3SET(&dest->latmat[3], V_Y(s[0]), V_Y(s[1]), 0.0); VEC3SET(&dest->latmat[6], 0.0, 0.0, 1.0); /* calculate reciprocal of two surface vectors */ inv_denom = 1.0 / (V_X(s[0])*V_Y(s[1]) - V_Y(s[0])*V_X(s[1])); V_X(rec_s[0]) = V_Y(s[1])*inv_denom; V_Y(rec_s[0]) = -V_X(s[1])*inv_denom; V_Z(rec_s[0]) = 0.0; V_X(rec_s[1]) = -V_Y(s[0])*inv_denom; V_Y(rec_s[1]) = V_X(s[0])*inv_denom; V_Z(rec_s[1]) = 0.0; z2_min = z1_min = 0.00; z2_max = V_Z(s[2]); z1_max = depth_1 * (z2_max < 0 ? -1.0: +1.0); if (z2_max < z2_min) { tempfloat = z2_max; z2_max = z2_min; z2_min = tempfloat; } if (z1_max < z1_min) { tempfloat = z1_max; z1_max = z1_min; z1_min = tempfloat; } /* display name */ g_free(dest->basename); dest->basename = g_strdup_printf("%s_%-1d%-1d%-1d_%6.4f", g_strstrip(src->basename), h, k, l, shift); /* store the work lattice */ for (i=0 ; i<3 ; i++) { wlat[3*i+0] = V_E(work_lat[0], i); wlat[3*i+1] = V_E(work_lat[1], i); wlat[3*i+2] = V_E(work_lat[2], i); } #if DEBUG_GENSURF P3MAT(" src lat: ", src->latmat); #endif /* construct the depth vector */ /* compute the surface normal */ VEC3SET(vec, dest->latmat[0], dest->latmat[3], dest->latmat[6]); VEC3SET(vec2, dest->latmat[1], dest->latmat[4], dest->latmat[7]); crossprod(dn, vec, vec2); /* search for a vector with the smallest */ /* orthogonal component to the surface vectors */ flag=0; depth_min=0; for (i=0 ; i<3 ; i++) { /* ignore zero indices, which yield 0 orthogonal component */ if (dest->surface.miller[i]) { /* dotproduct of vector with normal */ ARR3SET(vec, dn); vec[0] *= wlat[i]; vec[1] *= wlat[i+3]; vec[2] *= wlat[i+6]; depth = fabs(vec[0] + vec[1] + vec[2]); #if DEBUG_GENSURF printf("[i=%d, depth=%f]", i, depth); #endif if (flag) { if (depth > depth_min) continue; } else flag++; depth_min = depth; /* ensure the depth vector is pointing in the +ve z direction */ if (wlat[i+6] < 0.0) sign = -1.0; else sign = 1.0; dest->surface.depth_vec[0] = sign*wlat[i]; dest->surface.depth_vec[1] = sign*wlat[i+3]; dest->surface.depth_vec[2] = sign*wlat[i+6]; dest->latmat[2] = sign*wlat[i]; dest->latmat[5] = sign*wlat[i+3]; dest->latmat[8] = sign*wlat[i+6]; } } #if DEBUG_GENSURF printf(" : Minimum = %f\n", depth_min); P3VEC("depth vec:", dest->surface.depth_vec); #endif g_assert(flag != 0); /* generate lattice matrix inverse */ memcpy(dest->ilatmat, dest->latmat, 9*sizeof(gdouble)); matrix_invert(dest->ilatmat); /* locate the position of the new cell's repeat vectors */ /* in terms of the lattice space of the source model */ /* this gives us the lattice point matrix (lpm) which */ /* is used to determine how many source unit cells */ /* will be required to fill out the new surface cell */ memcpy(lpm, dest->latmat, 9*sizeof(gdouble)); memcpy(temp, tmat, 9*sizeof(gdouble)); matrix_invert(temp); matmat(temp, lpm); matmat(src->ilatmat, lpm); #if DEBUG_GENSURF P3MAT("tmat: ", tmat); P3MAT("dest latmat: ", dest->latmat); P3MAT("dest ilatmat: ", dest->ilatmat); P3MAT("lpm: ", lpm); #endif /* setup repeats required to fill the new cell */ amin=bmin=cmin=0; amax=bmax=cmax=0; /* required number of a cells */ for (i=0 ; i<3 ; i++) { ia = rint(lpm[i]); if (ia < amin) amin = ia; if (ia > amax) amax = ia; } /* required number of b cells */ for (i=3 ; i<6 ; i++) { ib = rint(lpm[i]); if (ib < bmin) bmin = ib; if (ib > bmax) bmax = ib; } /* required number of c cells */ for (i=6 ; i<9 ; i++) { ic = rint(lpm[i]); if (ic < cmin) cmin = ic; if (ic > cmax) cmax = ic; } /* one cell buffer */ amax+=2; bmax+=2; cmax+=2; amin--; bmin--; cmin--; #if DEBUG_GENSURF printf("a: %d - %d\n", amin, amax); printf("b: %d - %d\n", bmin, bmax); printf("c: %d - %d\n", cmin, cmax); #endif /* compute transformation pipeline */ memcpy(temp, src->latmat, 9*sizeof(gdouble)); matmat(tmat, temp); matmat(dest->ilatmat, temp); /* create a single transformed unit cell */ /* full loop required to cover one cell in the transformed coordinates */ for (ic=cmin ; icmoles ; mlist ; mlist=g_slist_next(mlist)) { mol = mlist->data; /* transform the centroid */ ARR3SET(va, mol->centroid); ARR3ADD(va, vec); vecmat(temp, va); va[2] += shift; /* ignore molecules outside the transformed cell */ /* NB: the range must be exactly the smallest numerical value below 1.0 [G_MINDOUBLE, 1.0] */ /* NB: the value of EPS adds a bias to promote molecules to the top of the surface */ #define EPS 0.001 if (va[0] < G_MINDOUBLE+EPS || va[0] > 1.0+EPS) continue; if (va[1] < G_MINDOUBLE+EPS || va[1] > 1.0+EPS) continue; if (va[2] < G_MINDOUBLE+EPS || va[2] > 1.0+EPS) continue; /* loop over all atoms in molecule */ for (clist=mol->cores ; clist ; clist=g_slist_next(clist)) { /* dup_core() will duplicate an attached shell as well */ core = dup_core(clist->data); dest->cores = g_slist_prepend(dest->cores, core); /* transform */ ARR3ADD(core->x, vec); vecmat(temp, core->x); core->x[2] += shift; /* init flags */ core->primary = TRUE; core->primary_core = NULL; core->orig = TRUE; /* shell */ if (core->shell) { shel = core->shell; dest->shels = g_slist_prepend(dest->shels, shel); ARR3ADD(shel->x, vec); vecmat(temp, shel->x); shel->x[2] += shift; /* init flags */ shel->primary = TRUE; shel->primary_shell = NULL; shel->orig = TRUE; } } } } } } dest->periodic = 3; dest->fractional = FALSE; dest->axes_type = CARTESIAN; dest->construct_pbc = TRUE; /* NB: can't leave the (expensive) delete_duplicate_cores() to the */ /* final model_prep() as that treats the model as 2D periodic */ /* and won't clip the top & bottom of the surface*/ matrix_lattice_init(dest); zone_init(dest); delete_duplicate_cores(dest); shell_make_links(dest); /* terminate here for debugging */ #define DEBUG_SURFACE_CELL 0 #if DEBUG_SURFACE_CELL dest->sginfo.spacenum = 1; dest->fractional = TRUE; dest->axes_type = CARTESIAN; dest->construct_pbc = TRUE; dest->gulp.method = CONV; model_prep(dest); return(0); #endif /* build surface core and shell lists from transformed cell */ clist = slist = NULL; /* region 1 cores and shells */ amax = dest->surface.region[0] + 1; for (i=1 ; icores ; list ; list=g_slist_next(list)) { core = dup_core(list->data); clist = g_slist_prepend(clist, core); /* printf("[%s] c ", core->label); P3VEC(" : ", core->x); */ core->x[2] -= (gdouble) i; vecmat(dest->latmat, core->x); core->region = REGION1A; if (core->shell) { shel = core->shell; slist = g_slist_prepend(slist, shel); /* CURRENT - alternate shell coordinate init */ /* more tolerant of pbc offset core-shell links */ shel->x[2] -= (gdouble) i; vecmat(dest->latmat, shel->x); /* printf("[%s] s ", shel->label); P3VEC(" : ", shel->x); */ shel->region = REGION1A; } } } /* region 2 cores and shells */ bmax = amax + dest->surface.region[1]; for (i=amax ; icores ; list ; list=g_slist_next(list)) { core = dup_core(list->data); clist = g_slist_prepend(clist, core); core->x[2] -= (gdouble) i; vecmat(dest->latmat, core->x); core->region = REGION2A; if (core->shell) { shel = core->shell; slist = g_slist_prepend(slist, shel); shel->x[2] -= (gdouble) i; vecmat(dest->latmat, shel->x); shel->region = REGION2A; } } } /* replace old structure (transformed cell) with the new surface */ free_core_list(dest); dest->cores = g_slist_reverse(clist); dest->shels = g_slist_reverse(slist); /* adjust depth translation vector to account for region sizes */ dest->latmat[2] *= dest->surface.region[0]+dest->surface.region[1]; dest->latmat[5] *= dest->surface.region[0]+dest->surface.region[1]; dest->latmat[8] *= dest->surface.region[0]+dest->surface.region[1]; /* surface cell init */ dest->periodic = 2; dest->sginfo.spacenum = 1; dest->fractional = FALSE; dest->axes_type = CARTESIAN; dest->construct_pbc = TRUE; dest->surface.keep_atom_order = src->surface.keep_atom_order; dest->gulp.method = CONV; /* surface cell type */ if (src->surface.true_cell) dest->surface.true_cell = TRUE; else { dest->surface.true_cell = FALSE; /* want to force c to be // to z */ /* NB: vectors are in columns */ dest->latmat[2] = 0.0; dest->latmat[5] = 0.0; } free_slist(sv_list1); /* CURRENT - does this screw up the true_cell crap? */ /* for (list=dest->cores ; list ; list=g_slist_next(list)) { core = list->data; ARR3SET(va, core->x); vecmat(dest->latmat, va); core->x[2] = va[2]; } dest->latmat[2] = 0.0; dest->latmat[5] = 0.0; dest->latmat[8] = 1.0; */ #if DEBUG_GENSURF printf("gensurf cores: %d\n", g_slist_length(dest->cores)); printf("gensurf shels: %d\n", g_slist_length(dest->shels)); #endif /* build mode follows source structure */ /* dest->build_molecules = src->build_molecules; */ /* last init */ model_prep(dest); calc_emp(dest); /* FIXME - bugged for unbroken bond chain materials */ /* mark the growth slice */ depth_min = dest->surface.region[0]+dest->surface.region[1]; depth_min *= gcd; depth_min = -1.0/depth_min; for (list1=dest->moles ; list1 ; list1=g_slist_next(list1)) { mol = list1->data; if (mol->centroid[2] > depth_min) { for (list2=mol->cores ; list2 ; list2=g_slist_next(list2)) { core = list2->data; core->growth = TRUE; } } } /* NEW - bonding analysis */ ob = src->surface.bonds_full; sb = g_slist_length(dest->bonds); c = dest->surface.region[0]+dest->surface.region[1]; dest->surface.bonds_cut = ob*c - sb; /* printout of bonding analysis */ /* printf("[%2d %2d %2d] (%f)", h, k, l, shift); printf(" [src = %d][surf = %d][r1+r2 = %d] : cleaved = %d : ", ob, sb, c, dest->surface.bonds_cut); printf(" : area = %f", dest->area); printf(" : cleaved = %d", dest->surface.bonds_cut); printf("\n"); */ return(0); } /*************************************/ /* molecule centroid z value sorting */ /*************************************/ gint surf_molecules_zsort(gpointer p1, gpointer p2) { struct mol_pak *m1 = p1, *m2 = p2; if (m1->centroid[2] > m2->centroid[2]) return(-1); return(1); } /*******************************************************************/ /* peturb a planar surface in order to test alternate terminations */ /*******************************************************************/ void surf_shift_explore(struct model_pak *src, struct surface_pak *surface) { gint i, j, n; GSList *list; struct plane_pak *plane; struct shift_pak *shift; struct core_pak *core; struct mol_pak *mol1, *mol2; struct model_pak *model; g_assert(src != NULL); g_assert(src->periodic == 3); /* setup plane and shift */ plane = plane_new(surface->miller, src); shift = shift_new(surface->shift); shift->region[0] = surface->region[0]; shift->region[1] = surface->region[1]; printf("Perturbing [%d %d %d] : %f : [%d][%d]\n", plane->index[0], plane->index[1], plane->index[2], shift->shift, shift->region[0], shift->region[1]); /* CURRENT - create a new model for each alternate (zero dipole) surface found */ /* generate surface */ model = make_surface(src, plane, shift); coords_init(INIT_COORDS, model); /* sort molecule centroids */ model->moles = g_slist_sort(model->moles, (gpointer) surf_molecules_zsort); printf("initial dipole: %f\n", model->gulp.sdipole); i = 0; /* top */ mol1 = g_slist_nth_data(model->moles, i); n = g_slist_length(model->moles); mol2 = g_slist_nth_data(model->moles, n-1); /* nth bottommost */ /* bottom = g_slist_copy(model->moles); bottom = g_slist_reverse(bottom); */ /* for each topmost item, exchange with a bottomost item and recalculate dipole */ /* move all atoms in selection */ for (list=mol1->cores ; list ; list=g_slist_next(list)) { core = list->data; region_move_atom(core, DOWN, model); } mol2 = g_slist_nth_data(model->moles, n-1); for (j=0 ; j<3 ; j++) { mol2 = g_slist_nth_data(model->moles, n-j-1); /* test */ for (list=mol2->cores ; list ; list=g_slist_next(list)) { core = list->data; region_move_atom(core, UP, model); /* P3VEC("c1: ", core->x); */ } /* update */ /* FIXME - something wierd going on here - dipole recalc doesnt seem */ /* to work unless connect_molecules() is called - why??? */ coords_compute(model); /* connect_bonds(model); */ connect_molecules(model); /* TODO - don't recalc from scratch - can update based on moves */ calc_emp(model); printf("pert [%d,%d] new dipole: %f\n", i, j, model->gulp.sdipole); /* TODO - if zero -> leave this model (after init) and create a new */ /* one for exploring further perturbations */ /* restore */ for (list=mol2->cores ; list ; list=g_slist_next(list)) { core = list->data; region_move_atom(core, DOWN, model); /* P3VEC("c2: ", core->x); */ } coords_compute(model); } gui_refresh(GUI_CANVAS); } /****************************************************************************** * GCD * Greatest common denominator ******************************************************************************/ gint GCD(gint p, gint q) { #if defined(__MWERKS__) /* fix for compiler bug! */ gint i; if (q == 0) { i = abs(p); return(i); } #else if (q == 0) return(abs(p)); #endif else return(GCD(q, p%q)); } /****************************************************************************** * vector_compare * compare two vectors returning their difference as an integer. This * routine is to be used in conjuction with SORT(). ******************************************************************************/ gint vector_compare(vector *a, vector *b) { int i; double diff; diff = V_MAGSQ(*a) - V_MAGSQ(*b); if (diff < -EPSILON) return(-1); else if (diff > EPSILON) return(1); else { /* magnitude of a & b is the same, so sort based on element magnitudes */ for (i=0 ; i<3 ; i++) { diff = V_E(*a,i) - V_E(*b,i); if (diff < -EPSILON) return(1); else if (diff > EPSILON) return(-1); } } /* a & b are identical */ return(0); }