/* Copyright (C) 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 #include #include "gdis.h" #include "coords.h" #include "edit.h" #include "file.h" #include "parse.h" #include "task.h" #include "model.h" #include "morph.h" #include "numeric.h" #include "sginfo.h" #include "matrix.h" #include "space.h" #include "surface.h" #include "shortcuts.h" #include "interface.h" #include "dialog.h" #include "opengl.h" /* main pak structures */ extern struct sysenv_pak sysenv; extern struct elem_pak elements[]; enum { SURF_TITLE, SURF_REGIONS, SURF_ESURF_UNRE, SURF_EATT_UNRE, SURF_ESURF_RE, SURF_EATT_RE, SURF_DIPOLE, SURF_GNORM, SURF_MODEL, SURF_PLANE, SURF_SHIFT, SURF_NCOLS }; /* globals */ GtkTreeStore *surf_tree_store; GtkWidget *surf_tree_view; GtkWidget *surf_morph_energy[4]; GtkWidget *surf_hkl_family; gint all_planes = FALSE; gdouble rank_value = 10.0; struct model_pak surfdata; gchar *titles[] = {"hkl/shift", "Dhkl/depth", "Esurf (u)", "Eatt (u)", "Esurf (r)", "Eatt (r)", "dipole ", "gnorm "}; /***************************/ /* attempt to match shifts */ /***************************/ gint compare_shifts(gconstpointer *s1, gconstpointer *s2) { struct shift_pak *s1data; struct shift_pak *s2data; s1data = (struct shift_pak *) s1; s2data = (struct shift_pak *) s2; /* seek mismatches & return failure */ if (s1data->shift != s2data->shift) return(1); if (s1data->region[0] != s2data->region[0]) return(1); if (s1data->region[1] != s2data->region[1]) return(1); /* otherwise, return succesful match */ return(0); } /*************************************************/ /* set values for a shift in the treeview widget */ /*************************************************/ void fn_surf_set_shift(GtkTreeIter *iter, struct shift_pak *shift) { gint i; gchar *text, *old_text; GtkTreeModel *treemodel; treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(surf_tree_view)); for (i=SURF_TITLE ; i<=SURF_GNORM ; i++) { switch(i) { case SURF_REGIONS: text = g_strdup_printf("%2d:%2d", shift->region[0], shift->region[1]); break; case SURF_EATT_UNRE: text = g_strdup_printf("%9.4f", shift->eatt[0]); break; case SURF_ESURF_UNRE: text = g_strdup_printf("%9.4f", shift->esurf[0]); break; case SURF_EATT_RE: text = g_strdup_printf("%9.4f", shift->eatt[1]); break; case SURF_ESURF_RE: text = g_strdup_printf("%9.4f", shift->esurf[1]); break; case SURF_DIPOLE: if (shift->dipole_computed) text = g_strdup_printf("%9.4f", shift->dipole); else text = g_strdup(" ? "); break; case SURF_GNORM: if (shift->gnorm < 0.0) text = g_strdup(" ? "); else text = g_strdup_printf("%9.4f", shift->gnorm); break; default: continue; } if (treemodel) { gtk_tree_model_get(treemodel, iter, i, &old_text, -1); g_free(old_text); } else printf("ERROR: can't free old plane/shift data.\n"); gtk_tree_store_set(surf_tree_store, iter, i, text, -1); g_free(text); } } /**********************************************/ /* find the iterator corresponding to a plane */ /**********************************************/ gint surf_plane_iter(GtkTreeIter *iter, struct plane_pak *plane) { GtkTreeModel *treemodel; struct plane_pak *comp; /* checks */ if (!GTK_IS_TREE_VIEW(surf_tree_view)) return(0); treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(surf_tree_view)); if (!treemodel) return(0); /* are there branches on the tree? */ if (gtk_tree_model_get_iter_first(treemodel, iter)) { do { gtk_tree_model_get(treemodel, iter, SURF_PLANE, &comp, -1); if (comp == plane) return(1); } while (gtk_tree_model_iter_next(treemodel, iter)); } return(0); } /**********************************************/ /* find the iterator corresponding to a shift */ /**********************************************/ gint surf_shift_iter(GtkTreeIter *iter, struct shift_pak *shift) { gint n; GtkTreeModel *treemodel; GtkTreeIter parent; struct shift_pak *data; /* checks */ if (!GTK_IS_TREE_VIEW(surf_tree_view)) return(0); treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(surf_tree_view)); if (!treemodel) return(0); /* are there branches on the tree? */ if (gtk_tree_model_get_iter_first(treemodel, &parent)) { /* loop over all parents */ do { /* loop over all children */ n=0; while (gtk_tree_model_iter_nth_child(treemodel, iter, &parent, n)) { gtk_tree_model_get(treemodel, iter, SURF_SHIFT, &data, -1); g_assert(data != NULL); if (data == shift) return(1); n++; } } while (gtk_tree_model_iter_next(treemodel, &parent)); } return(0); } /******************************************************/ /* remove all shifts listed in the tree under a plane */ /******************************************************/ void surf_prune_shifts(struct plane_pak *plane) { gint n; GtkTreeIter child, parent; GtkTreeModel *treemodel; g_assert(plane != NULL); /* find parent level iterator */ if (!surf_plane_iter(&parent, plane)) return; treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(surf_tree_view)); if (!treemodel) return; n = gtk_tree_model_iter_n_children(treemodel, &parent) - 1; while (n >= 0) { if (gtk_tree_model_iter_nth_child(treemodel, &child, &parent, n)) { gtk_tree_store_remove(surf_tree_store, &child); } n--; } } /***********************************************/ /* search and update a shift's treeview values */ /***********************************************/ void surf_shift_refresh(struct shift_pak *shift) { GtkTreeIter iter; if (surf_shift_iter(&iter, shift)) fn_surf_set_shift(&iter, shift); } /***********************************************/ /* search and update a plane's treeview values */ /***********************************************/ void surf_plane_refresh(struct plane_pak *plane) { } /*********************************/ /* add a shift to the tree store */ /*********************************/ void fn_graft_shift(GtkTreeIter *parent, struct shift_pak *shift, struct plane_pak *plane, struct model_pak *model) { gchar *title, *dipole; GtkTreeIter child; g_assert(shift != NULL); title = g_strdup_printf("%6.4f", shift->shift); if (shift->dipole_computed) dipole = g_strdup_printf("%9.4f", shift->dipole); else dipole = g_strdup(" ? "); /* set child iterator data */ gtk_tree_store_append(surf_tree_store, &child, parent); gtk_tree_store_set(surf_tree_store, &child, SURF_TITLE, title, SURF_DIPOLE, dipole, SURF_MODEL, model, SURF_PLANE, plane, SURF_SHIFT, shift, -1); g_free(title); g_free(dipole); /* set shift values */ fn_surf_set_shift(&child, shift); } /******************************************/ /* add a list of shifts to the tree store */ /******************************************/ void fn_graft_shift_list(struct plane_pak *plane, struct model_pak *model) { GSList *list; GtkTreeIter parent; struct shift_pak *shift; g_assert(plane != NULL); /* acquire parent level iterator */ if (!surf_plane_iter(&parent, plane)) gtk_tree_store_append(surf_tree_store, &parent, NULL); /* loop over all shifts */ for (list=plane->shifts ; list ; list=g_slist_next(list)) { shift = list->data; fn_graft_shift(&parent, shift, plane, model); } } /*********************************/ /* add a plane to the tree store */ /*********************************/ void fn_graft_plane(struct plane_pak *plane, struct model_pak *model) { gchar *title, *depth; GtkTreeIter root; GtkTreeModel *treemodel; GtkTreePath *treepath; g_assert(model != NULL); g_assert(plane != NULL); /* acquire parent level iterator */ if (!surf_plane_iter(&root, plane)) gtk_tree_store_append(surf_tree_store, &root, NULL); /* set the parent iterator data */ title = g_strdup_printf("(%d%d%d)",plane->index[0],plane->index[1],plane->index[2]); depth = g_strdup_printf("%f", plane->dhkl); gtk_tree_store_set(surf_tree_store, &root, SURF_TITLE, title, SURF_REGIONS, depth, SURF_MODEL, model, SURF_PLANE, plane, SURF_SHIFT, NULL, -1); g_free(title); g_free(depth); /* printf("Adding model: %p, plane: %p\n", model, plane); */ fn_graft_shift_list(plane, model); /* get treepath of root */ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(surf_tree_view)); if (treemodel) { treepath = gtk_tree_model_get_path(treemodel, &root); gtk_tree_view_expand_row(GTK_TREE_VIEW(surf_tree_view), treepath, TRUE); gtk_tree_path_free(treepath); } } /******************************************/ /* add a list of planes to the tree store */ /******************************************/ void fn_graft_plane_list(GSList *plist, struct model_pak *model) { GSList *list; struct plane_pak *plane; /* go through the plane list */ for (list=plist ; list ; list=g_slist_next(list)) { plane = list->data; if (plane->primary) fn_graft_plane(plane, model); } } /*********************/ /* add shift to list */ /*********************/ gint fn_add_plane_with_shift(void) { GtkTreeIter iter; struct model_pak *model, surf; struct plane_pak *plane; struct shift_pak *shift; /* checks */ model = surfdata.surface.model; if (!model) return(FALSE); if (model->id == MORPH) return(FALSE); /* P3VEC("Adding: ", surfdata.surface.miller); */ /* create new shift with current region values */ shift = shift_new(surfdata.surface.shift); shift->region[0] = surfdata.surface.region[0]; shift->region[1] = surfdata.surface.region[1]; /* init for dipole calculation */ model_init(&surf); surf.surface.shift = shift->shift; surf.surface.region[0] = 1; surf.surface.region[1] = 0; /* does the plane already exist? */ plane = plane_find(surfdata.surface.miller, model); if (plane) { ARR3SET(surf.surface.miller, plane->index); generate_surface(model, &surf); shift->dipole = surf.gulp.sdipole; shift->dipole_computed = TRUE; plane->shifts = g_slist_append(plane->shifts, shift); /* add to shift to treestore */ if (surf_plane_iter(&iter, plane)) fn_graft_shift(&iter, shift, plane, model); else printf("FIXME NOW - need to create plane iter here.\n"); } else { /* create new plane with current hkl */ plane = plane_new(surfdata.surface.miller, model); if (plane) { ARR3SET(surf.surface.miller, plane->index); generate_surface(model, &surf); shift->dipole = surf.gulp.sdipole; shift->dipole_computed = TRUE; plane->shifts = g_slist_append(plane->shifts, shift); /* append new plane to model */ model->planes = g_slist_append(model->planes, plane); /* add to whole plane to treestore */ fn_graft_plane(plane, model); } } model_free(&surf); return(TRUE); } /**********************************************/ /* search for valid shifts - output to a file */ /**********************************************/ gint fn_add_valid_shifts(void) { struct model_pak *model; struct surface_pak *surfdat = &surfdata.surface; struct plane_pak *plane; /* checks */ model = surfdat->model; g_return_val_if_fail(model != NULL, 1); if (model->periodic != 3) { gui_text_show(ERROR, "Base model is not 3D periodic.\n"); return(1); } if (!surfdata.surface.miller[0] && !surfdata.surface.miller[1] && !surfdata.surface.miller[2]) { gui_text_show(ERROR, "Don't be silly.\n"); return(2); } /* does the plane already exist? */ plane = plane_find(surfdata.surface.miller, model); if (plane) surf_prune_shifts(plane); else { plane = plane_new(surfdata.surface.miller, model); /* add to list */ if (plane) model->planes = g_slist_append(model->planes, plane); else return(3); } /* find valid shifts for the plane */ /* NB: this REPLACES existing shifts */ calc_valid_shifts(model, plane); /* update treestore */ fn_graft_plane(plane, model); return(FALSE); } /********************************************************/ /* scan a plane's shift list for the best energy values */ /********************************************************/ #define DEBUG_UPDATE_PLANE_ENERGY 0 void update_plane_energy(struct plane_pak *plane, struct model_pak *model) { gint i; GSList *list; struct plane_pak *plane2; struct shift_pak *sdata; /* checks */ g_assert(model != NULL); g_assert(plane != NULL); #if DEBUG_UPDATE_PLANE_ENERGY printf("(%d %d %d)\n", plane->index[0], plane->index[1], plane->index[2]); #endif /* set best values to those of the 1st shift */ list = plane->shifts; if (list) { sdata = list->data; /* init the plane's best values */ for (i=0 ; i<2 ; i++) { plane->esurf[i] = sdata->esurf[i]; plane->esurf_shift = sdata->shift; plane->eatt[i] = sdata->eatt[i]; plane->eatt_shift = sdata->shift; plane->bbpa = sdata->bbpa; plane->bbpa_shift = sdata->shift; } } else return; /* search the shift list for better values */ while (list != NULL) { sdata = list->data; for (i=0 ; i<2 ; i++) { /* surface energy */ if (sdata->esurf[i] < plane->esurf[i]) { plane->esurf[i] = sdata->esurf[i]; plane->esurf_shift = sdata->shift; } /* attachment energy */ if (sdata->eatt[i] > plane->eatt[i]) { plane->eatt[i] = sdata->eatt[i]; plane->eatt_shift = sdata->shift; } } /* broken bonds */ if (sdata->bbpa < plane->bbpa) { plane->bbpa = sdata->bbpa; plane->bbpa_shift = sdata->shift; } list = g_slist_next(list); } #if DEBUG_UPDATE_PLANE_ENERGY printf(" %f\n", plane->bbpa); #endif /* update symmetry related planes */ if (plane->primary) { for (list=model->planes ; list ; list=g_slist_next(list)) { plane2 = list->data; if (plane2->parent == plane) { plane2->esurf[0] = plane->esurf[0]; plane2->esurf[1] = plane->esurf[1]; plane2->eatt[0] = plane->eatt[0]; plane2->eatt[1] = plane->eatt[1]; } } } } /*************************/ /* construct the surface */ /*************************/ #define MAKE_SURFACE_DEBUG 0 gpointer make_surface(struct model_pak *data, struct plane_pak *pdata, struct shift_pak *sdata) { gint bmode, r1size; gdouble sbe; GSList *list; struct model_pak *surf; struct core_pak *core; /* checks */ g_assert(data != NULL); if (data->periodic != 3) { gui_text_show(ERROR, "base model is not 3D periodic.\n"); return(NULL); } if (!pdata->index[0] && !pdata->index[1] && !pdata->index[2]) { gui_text_show(ERROR, "Don't be silly.\n"); return(NULL); } #if MAKE_SURFACE_DEBUG printf("Generating surface for model: %d\n", source); #endif /* ignore mols when building surface? */ bmode = data->build_molecules; if (data->surface.ignore_bonding) { data->build_molecules = FALSE; connect_bonds(data); connect_molecules(data); } /* allocate & init for surface data */ surf = model_new(); /* NEW - label it as MARVIN, so it's build mode follows the */ /* source model, rather than the GULP setup data - see model_prep() */ surf->id = MARVIN; /* transfer appropriate GULP setup data to new model */ gulp_data_copy(data, surf); surf->gulp.run = E_SINGLE; surf->gulp.method = CONV; /* transfer surface data to new model */ /* NB: memcpy would produce undesired effects if pointers were */ /* later added to the surface struct */ ARR3SET(surf->surface.miller, pdata->index); surf->surface.shift = sdata->shift; surf->surface.region[0] = sdata->region[0]; surf->surface.region[1] = sdata->region[1]; /* setup for region display */ if (surf->surface.region[0]) surf->region_empty[REGION1A] = FALSE; if (surf->surface.region[1]) surf->region_empty[REGION2A] = FALSE; surf->build_molecules = bmode; #if MAKE_SURFACE_DEBUG printf("Miller (%d,%d,%d)\n",surf->surface.miller[0] ,surf->surface.miller[1] ,surf->surface.miller[2]); printf("Shift %f\n",surf->surface.shift); printf("Region sizes (%d,%d)\n",surf->surface.region[0] ,surf->surface.region[1]); #endif /* TODO - valid shift/region size loop??? */ generate_surface(data, surf); gulp_files_init(surf); /* coords_init(INIT_COORDS, surf); */ /* restore connectivity in source model */ if (data->surface.ignore_bonding) { data->build_molecules = bmode; connect_bonds(data); connect_molecules(data); } /* calculate the bulk energy needed for GULP surface calcs */ sbe = data->gulp.energy; if (fabs(sbe) < FRACTION_TOLERANCE) { gui_text_show(WARNING, "Suspicious total energy. Has it been properly calculated?\n"); } /* calc the number of region 1 atoms */ r1size=0; for (list=surf->cores ; list ; list=g_slist_next(list)) { core = list->data; if (core->region == REGION1A) r1size++; } sbe /= data->num_atoms; /* E per atom in unit cell */ sbe *= r1size; /* E scaled to region 1 size */ surf->gulp.sbulkenergy = sbe; /* employ src file? */ tree_model_add(surf); tree_select_model(surf); return(surf); } /*************/ /* rank cuts */ /*************/ gint zsort(gdouble *z1, gdouble *z2) { if (*z1 > *z2) return(1); if (*z2 > *z1) return(-1); return(0); } /***************************************/ /* smallest common multiple for a pair */ /***************************************/ gint scf(gint a, gint b) { if (!b) return(abs(a)); return(scf(b, a%b)); } /***************************************/ /* smallest common multiple for vector */ /***************************************/ gint simplify(gint *vec, gint size) { gint i, n; n = scf(*vec, *(vec+1)); for (i=2 ; ibasename); /* store the planes */ write_gmf(filename, data); /* read morphology file in & compute hull */ data = model_new(); /* default morphology computation type */ data->morph_type = DHKL; /* only successful if properly loaded */ status = 2; if (data) { status--; if (!read_gmf(filename, data)) status--; } if (!status) { tree_model_add(data); /* tree_select(data->number); */ } g_free(filename); } /* TODO - rewrite to generate specified number of faces (rank_spin) */ /* based on Dhkl ranking - maybe! - calc valid shifts for each */ gint dhkl_compare(gpointer ptr1, gpointer ptr2) { struct plane_pak *plane1=ptr1, *plane2=ptr2; if (plane1->dhkl > plane2->dhkl) return(-1); if (plane1->dhkl < plane2->dhkl) return(1); return(0); } /******************************************************/ /* adds ranked faces (from data) to the supplied list */ /******************************************************/ /* if num -> get num ranked faces, else get until Dhkl < min */ #define DEBUG_GET_RANKED_FACES 0 GSList *get_ranked_faces(gint num, gdouble min, struct model_pak *data) { gint n, h, k, l, limit; gint f1[3], f2[3]; gdouble m[3]; GSList *list=NULL, *plist=NULL, *list1=NULL, *list2=NULL; struct plane_pak *pdata; /* loop over a large range */ /* FIXME - should loop until num/max is satisfied */ limit = 4; for (h=limit ; h>=-limit ; h--) { for (k=limit ; k>=-limit ; k--) { for (l=limit ; l>=-limit ; l--) { /* skip (000) */ if (!h && !k && !l) continue; VEC3SET(f1, h, k, l); /* skip things with a common multiple > 1, since */ /* fn_make_plane() takes care of systematic absences */ /* NB: this is ok for morph pred. but for XRD we may want such planes */ if (num) { n = simplify(f1, 3); if (n != 1) continue; } /* add the plane */ ARR3SET(m, f1); pdata = plane_new(m, data); if (pdata) plist = g_slist_prepend(plist, pdata); } } } plist = g_slist_reverse(plist); /* sort via Dhkl */ plist = g_slist_sort(plist, (gpointer) dhkl_compare); /* VEC3SET(f1, 1, 0, -4); printf("f1 : [%d %d %d]\n", f1[0], f1[1], f1[2]); n = simplify(f1, 3); printf("n = %d\n", n); */ /* mark the planes that are symmetry related */ list1 = plist; while (list1 != NULL) { /* get a reference (primary) plane */ pdata = list1->data; if (pdata->primary) { ARR3SET(f1, pdata->index); } else { list1 = g_slist_next(list1); continue; } /* scan for symmetry related planes */ list2 = g_slist_next(list1); while (list2 != NULL) { pdata = list2->data; ARR3SET(f2, pdata->index); /* get the hkl's and test */ #if DEBUG_GET_RANKED_FACES printf("testing: %d %d %d & %d %d %d\n", f1[0], f1[1], f1[2], f2[0], f2[1], f2[2]); #endif if (facet_equiv(data,f1,f2)) pdata->primary = FALSE; list2 = g_slist_next(list2); } list1 = g_slist_next(list1); } /* write the planes list to the model */ /* write only up to the requested number */ list1 = plist; n=0; while (list1 != NULL) { /* get a reference (primary) plane */ pdata = list1->data; /* if (pdata->primary) */ { /* automatically add valid shifts */ calc_valid_shifts(data, pdata); /* default single shift */ /* sdata = create_shift(0.0); pdata->shifts = g_slist_append(pdata->shifts, sdata); */ /* add plane */ /* TODO - don't prepend? (may be others already there) */ /* list = g_slist_prepend(list, pdata); */ /* number of unique planes */ if (pdata->primary) n++; if (num) { /* got enough unique planes? */ /* if (n == num) */ if (n > num) break; else list = g_slist_prepend(list, pdata); } else { /* if (pdata->dhkl < min) break; */ if (pdata->dhkl > min) list = g_slist_prepend(list, pdata); } } list1 = g_slist_next(list1); } list = g_slist_reverse(list); return(list); } /**********************/ /* GULP debugging aid */ /**********************/ void gulp_dump(struct model_pak *data) { printf(" num atoms = %d\n", data->num_atoms); printf(" sbe = %f\n", data->gulp.sbulkenergy); printf(" energy = %f\n", data->gulp.energy); printf("E surf (un) = %f\n", data->gulp.esurf[0]); printf("E surf (re) = %f\n", data->gulp.esurf[1]); printf(" E att (un) = %f\n", data->gulp.eatt[0]); printf(" E att (re) = %f\n", data->gulp.eatt[1]); } /**************************************************/ /* eliminate repeated z values in a (sorted) list */ /**************************************************/ GSList *trim_zlist(GSList *zlist) { gdouble dz, *z1, *z2; GSList *list, *rlist; /* get 1st item */ list = zlist; if (list) { z1 = (gdouble *) list->data; list = g_slist_next(list); } else return(NULL); /* compare and eliminate repeats */ rlist = zlist; while (list) { z2 = (gdouble *) list->data; list = g_slist_next(list); dz = fabs(*z1 - *z2); if (dz < FRACTION_TOLERANCE) { rlist = g_slist_remove(rlist, z2); g_free(z2); } else z1 = z2; } return(rlist); } /*********************************************/ /* append a list of valid shifts for a plane */ /*********************************************/ #define DEBUG_CALC_SHIFTS 0 gint calc_valid_shifts(struct model_pak *data, struct plane_pak *pdata) { gint build, num_cuts, num_shifts, dummy[3]; gdouble gcd, zlim, *z1, *z2, vec[3]; GSList *slist, *list, *zlist; struct model_pak *surf; struct shift_pak *sdata; struct core_pak *core; struct mol_pak *mol; /* allocate & template */ surf = g_malloc(sizeof(struct model_pak)); model_init(surf); surf->mode = FREE; surf->id = GULP; surf->periodic = 2; /* actual surface we want constructed */ ARR3SET(surf->surface.miller, pdata->index); surf->surface.shift = 0.0; surf->surface.region[0] = 1.0; surf->surface.region[1] = 0.0; gcd = GCD(pdata->index[0], GCD(pdata->index[1], pdata->index[2])); #if DEBUG_CALC_SHIFTS printf("*** calc shifts ***\n"); printf("miller: %f %f %f\n",surf->surface.miller[0] ,surf->surface.miller[1] ,surf->surface.miller[2]); printf("region: %d %d\n", (gint) surf->surface.region[0], (gint) surf->surface.region[1]); printf(" gcd: %f\n", gcd); printf(" Shift: %f\n", surf->surface.shift); #endif generate_surface(data, surf); if (surf->surface.dspacing == 0.0) { printf("calc_valid_shifts() error, invalid dspacing.\n"); goto calc_valid_shifts_cleanup; } #if DEBUG_CALC_SHIFTS printf(" Dhkl: %f\n", surf->surface.dspacing); #endif /* transfer appropriate setup data to new model */ gulp_data_copy(data, surf); surf->gulp.run = E_SINGLE; surf->gulp.method = CONV; /* NEW */ pdata->area = surf->area; /* only sample one dhkl's worth of cuts */ zlim = G_MINDOUBLE - 1.0 / gcd; /* get all possible shifts from z coordinates */ zlist = NULL; /* z1 = g_malloc(sizeof(gdouble)); *z1 = 0.0; zlist = g_slist_prepend(zlist, z1); */ if (data->surface.ignore_bonding) { #if DEBUG_CALC_SHIFTS printf("Ignoring bonding...\n"); #endif /* core z coord */ for (list=surf->cores ; list ; list=g_slist_next(list)) { core = list->data; ARR3SET(vec, core->x); /* helps get around some precision problems */ vec[2] = decimal_round(vec[2], 6); /* grab one slice [0, -1.0) -> [0, -Dhkl) */ if (vec[2] < zlim) continue; if (vec[2] > 0.0) continue; /* NB: cartesian z direction is opposite to shift value sign */ z1 = g_malloc(sizeof(gdouble)); *z1 = -vec[2]; fractional_clamp(z1, dummy, 1); zlist = g_slist_prepend(zlist, z1); } } else { #if DEBUG_CALC_SHIFTS printf("Using molecule centroids... [%d]\n", g_slist_length(surf->moles)); #endif /* molecule centroid */ for (list=surf->moles ; list ; list=g_slist_next(list)) { mol = list->data; ARR3SET(vec, mol->centroid); /* helps get around some precision problems */ vec[2] = decimal_round(vec[2], 6); /* grab one slice [0, -1.0) -> [0, -Dhkl) */ if (vec[2] < zlim) continue; if (vec[2] > 0.0) continue; /* NB: cartesian z direction is opposite to shift value sign */ z1 = g_malloc(sizeof(gdouble)); *z1 = -vec[2]; fractional_clamp(z1, dummy, 1); zlist = g_slist_prepend(zlist, z1); } } zlist = g_slist_sort(zlist, (gpointer) zsort); num_cuts = g_slist_length(zlist); /* NEW - some pathalogical cases have shift values just above 0.0 */ /* resulting in no cuts - so enforce at least one */ if (!num_cuts) { z1 = g_malloc(sizeof(gdouble)); *z1 = 0.0; zlist = g_slist_prepend(zlist, z1); num_cuts = 1; } #if DEBUG_CALC_SHIFTS printf("Found %d shifts.\n", num_cuts); for (list=zlist ; list ; list=g_slist_next(list)) { z1 = (gdouble *) list->data; printf(" - %.20f\n", *z1); } printf("\n"); #endif zlist = trim_zlist(zlist); num_cuts = g_slist_length(zlist); #if DEBUG_CALC_SHIFTS printf("Found %d unique shifts.\n", num_cuts); for (list=zlist ; list ; list=g_slist_next(list)) { z1 = (gdouble *) list->data; printf("%.4f ", *z1); } printf("\n"); #endif /* failsafe */ if (!zlist) goto calc_valid_shifts_cleanup; /* use midpoints to avoid cutoff problems (also neater shift values) */ /* also, the unchanged first cut will be forced to 0.0 */ zlist = g_slist_reverse(zlist); z1 = zlist->data; list = g_slist_next(zlist); while (list) { z2 = list->data; *z1 = 0.5*(*z1 + *z2); z1 = z2; list = g_slist_next(list); } *z1 = 0.0; #if DEBUG_CALC_SHIFTS printf("Midpoint equivalent cuts:\n"); for (list=zlist ; list ; list=g_slist_next(list)) { z1 = (gdouble *) list->data; printf(" %.20f", *z1); } printf("\n"); #endif /* re-rank */ zlist = g_slist_sort(zlist, (gpointer) zsort); #if DEBUG_CALC_SHIFTS printf("Ranked shifts: %d\n", num_cuts); for (list=zlist ; list ; list=g_slist_next(list)) { z1 = (gdouble *) list->data; printf(" %f", *z1); } printf("\n"); #endif zlist = trim_zlist(zlist); num_shifts = g_slist_length(zlist); #if DEBUG_CALC_SHIFTS printf("Unique shifts: %d\n", num_shifts); for (list=zlist ; list ; list=g_slist_next(list)) { z1 = (gdouble *) list->data; printf(" %f", *z1); } printf("\n"); #endif /* turn bonding off */ build = data->build_molecules; if (data->surface.ignore_bonding && build) { data->build_molecules = FALSE; connect_bonds(data); connect_molecules(data); } /* generate & check each one */ /* NB: this'll overwrite our 1st model, saving having to delete it */ slist = NULL; for (list=zlist ; list ; list=g_slist_next(list)) { z1 = (gdouble *) list->data; /* destroy surfs core/shell lists */ free_core_list(surf); /* make the surface (also computes dipole) */ surf->surface.shift = *z1; generate_surface(data, surf); #if DEBUG_CALC_SHIFTS printf("Shift = %f, dipole = %f, bonds broken = %d / %f\n", *z1, surf->gulp.sdipole, surf->surface.bonds_cut, surf->area); #endif /* create new model if valid cut (unless user wants all cuts) */ if (fabs(surf->gulp.sdipole) < data->surface.dipole_tolerance || data->surface.include_polar) { sdata = shift_new(*z1); sdata->dipole = surf->gulp.sdipole; sdata->dipole_computed = TRUE; /* NEW - broken bonds per area calc */ sdata->bbpa = (gdouble) surf->surface.bonds_cut; sdata->bbpa /= surf->area; slist = g_slist_prepend(slist, sdata); } } /* new if shift list is non-empty, overwrite only if we found something */ if (slist) { slist = g_slist_reverse(slist); if (pdata->shifts) { shift_data_free(pdata->shifts); g_slist_free(pdata->shifts); } pdata->shifts = slist; } /* restore source model's connectivity */ if (data->surface.ignore_bonding && build) { data->build_molecules = build; connect_bonds(data); connect_molecules(data); } /* this has data only if the above was successful */ free_slist(zlist); calc_valid_shifts_cleanup: /* cleanup */ model_free(surf); g_free(surf); return(0); } /***********************************/ /* computes the dipole for a plane */ /***********************************/ #define DEBUG_TEST_VALID_SHIFTS 0 void test_valid_shifts(struct plane_pak *plane, struct model_pak *data) { GSList *list; struct shift_pak *shift; struct model_pak surf; #if DEBUG_TEST_VALID_SHIFTS printf("Testing for valid shifts [%d %d %d]\n", plane->index[0], plane->index[1], plane->index[2]); #endif /* init the model to be used for generating shifts */ model_init(&surf); gulp_data_copy(data, &surf); /* test the shifts */ for (list=plane->shifts ; list ; list=g_slist_next(list)) { shift = list->data; /* init the surface we want */ ARR3SET(surf.surface.miller, plane->index); surf.surface.region[0] = 1; surf.surface.region[1] = 0; surf.surface.shift = shift->shift; /* destroy old cores & then create the surface */ free_core_list(&surf); generate_surface(data, &surf); #if DEBUG_TEST_VALID_SHIFTS printf("[%f:%f]\n", shift->shift, surf.gulp.sdipole); #endif shift->dipole = surf.gulp.sdipole; shift->dipole_computed = TRUE; } } /******************************/ /* do the energy calculations */ /******************************/ #define DEBUG_EXEC_ECALC_TASK 0 void exec_ecalc_task(struct model_pak *model, gpointer data) { gchar *inp; struct task_pak *task = data; g_assert(model != NULL); g_assert(task != NULL); /* build the full path filename */ inp = g_build_filename(sysenv.cwd, model->gulp.temp_file, NULL); if (write_gulp(inp, model)) printf("write failed.\n"); else { if (!model->gulp.no_exec) { /* NEW */ task->status_file = g_build_filename(sysenv.cwd, model->gulp.out_file, NULL); if (exec_gulp(model->gulp.temp_file, model->gulp.out_file)) printf("exec failed.\n"); } } g_free(inp); } /*********************************************/ /* process result from an energy calculation */ /*********************************************/ #define DEBUG_PROC_ECALC_TASK 0 void proc_ecalc_task(struct model_pak *model) { gchar *out; struct shift_pak *sdata; g_assert(model != NULL); /* don't process if gulp execution was turned off */ if (model->gulp.no_exec) return; /* flag that gulp read routine shouldn't prep the model */ model->grafted = TRUE; /* read gulp file */ out = g_build_filename(sysenv.cwd, model->gulp.out_file, NULL); if (read_gulp_output(out, model)) printf("proc_ecalc_task() error: read failed.\n"); g_free(out); #if DEBUG_PROC_ECALC_TASK gulp_dump(model); #else /* unlink(model->gulp.temp_file); unlink(model->gulp.out_file); printf("inp: %s\n", model->gulp.temp_file); printf("out: %s\n", model->gulp.out_file); */ #endif /* feed energy into shift */ sdata = (model->planes)->data; if (sdata) { sdata->esurf[0] = model->gulp.esurf[0]; sdata->esurf[1] = model->gulp.esurf[1]; sdata->eatt[0] = model->gulp.eatt[0]; sdata->eatt[1] = model->gulp.eatt[1]; sdata->gnorm = model->gulp.gnorm; } /* gui update */ surf_shift_refresh(sdata); /* free model */ /* NB: this was a borrowed pointer, so we don't want it freed */ model->planes = NULL; model_free(model); g_free(model); } /***********************************************/ /* submit a background energy calculation task */ /***********************************************/ void new_ecalc_task(struct model_pak *model, struct plane_pak *pdata, struct shift_pak *sdata) { gint r1size; GSList *list; struct model_pak *surf; struct core_pak *core; g_assert(model != NULL); g_assert(pdata != NULL); g_assert(sdata != NULL); /* allocate & init new model for the surface */ surf = g_malloc(sizeof(struct model_pak)); model_init(surf); gulp_data_copy(model, surf); surf->id = GULP; surf->gulp.method = CONV; /* NEW - no dependance on this */ surf->surface.model = NULL; surf->surface.shift = sdata->shift; surf->surface.region[0] = sdata->region[0]; surf->surface.region[1] = sdata->region[1]; ARR3SET(surf->surface.miller, pdata->index); /* add the cores */ generate_surface(model, surf); gulp_files_init(surf); /* compute surface bulk energy */ surf->gulp.sbulkenergy = model->gulp.energy; surf->gulp.sbulkenergy /= (gdouble) model->num_atoms; r1size=0; for (list=surf->cores ; list ; list=g_slist_next(list)) { core = list->data; if (core->region == REGION1A) r1size++; } if (r1size) surf->gulp.sbulkenergy *= (gdouble) r1size; else printf("Warning: empty region 1.\n"); /* FIXME - cheat, by tacking the shift to update on the *planes* slist */ g_assert(surf->planes == NULL); surf->planes = g_slist_append(surf->planes, sdata); task_new("Energy", &exec_ecalc_task, surf, &proc_ecalc_task, surf, NULL); } /***********************************/ /* single region convergence cycle */ /***********************************/ #define DEBUG_SURF_CONV 1 gint surf_conv(FILE *fp, struct model_pak *model, gint type) { gint i, n, flag, relax, region, r1size, status; gdouble sbe, de, old_energy; gchar *inp, *out, *full_inp, *full_out; GSList *list; struct model_pak *surf; struct core_pak *core; struct plane_pak *plane; struct shift_pak *shift; g_assert(model != NULL); /* retrieve plane & shift */ plane = (model->planes)->data; shift = (plane->shifts)->data; switch(type) { case REGION1A: region = 0; break; case REGION2A: region = 1; break; default: printf("surf_conv() error: bad region type.\n"); return(1); } /* init surface */ surf = g_malloc(sizeof(struct model_pak)); model_init(surf); gulp_data_copy(model, surf); ARR3SET(surf->surface.miller, model->surface.miller); surf->surface.region[0] = model->surface.region[0]; surf->surface.region[1] = model->surface.region[1]; surf->surface.shift = model->surface.shift; surf->surface.converge_eatt = model->surface.converge_eatt; surf->sginfo.lookup = FALSE; /* which energy to converge */ if (model->gulp.run == E_OPTIMIZE) relax = 1; else relax = 0; /* surface bulk energy per atom */ sbe = model->gulp.energy; sbe /= (gdouble) model->num_atoms; /* converge region size */ n=flag=0; old_energy = 0.0; de = 99999999.9; status = 0; for (;;) { /* FIXME - any other stuff to free? */ free_core_list(surf); generate_surface(model, surf); /* compute surface bulk energy */ r1size=0; for (list=surf->cores ; list ; list=g_slist_next(list)) { core = list->data; if (core->region == REGION1A) r1size++; } if (!r1size) printf("Warning: empty region 1.\n"); surf->gulp.sbulkenergy = sbe * (gdouble) r1size; /* create appropriate name */ inp = g_strdup_printf("%s_%d_%d.gin", surf->basename, (gint) surf->surface.region[0], (gint) surf->surface.region[1]); out = g_strdup_printf("%s_%d_%d.got", surf->basename, (gint) surf->surface.region[0], (gint) surf->surface.region[1]); full_inp = g_build_filename(sysenv.cwd, inp, NULL); full_out = g_build_filename(sysenv.cwd, out, NULL); /* command cycle: write input, execute, read output */ /* if (0) */ for (i=0 ; i<3 ; i++) { if (!status) { switch(i) { case 0: status = write_gulp(full_inp, surf); break; case 1: status = exec_gulp(inp, out); if (!status) unlink(full_inp); /* printf("input: %s\n", full_inp); */ break; case 2: /* read_gulp_out() can call gui_text_show() which modifies */ /* the message widget, hence the thread lock is required */ /* FIXME - currently, this function surf_conv() is always */ /* called in a thread but what if it isn't? ie will the */ /* threads enter call deadlock if we're in the main GTK loop? */ gdk_threads_enter(); status = read_gulp_output(full_out, surf); if (!status) unlink(full_out); /* printf("output: %s\n", full_out); */ gdk_threads_leave(); break; } } } /* remove old files for next cycle */ g_free(full_inp); g_free(full_out); g_free(inp); g_free(out); /* get difference */ if (surf->surface.converge_eatt) { de = surf->gulp.eatt[relax] - old_energy; old_energy = surf->gulp.eatt[relax]; } else { de = surf->gulp.esurf[relax] - old_energy; old_energy = surf->gulp.esurf[relax]; } /* bad convergence checks */ if (n > 10 && de > 0.1) { fprintf(fp, "ERROR: failed convergence cycle.\n"); status = 1; break; } if (surf->surface.converge_eatt) { fprintf(fp, "[%d:%d] Eatt = %f (%f)\n", (gint) surf->surface.region[0], (gint) surf->surface.region[1], surf->gulp.eatt[relax], de); } else { fprintf(fp, "[%d:%d] Esurf = %f (%f)\n", (gint) surf->surface.region[0], (gint) surf->surface.region[1], surf->gulp.esurf[relax], de); } fflush(fp); /* convergence check */ if (surf->surface.converge_eatt) { if (fabs(de) < MAX_DEEATT) break; } else { if (fabs(de) < MAX_DESURF) break; } /* next size */ surf->surface.region[region]++; n++; } if (!status) fprintf(fp, "Region %d converged.\n", region+1); fflush(fp); /* we can go back one size, due to the way convergence is checked */ surf->surface.region[region]--; /* transfer data to source model */ model->surface.region[region] = surf->surface.region[region]; model->gulp.eatt[region] = surf->gulp.eatt[region]; model->gulp.esurf[region] = surf->gulp.esurf[region]; model->gulp.gnorm = surf->gulp.gnorm; /* cleanup */ model_free(surf); g_free(surf); return(status); } /*****************************************/ /* region convergence calculation (task) */ /*****************************************/ #define DEBUG_EXEC_REGCON_TASK 0 void exec_regcon_task(struct model_pak *model, struct task_pak *task) { gint m, r, run; gchar *name, *tmp; struct plane_pak *plane; FILE *fp; /* checks */ g_assert(model != NULL); g_assert(model->planes != NULL); g_assert(model->periodic == 3); /* NEW - status file */ tmp = gun("txt"); name = g_build_filename(sysenv.cwd, tmp, NULL); g_free(tmp); fp = fopen(name, "wt"); if (fp) task->status_file = name; else { fp = stdout; g_free(name); } /* retrieve plane & shift */ plane = (model->planes)->data; /* estimate starting region sizes from the dspacing for the plane */ g_return_if_fail(plane != NULL); /* mult dhkl by the gcd */ m = GCD(plane->index[0], GCD(plane->index[1], plane->index[2])); if (plane->dhkl < MIN_THICKNESS) { /* FIXME - what to do if dhkl is very small or even zero */ r = 1 + (gint) (MIN_THICKNESS / (m*plane->dhkl)); if (r > model->surface.region[0]) model->surface.region[0] = r; if (r > model->surface.region[1]) model->surface.region[1] = r; } fprintf(fp, "----------------------------------------------\n"); fprintf(fp, " Miller: %d %d %d \n", plane->index[0] , plane->index[1] , plane->index[2]); fprintf(fp, " Dhkl: %f\n", plane->dhkl); fprintf(fp, " Shift: %f\n", model->surface.shift); fprintf(fp, "Initial regions: %d , %d\n", (gint) model->surface.region[0], (gint) model->surface.region[1]); fprintf(fp, " Convergence: "); if (model->surface.converge_eatt) fprintf(fp, "Attachment Energy based\n"); else fprintf(fp, "Surface Energy based\n"); fprintf(fp, "----------------------------------------------\n"); /* save run type */ run = model->gulp.run; fprintf(fp, "Beginning unrelaxed convergence...\n"); /* converge region 2 size */ model->gulp.run = E_SINGLE; if (model->surface.converge_r2) { if (surf_conv(fp, model, REGION2A)) { task->message = g_strdup("Failed unrelaxed convergence of region 2.\n"); return; } } else fprintf(fp, "Skipping region 2 convergence...\n"); /* converge region 1 size */ if (model->surface.converge_r1) { if (surf_conv(fp, model, REGION1A)) { task->message = g_strdup("Failed unrelaxed convergence of region 1.\n"); return; } } else fprintf(fp, "Skipping region 1 convergence...\n"); /* if opti is specified - repeat with unrelaxed regions as starting point */ if (run == E_OPTIMIZE) { model->gulp.run = E_OPTIMIZE; fprintf(fp, "Beginning relaxed convergence...\n"); fflush(fp); /* converge region 2 size */ if (model->surface.converge_r2) { if (surf_conv(fp, model, REGION2A)) { if (task) task->message = g_strdup("Failed relaxed convergence of region 2.\n"); return; } fflush(fp); } else fprintf(fp, "Skipping region 2 convergence...\n"); /* converge region 1 size */ if (model->surface.converge_r1) { if (surf_conv(fp, model, REGION1A)) { if (task) task->message = g_strdup("Failed relaxed convergence of region 1.\n"); return; } fflush(fp); } else printf("Skipping region 1 convergence...\n"); } fclose(fp); } /**************************************/ /* region convergence task processing */ /**************************************/ #define DEBUG_PROC_REGON_TASK 0 void proc_regcon_task(struct model_pak *model) { struct plane_pak *plane; struct shift_pak *shift; /* checks */ g_assert(model != NULL); plane = (model->planes)->data; g_assert(plane != NULL); shift = (plane->shifts)->data; g_assert(shift != NULL); /* the shift pointer belongs to the main model, so */ /* we don't want it free'd when model is destroyed */ plane->shifts = NULL; #if DEBUG_PROC_REGON_TASK printf("Final regions: %d , %d\n", (gint) model->surface.region[0], (gint) model->surface.region[1]); printf("Final Esurf: %f , %f\n", model->gulp.esurf[0], model->gulp.esurf[1]); printf("Final Eatt: %f , %f\n", model->gulp.eatt[0], model->gulp.eatt[1]); printf("Final gnorm: %f\n", model->gulp.gnorm); #endif shift->region[0] = model->surface.region[0]; shift->region[1] = model->surface.region[1]; shift->esurf[0] = model->gulp.esurf[0]; shift->esurf[1] = model->gulp.esurf[1]; shift->eatt[0] = model->gulp.eatt[0]; shift->eatt[1] = model->gulp.eatt[1]; shift->gnorm = model->gulp.gnorm; /* update gui */ surf_shift_refresh(shift); shift->locked = FALSE; /* cleanup */ model_free(model); g_free(model); } /*********************************/ /* region convergence task setup */ /*********************************/ void new_regcon_task(struct model_pak *model, struct plane_pak *plane, struct shift_pak *shift) { GSList *list; struct core_pak *core; struct plane_pak *plane2; struct model_pak *temp; /* checks */ g_assert(model != NULL); g_assert(plane != NULL); g_assert(shift != NULL); /* NEW - prevent deletion */ shift->locked = TRUE; /* duplicate the data passed, since it may be changed */ /* or destroyed by the user while the task is still queued */ temp = g_malloc(sizeof(struct model_pak)); model_init(temp); /* duplicate source model data */ temp->gulp.energy = model->gulp.energy; gulp_data_copy(model, temp); temp->cores = dup_core_list(model->cores); /* FIXME - this is ugly, but the only way (curently) to do it, */ /* as dup_core_list dups shells - but doesn't add them to a list */ for (list=temp->cores ; list ; list=g_slist_next(list)) { core = list->data; if (core->shell) temp->shels = g_slist_prepend(temp->shels, core->shell); } /* TODO - implement a copy_lattice_info() primitive */ temp->periodic = model->periodic; temp->fractional = model->fractional; memcpy(temp->pbc, model->pbc, 6 * sizeof(gdouble)); memcpy(temp->latmat, model->latmat, 9 * sizeof(gdouble)); memcpy(temp->ilatmat, model->ilatmat, 9 * sizeof(gdouble)); temp->sginfo.spacename = g_strdup(model->sginfo.spacename); temp->sginfo.cellchoice = model->sginfo.cellchoice; /* always use name for lookup */ temp->sginfo.spacenum = -1; if (model->surface.ignore_bonding) temp->build_molecules = FALSE; /* TODO - enforce unfragment() ??? */ model_prep(temp); /* temp->fractional = TRUE; zone_init(temp); connect_bonds(temp); connect_molecules(temp); */ /* NEW - no dependence once we've spawned the task */ temp->surface.converge_eatt = model->surface.converge_eatt; temp->surface.model = NULL; temp->surface.shift = shift->shift; temp->surface.region[0] = shift->region[0]; temp->surface.region[1] = shift->region[1]; ARR3SET(temp->surface.miller, plane->index); /* append the required plane and shift to the temporary model */ plane2 = plane_new(temp->surface.miller, model); plane2->shifts = g_slist_append(plane2->shifts, shift); g_assert(temp->planes == NULL); temp->planes = g_slist_append(temp->planes, plane2); task_new("Regcon", &exec_regcon_task, temp, &proc_regcon_task, temp, NULL); } /*************************/ /* surface creation task */ /*************************/ void cb_surf_create(GtkWidget *w, struct model_pak *model) { struct plane_pak *plane; struct shift_pak *shift; /* setup plane */ plane = plane_new(surfdata.surface.miller, model); g_return_if_fail(plane != NULL); /* setup shift */ shift = shift_new(surfdata.surface.shift); shift->region[0] = surfdata.surface.region[0]; shift->region[1] = surfdata.surface.region[1]; /* create */ make_surface(model, plane, shift); /* avoid BSOD */ coords_init(INIT_COORDS, model); redraw_canvas(SINGLE); g_free(plane); g_free(shift); } /*****************************************/ /* act on all selected tasks in the list */ /*****************************************/ void surf_task_selected(GtkWidget *w, gint type) { gint refresh=FALSE; GList *list, *row; GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; struct model_pak *model; struct plane_pak *plane; struct shift_pak *shift; /* checks */ treemodel = GTK_TREE_MODEL(surf_tree_store); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(surf_tree_view)); if (!selection || !treemodel) return; /* acquire a list of selected rows */ list = gtk_tree_selection_get_selected_rows(selection, &treemodel); for (row=list ; row ; row=g_list_next(row)) { if (gtk_tree_model_get_iter(treemodel, &iter, row->data)) { gtk_tree_model_get(treemodel, &iter, SURF_MODEL, &model, SURF_PLANE, &plane, SURF_SHIFT, &shift, -1); /* perform the required operation */ switch (type) { case CALC_SHIFTS: if (plane && !shift) { surf_prune_shifts(plane); calc_valid_shifts(model, plane); fn_graft_plane(plane, model); } break; case CALC_ENERGY: if (shift) new_ecalc_task(model, plane, shift); break; case CONV_REGIONS: if (shift) new_regcon_task(model, plane, shift); break; case MAKE_FACES: if (shift) make_surface(model, plane, shift); refresh = TRUE; break; } } } g_list_foreach(list, (gpointer) gtk_tree_path_free, NULL); g_list_free(list); if (refresh) redraw_canvas(SINGLE); } /****************************************/ /* save the current surface calculation */ /****************************************/ void export_planes(gchar *name) { struct model_pak *data; gchar *filename, *a, *b; data = surfdata.surface.model; if (!data) return; /* process filename to force .gmf extension */ a = strdup_basename(name); b = g_strconcat(a, ".gmf", NULL); filename = g_build_filename(sysenv.cwd, b, NULL); write_gmf(filename, data); dialog_destroy_type(FILE_SELECT); g_free(filename); g_free(b); g_free(a); } /******************************************/ /* apply a new morphology type to a model */ /******************************************/ void change_morph_type(struct model_pak *data, gint type) { gpointer camera; g_return_if_fail(data != NULL); data->morph_type = type; /* save camera */ camera = camera_dup(data->camera); morph_build(data); model_prep(data); coords_init(REDO_COORDS, data); /* rescale & restore camera */ camera_rescale(data->rmax, camera); camera_copy(data->camera, camera); g_free(camera); redraw_canvas(SINGLE); } /*****************************/ /* delete a shift in a plane */ /*****************************/ void surf_shift_delete(struct shift_pak *shift, struct plane_pak *plane) { g_assert(shift != NULL); g_assert(plane != NULL); plane->shifts = g_slist_remove(plane->shifts, shift); shift_free(shift); } /*******************************************/ /* delete a plane and symmetry equivalents */ /*******************************************/ void surf_plane_delete(struct plane_pak *plane, struct model_pak *data) { gint n=1; GSList *list; struct plane_pak *pcomp; g_assert(plane != NULL); g_assert(data != NULL); /* remove equivalents */ list = data->planes; while (list) { pcomp = list->data; list = g_slist_next(list); /* NB: don't delete the reference plane until the end */ if (pcomp == plane) continue; if (facet_equiv(data, plane->index, pcomp->index)) { data->planes = g_slist_remove(data->planes, pcomp); g_free(pcomp); n++; } } /* remove main plane */ data->planes = g_slist_remove(data->planes, plane); plane_free(plane); } /***************************/ /* shift deletion callback */ /***************************/ void surf_prune_selected(void) { GList *list, *row; GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; struct model_pak *model=NULL; struct plane_pak *plane=NULL; struct shift_pak *shift=NULL; /* checks */ treemodel = GTK_TREE_MODEL(surf_tree_store); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(surf_tree_view)); if (!selection || !treemodel) return; /* delete all selected shifts */ /* reverse enumeration so the tree store doesn't change when an iter is removed */ list = gtk_tree_selection_get_selected_rows(selection, &treemodel); for (row=g_list_last(list) ; row ; row=g_list_previous(row)) { if (gtk_tree_model_get_iter(treemodel, &iter, row->data)) { gtk_tree_model_get(treemodel, &iter, SURF_MODEL, &model, SURF_SHIFT, &shift, SURF_PLANE, &plane, -1); if (plane) { if (shift) { gtk_tree_store_remove(surf_tree_store, &iter); surf_shift_delete(shift, plane); } else { gtk_tree_store_remove(surf_tree_store, &iter); surf_plane_delete(plane, model); } } } } if (model) if (model->id == MORPH) change_morph_type(model, model->morph_type); g_list_foreach(list, (gpointer) gtk_tree_path_free, NULL); g_list_free(list); } /************************/ /* free the entire list */ /************************/ void surf_prune_all(void) { struct model_pak *model=surfdata.surface.model; g_assert(model != NULL); /* clear the tree view widget */ gtk_tree_store_clear(surf_tree_store); /* free the underlying data */ plane_data_free(model->planes); g_slist_free(model->planes); model->planes = NULL; model->num_planes = 0; } /*****************************/ /* remove all invalid shifts */ /*****************************/ void surf_prune_invalid(void) { gint m, n; GtkTreeIter iter, parent; GtkTreeModel *treemodel; struct model_pak *model=surfdata.surface.model; struct plane_pak *plane=NULL; struct shift_pak *shift=NULL; g_assert(model != NULL); treemodel = GTK_TREE_MODEL(surf_tree_store); if (!treemodel) return; /* loop backwards over planes */ m = gtk_tree_model_iter_n_children(treemodel, NULL) - 1; while (m >= 0) { if (!gtk_tree_model_iter_nth_child(treemodel, &parent, NULL, m)) break; m--; gtk_tree_model_get(treemodel, &parent, SURF_PLANE, &plane, -1); /* loop backwards over shifts in current plane */ n = gtk_tree_model_iter_n_children(treemodel, &parent) - 1; while (n >= 0) { if (!gtk_tree_model_iter_nth_child(treemodel, &iter, &parent, n)) break; n--; gtk_tree_model_get(treemodel, &iter, SURF_SHIFT, &shift, -1); /* test dipole */ if (fabs(shift->dipole) >= model->surface.dipole_tolerance) { gtk_tree_store_remove(surf_tree_store, &iter); surf_shift_delete(shift, plane); } } } } /*****************************************************/ /* callbacks to change the morphology type displayed */ /*****************************************************/ void bfdh_morph(struct model_pak *data) { change_morph_type(data, DHKL); } void equn_morph(struct model_pak *data) { change_morph_type(data, EQUIL_UN); } void grun_morph(struct model_pak *data) { change_morph_type(data, GROWTH_UN); } void eqre_morph(struct model_pak *data) { change_morph_type(data, EQUIL_RE); } void grre_morph(struct model_pak *data) { change_morph_type(data, GROWTH_RE); } /***********************************/ /* shift energy value modification */ /***********************************/ void shift_commit(GtkWidget *w, gpointer dummy) { gint i, empty[4]; const gchar *text[4]; gdouble value[4]; GList *list, *row; GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; struct model_pak *model; struct plane_pak *plane; struct shift_pak *shift; /* checks */ treemodel = GTK_TREE_MODEL(surf_tree_store); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(surf_tree_view)); if (!selection || !treemodel) return; /* acquire the energy values */ for (i=4 ; i-- ; ) { text[i] = gtk_entry_get_text(GTK_ENTRY(surf_morph_energy[i])); if (strlen(text[i])) empty[i] = FALSE; else empty[i] = TRUE; value[i] = str_to_float(text[i]); } /* modify all selected rows */ list = gtk_tree_selection_get_selected_rows(selection, &treemodel); for (row=list ; row ; row=g_list_next(row)) { if (gtk_tree_model_get_iter(treemodel, &iter, row->data)) { gtk_tree_model_get(treemodel, &iter, SURF_MODEL, &model, SURF_PLANE, &plane, SURF_SHIFT, &shift, -1); if (model && plane && shift) { if (!empty[0]) shift->esurf[0] = value[0]; if (!empty[1]) shift->eatt[0] = value[1]; if (!empty[2]) shift->esurf[1] = value[2]; if (!empty[3]) shift->eatt[1] = value[3]; fn_surf_set_shift(&iter, shift); update_plane_energy(plane, model); } } } if (model) if (model->id == MORPH) change_morph_type(model, model->morph_type); g_list_foreach(list, (gpointer) gtk_tree_path_free, NULL); g_list_free(list); } /************************************************/ /* added ranked faces based on dhkl to the list */ /************************************************/ #define DEBUG_ADD_RANKED_FACES 0 void add_ranked_faces(GtkWidget *w, gpointer dummy) { struct model_pak *data; GSList *list; /* checks */ data = surfdata.surface.model; if (!data) return; if (data->periodic != 3) return; list = get_ranked_faces((gint) rank_value, 0.0, data); #if DEBUG_ADD_RANKED_FACES printf("Maximum faces: %f\n", rank_value); printf(" Added faces: %d\n", g_slist_length(list)); #endif data->planes = g_slist_concat(data->planes, list); fn_graft_plane_list(list, data); } /*****************************************/ /* import a previous surface calculation */ /*****************************************/ void import_planes(gchar *name) { gchar *filename; GSList *plist; struct model_pak *data; struct plane_pak *pdata; data = surfdata.surface.model; if (!data) return; if (data->periodic != 3) { dialog_destroy_type(FILE_SELECT); gui_text_show(ERROR, "Source structure is not 3D periodic."); return; } filename = g_build_filename(sysenv.cwd, name, NULL); /* get the planes */ if (load_planes(filename, data)) { gui_text_show(ERROR, "Bad planes file."); return; } plist = data->planes; while (plist != NULL) { pdata = plist->data; /* if shift list is empty (primary plane only!) */ /* then add the result of a calc valid shifts */ /* otherwise verify the supplied shift */ if (pdata->primary) { if (pdata->shifts) test_valid_shifts(pdata, data); else calc_valid_shifts(data, pdata); } plist = g_slist_next(plist); } /* clean up */ dialog_destroy_type(FILE_SELECT); fn_graft_plane_list(data->planes, data); g_free(filename); } /***********************************************/ /* handle import/export file selection request */ /***********************************************/ void surf_load_planes(void) { file_dialog("Load planes", NULL, FILE_LOAD, (gpointer) import_planes, MORPH); } void surf_save_planes(void) { file_dialog("Save planes", NULL, FILE_SAVE, (gpointer) export_planes, MORPH); } /**********************************/ /* tree selection change callback */ /**********************************/ void surf_selection_changed(GtkTreeSelection *selection, gpointer data) { gint i, j; gchar *text; GList *list, *row; GtkTreeIter iter, child; GtkTreePath *last; GtkTreeModel *treemodel; struct model_pak *model=NULL; struct plane_pak *plane=NULL; struct shift_pak *shift=NULL; /* checks */ treemodel = GTK_TREE_MODEL(surf_tree_store); if (!treemodel) return; /* blank the hkl family entry */ gtk_entry_set_text(GTK_ENTRY(surf_hkl_family), ""); /* only update input widget if one row selected */ list = gtk_tree_selection_get_selected_rows(selection, &treemodel); if (g_list_length(list) == 1) { if (gtk_tree_model_get_iter(treemodel, &iter, list->data)) { gtk_tree_model_get(treemodel, &iter, SURF_MODEL, &model, SURF_PLANE, &plane, SURF_SHIFT, &shift, -1); g_assert(model != NULL); /* CURRENT - print hkl family */ { gint *m; GSList *l1, *l2; GString *family; family = g_string_new(NULL); l2 = get_facet_equiv(model, plane->index); for (l1=l2 ; l1 ; l1=g_slist_next(l1)) { m = l1->data; g_string_sprintfa(family, "(%d %d %d) ", m[0], m[1], m[2]); } gtk_entry_set_text(GTK_ENTRY(surf_hkl_family), family->str); g_string_free(family, TRUE); free_slist(l2); } /* disallow parent selection? */ if (model->id == MORPH) { if (plane && shift) { j=0; for (i=SURF_ESURF_UNRE ; i<=SURF_EATT_RE ; i++) { gtk_tree_model_get(treemodel, &iter, i, &text, -1); g_strstrip(text); if (text) gtk_entry_set_text(GTK_ENTRY(surf_morph_energy[j]), text); g_free(text); j++; } } } else { if (plane) { ARR3SET(surfdata.surface.miller, plane->index); } if (shift) { surfdata.surface.shift = shift->shift; surfdata.surface.region[0] = shift->region[0]; surfdata.surface.region[1] = shift->region[1]; } /* update widgets */ gui_relation_update(model); } } } /* for each selected plane - expand row - select all shifts */ for (row=list ; row ; row=g_list_next(row)) { if (gtk_tree_model_get_iter(treemodel, &iter, row->data)) { gtk_tree_view_expand_row(GTK_TREE_VIEW(surf_tree_view), row->data, TRUE); gtk_tree_model_get(treemodel, &iter, SURF_MODEL, &model, SURF_PLANE, &plane, SURF_SHIFT, &shift, -1); /* if (and only if) row is a plane - select self and all children */ if (plane && !shift) { j = gtk_tree_model_iter_n_children(treemodel, &iter); if (j) { if (gtk_tree_model_iter_nth_child(treemodel, &child, &iter, j-1)) { last = gtk_tree_model_get_path(treemodel, &child); gtk_tree_selection_select_range(selection, row->data, last); gtk_tree_path_free(last); } } } } } g_list_foreach(list, (gpointer) gtk_tree_path_free, NULL); g_list_free(list); } /*********************/ /* collapse all rows */ /*********************/ void surf_collapse_all(void) { gtk_tree_view_collapse_all(GTK_TREE_VIEW(surf_tree_view)); } /*******************/ /* expand all rows */ /*******************/ void surf_expand_all(void) { gtk_tree_view_expand_all(GTK_TREE_VIEW(surf_tree_view)); } /*******************/ /* select all rows */ /*******************/ void surf_select_all(void) { GtkTreeSelection *selection; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(surf_tree_view)); if (!selection) return; gtk_tree_selection_select_all(selection); } /**************************/ /* morphology type change */ /**************************/ void surf_morph_type(GtkWidget *w, gpointer data) { const gchar *text; struct model_pak *model=data; g_assert(model != NULL); text = gtk_entry_get_text(GTK_ENTRY(w)); model->morph_type = DHKL; if (g_ascii_strncasecmp(text, "Esurf (u", 8) == 0) model->morph_type = EQUIL_UN; if (g_ascii_strncasecmp(text, "Esurf (r", 8) == 0) model->morph_type = EQUIL_RE; if (g_ascii_strncasecmp(text, "Eatt (u", 7) == 0) model->morph_type = GROWTH_UN; if (g_ascii_strncasecmp(text, "Eatt (r", 7) == 0) model->morph_type = GROWTH_RE; if (g_ascii_strncasecmp(text, "broken", 6) == 0) model->morph_type = MORPH_BBPA; } /*******************************/ /* shift perturbation callback */ /*******************************/ void gui_shift_perturb(GtkWidget *w, struct model_pak *model) { surf_shift_explore(model, &surfdata.surface); } /******************/ /* MENU structure */ /******************/ static GtkItemFactoryEntry surface_menu[] = { { "/File", NULL, NULL, 0, "" }, /* { "/File/Create morphology", NULL, make_morph, 1, NULL }, */ { "/File/sep1", NULL, NULL, 0, "" }, { "/File/Load planes...", NULL, surf_load_planes, 1, NULL }, { "/File/Save planes...", NULL, surf_save_planes, 1, NULL }, { "/Edit", NULL, NULL, 0, "" }, { "/Edit/Collapse all", NULL, surf_collapse_all, 1, NULL }, { "/Edit/Expand all", NULL, surf_expand_all, 1, NULL }, { "/Edit/sep1", NULL, NULL, 0, "" }, { "/Edit/Delete invalid", NULL, surf_prune_invalid, 1, NULL }, { "/Edit/Delete selected", NULL, surf_prune_selected, 1, NULL }, { "/Edit/Delete all", NULL, surf_prune_all, 1, NULL }, { "/Edit/sep1", NULL, NULL, 0, "" }, { "/Edit/Select all", NULL, surf_select_all, 1, NULL } }; /************************************/ /* surface creation/cut elimination */ /************************************/ void surface_dialog(void) { gint i, j, surface_entries; gchar *title; gpointer dialog, entry; GList *list; GtkWidget *window, *scr_win, *frame, *menu_bar; GtkWidget *hbox1, *hbox, *vbox1, *vbox2, *vbox3, *vbox; GtkWidget *label, *button, *spin; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *select; GtkItemFactory *item; struct model_pak *data; data = sysenv.active_model; /* checks */ if (!data) return; if (data->periodic != 3) { if (data->id != MORPH) { gui_text_show(ERROR, "Your model is not 3D periodic.\n"); return; } } /* new dialog */ dialog = dialog_request(GENSURF, "Surfaces", NULL, NULL, data); if (!dialog) return; window = dialog_window(dialog); if (fabs(data->gulp.energy) < FRACTION_TOLERANCE && data->id != MORPH) { gui_text_show(WARNING, "Has the total energy been calculated?\n"); } /* FIXME - what if bonding is switched off? */ data->surface.bonds_full = g_slist_length(data->bonds); /* init the current surface */ all_planes = FALSE; model_init(&surfdata); surfdata.surface.model = data; surfdata.surface.shift = data->surface.shift; surfdata.surface.region[0] = data->surface.region[0]; surfdata.surface.region[1] = data->surface.region[1]; ARR3SET(surfdata.surface.miller, data->surface.miller); if (data->id == MORPH) title = g_strdup_printf("%s morphology", data->basename); else { title = g_strdup_printf("%s surfaces", data->basename); /* enforce whole molecules */ model_colour_scheme(data->colour_scheme, data); coords_init(CENT_COORDS, data); } gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); gtk_window_set_title(GTK_WINDOW(window), title); g_free(title); /* main vbox */ vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), vbox); /* NEW - menu */ surface_entries = sizeof (surface_menu) / sizeof (surface_menu[0]); item = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", NULL); gtk_item_factory_create_items(item, surface_entries, surface_menu, NULL); menu_bar = gtk_item_factory_get_widget(item, ""); gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); /* hbox for split pane */ hbox1 = gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, TRUE, 10); if (data->id == MORPH) { /* left vbox */ vbox1 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox1), vbox1, FALSE, FALSE, PANEL_SPACING); frame = gtk_frame_new("Morphology type"); gtk_box_pack_start(GTK_BOX(vbox1),frame,FALSE,FALSE,0); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); /* display type */ /* TODO - for loop ??? */ new_radio_group(0, vbox, TT); button = add_radio_button("BFDH", (gpointer) bfdh_morph, data); if (data->morph_type == DHKL) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); button = add_radio_button("Equilibrium unrelaxed", (gpointer) equn_morph, data); if (data->morph_type == EQUIL_UN) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); button = add_radio_button("Growth unrelaxed", (gpointer) grun_morph, data); if (data->morph_type == GROWTH_UN) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); button = add_radio_button("Equilibrium relaxed", (gpointer) eqre_morph, data); if (data->morph_type == EQUIL_RE) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); button = add_radio_button("Growth relaxed", (gpointer) grre_morph, data); if (data->morph_type == GROWTH_RE) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); /* editable shift values */ frame = gtk_frame_new("Shift values"); gtk_box_pack_start(GTK_BOX(vbox1),frame,FALSE,FALSE,0); vbox = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); j=0; for (i=SURF_ESURF_UNRE ; i<=SURF_EATT_RE ; i++) { hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); switch (i) { case SURF_ESURF_UNRE: label = gtk_label_new("Esurf (unrelaxed)"); break; case SURF_EATT_UNRE: label = gtk_label_new("Eatt (unrelaxed)"); break; case SURF_ESURF_RE: label = gtk_label_new("Esurf (relaxed)"); break; case SURF_EATT_RE: default: label = gtk_label_new("Eatt (relaxed)"); break; } /* row */ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); surf_morph_energy[j] = gtk_entry_new(); gtk_box_pack_end(GTK_BOX(hbox), surf_morph_energy[j], FALSE, FALSE, 0); gtk_widget_set_size_request(surf_morph_energy[j], 9*sysenv.gtk_fontsize, -1); j++; } } else { /* left vbox */ vbox = gtk_vbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(hbox1), vbox, FALSE, FALSE, 10); frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0); vbox3 = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox3); gtk_container_set_border_width(GTK_CONTAINER(vbox3), PANEL_SPACING); /* two vboxes; one for labels, the other for the spinners */ hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox3), hbox, TRUE, FALSE, 0); vbox1 = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, FALSE, 0); vbox2 = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, FALSE, 0); /* labels */ label = gtk_label_new("Miller"); gtk_box_pack_start(GTK_BOX(vbox1), label, TRUE, FALSE, 0); label = gtk_label_new("Shift"); gtk_box_pack_start(GTK_BOX(vbox1), label, TRUE, FALSE, 0); label = gtk_label_new("Depths"); gtk_box_pack_start(GTK_BOX(vbox1), label, TRUE, FALSE, 0); /* miller */ hbox = gtk_hbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, FALSE, 0); spin = gui_direct_spin(NULL, &surfdata.surface.miller[0], -99, 99, 1, NULL, NULL, hbox); gtk_widget_set_size_request(spin, 6*sysenv.gtk_fontsize, -1); spin = gui_direct_spin(NULL, &surfdata.surface.miller[1], -99, 99, 1, NULL, NULL, hbox); gtk_widget_set_size_request(spin, 6*sysenv.gtk_fontsize, -1); spin = gui_direct_spin(NULL, &surfdata.surface.miller[2], -99, 99, 1, NULL, NULL, hbox); gtk_widget_set_size_request(spin, 6*sysenv.gtk_fontsize, -1); /* shift */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, FALSE, 0); spin = gui_direct_spin(NULL, &surfdata.surface.shift, 0.0, 1.0, 0.05, NULL, NULL, hbox); gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), 4); gtk_widget_set_size_request(spin, 6*sysenv.gtk_fontsize, -1); /* regions */ hbox = gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, FALSE, 0); gui_direct_spin(NULL, &surfdata.surface.region[0], 0, 99, 1, NULL, NULL, hbox); gui_direct_spin(NULL, &surfdata.surface.region[1], 0, 99, 1, NULL, NULL, hbox); /* create this surface */ gui_button(" Create ", cb_surf_create, (gpointer) data, hbox, TF); /* frame for adding a faces */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, PANEL_SPACING); vbox1 = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox1); gtk_container_set_border_width(GTK_CONTAINER(vbox1), PANEL_SPACING); gui_button_x("Add the current surface", fn_add_plane_with_shift, NULL, vbox1); gui_button_x("Add all valid shifts", fn_add_valid_shifts, NULL, vbox1); hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0); gui_direct_spin("Add ", &rank_value, 1, 50, 1, NULL, NULL, hbox); label = gtk_label_new(" Dhkl ranked faces "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gui_button_x(NULL, add_ranked_faces, NULL, hbox); /* CURRENT */ /* gui_button_x("Shift perturbation (exp) ", gui_shift_perturb, data, vbox1); */ /* frame for morphology construction */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, PANEL_SPACING); vbox1 = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox1); gtk_container_set_border_width(GTK_CONTAINER(vbox1), PANEL_SPACING); /* FIXME - this seems to be broken */ /* gui_button_x("Overlay morphology", surf_make_morph, data, vbox1); */ gui_button_x("Create morphology", make_morph, NULL, vbox1); hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0); spin = new_spinner("Create nuclei with size ", 1.0, 1000.0, 1.0, NULL, NULL, hbox); g_object_set_data(G_OBJECT(spin), "model", data); gui_button_x(NULL, morph_sculpt, spin, hbox); /* CURRENT */ gui_direct_check("Cleave to match shift (EXP)", &data->sculpt_shift_use, NULL, NULL, vbox1); hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0); list = NULL; list = g_list_append(list, "Dhkl"); list = g_list_append(list, "Esurf (u)"); list = g_list_append(list, "Esurf (r)"); list = g_list_append(list, "Eatt (u)"); list = g_list_append(list, "Eatt (r)"); list = g_list_append(list, "Broken bonds"); entry = gui_pulldown_new("Morphology type ", list, FALSE, hbox); g_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(surf_morph_type), data); /* frame for convergence check buttons */ frame = gtk_frame_new("Convergence"); gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox1); gtk_container_set_border_width (GTK_CONTAINER(GTK_BOX(vbox1)), PANEL_SPACING); gui_direct_check("Attachment energy based", &data->surface.converge_eatt, NULL, NULL, vbox1); gui_direct_check("Converge region 1", &data->surface.converge_r1, NULL, NULL, vbox1); gui_direct_check("Converge region 2", &data->surface.converge_r2, NULL, NULL, vbox1); /* frame for option check buttons */ frame = gtk_frame_new("Construction"); gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox1); gtk_container_set_border_width (GTK_CONTAINER(GTK_BOX(vbox1)), PANEL_SPACING); gui_direct_check("Allow polar surfaces", &data->surface.include_polar, NULL, NULL, vbox1); gui_direct_check("Allow bond cleaving", &data->surface.ignore_bonding, NULL, NULL, vbox1); gui_direct_check("Ignore model symmetry", &data->surface.ignore_symmetry, NULL, NULL, vbox1); gui_direct_check("Preserve depth periodicity", &data->surface.true_cell, NULL, NULL, vbox1); gui_direct_check("Preserve atom ordering", &data->surface.keep_atom_order, NULL, NULL, vbox1); /* misc options */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,PANEL_SPACING); vbox1 = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame), vbox1); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox1)), PANEL_SPACING); hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0); gui_direct_spin("Surface dipole cutoff ", &data->surface.dipole_tolerance, 0.001, 99.999, 0.001, NULL, NULL, hbox); } /* right vbox */ vbox1 = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox1), vbox1, TRUE, TRUE, 0); /* valid cut listing */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(vbox1),frame,TRUE,TRUE,0); vbox = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); /* scrolled model pane */ scr_win = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scr_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), scr_win, TRUE, TRUE, 0); /* planes storage */ surf_tree_store = gtk_tree_store_new(SURF_NCOLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); /* planes viewing widget */ surf_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(surf_tree_store)); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scr_win), surf_tree_view); /* set up column renderers */ for (i=0 ; i<=SURF_GNORM ; i++) { renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(titles[i], renderer, "text", i, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(surf_tree_view), column); } /* setup the selection handler */ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(surf_tree_view)); gtk_tree_selection_set_mode(select, GTK_SELECTION_MULTIPLE); g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(surf_selection_changed), NULL); fn_graft_plane_list(data->planes, data); /* CURRENT - list of hkl family planes */ surf_hkl_family = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(vbox), surf_hkl_family, FALSE, FALSE, 0); if (data->id == MORPH) { /* shift operation button row */ hbox = gtk_hbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gui_button("Commit ", shift_commit, NULL, hbox, TT); gui_button("Delete ", surf_prune_selected, data, hbox, TT); } else { hbox = gtk_hbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gui_button("Create surface", surf_task_selected, (gpointer) MAKE_FACES, hbox, TT); gui_button("Converge regions", surf_task_selected, (gpointer) CONV_REGIONS, hbox, TT); gui_button("Calculate energy", surf_task_selected, (gpointer) CALC_ENERGY, hbox, TT); gui_button("Calculation setup", gulp_dialog, data, hbox, TT); /* hbox = gtk_hbox_new(TRUE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gui_button("Find valid shifts", surf_task_selected, (gpointer) CALC_SHIFTS, hbox, TT); gui_button("Remove selected", surf_prune_selected, data, hbox, TT); gui_button("Remove invalid", surf_prune_invalid, data, hbox, TT); gui_button("Remove all", surf_prune_all, data, hbox, TT); */ } /* terminating button */ gui_stock_button(GTK_STOCK_CLOSE, dialog_destroy, dialog, GTK_DIALOG(window)->action_area); /* done */ gtk_widget_show_all(window); }