From 189da15810deabd739d7c11c6e95fea55739fe60 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sat, 1 Aug 2020 15:13:49 +0200 Subject: Initial import from archive --- src/Makefile.am | 14 + src/amplitude-r.c | 86 +++ src/amplitude-r.h | 19 + src/anneal.h | 20 + src/argand.c | 118 ++++ src/argand.h | 26 + src/cdm.c | 1202 +++++++++++++++++++++++++++++++++++++ src/cdm.h | 77 +++ src/cflip.c | 910 ++++++++++++++++++++++++++++ src/cflip.h | 28 + src/clean.c | 544 +++++++++++++++++ src/clean.h | 27 + src/colwheel.c | 153 +++++ src/colwheel.h | 26 + src/contourise.c | 441 ++++++++++++++ src/contourise.h | 21 + src/correspondence.c | 73 +++ src/correspondence.h | 20 + src/data.c | 162 +++++ src/data.h | 34 ++ src/displaywindow.c | 1609 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/displaywindow.h | 76 +++ src/dpsynth.c | 291 +++++++++ src/dpsynth.h | 37 ++ src/elements.c | 86 +++ src/elements.h | 33 ++ src/elser.c | 811 +++++++++++++++++++++++++ src/elser.h | 25 + src/geometry.c | 324 ++++++++++ src/geometry.h | 29 + src/gsf.c | 669 +++++++++++++++++++++ src/gsf.h | 30 + src/gtk-symmetry.c | 243 ++++++++ src/gtk-symmetry.h | 50 ++ src/gtk-valuegraph.c | 234 ++++++++ src/gtk-valuegraph.h | 41 ++ src/luzzatti.c | 91 +++ src/luzzatti.h | 21 + src/main.c | 471 +++++++++++++++ src/main.h | 69 +++ src/model-display.c | 411 +++++++++++++ src/model-display.h | 27 + src/model-editor.c | 831 ++++++++++++++++++++++++++ src/model-editor.h | 69 +++ src/model.c | 523 ++++++++++++++++ src/model.h | 79 +++ src/multislice.c | 126 ++++ src/multislice.h | 25 + src/normalise.c | 721 ++++++++++++++++++++++ src/normalise.h | 32 + src/options.c | 191 ++++++ src/options.h | 23 + src/png-file.c | 125 ++++ src/png-file.h | 23 + src/refine-brent.c | 138 +++++ src/refine-brent.h | 26 + src/refine-cgrad.c | 339 +++++++++++ src/refine-cgrad.h | 26 + src/refine-lmder.c | 439 ++++++++++++++ src/refine-lmder.h | 26 + src/refine-lsq.c | 320 ++++++++++ src/refine-lsq.h | 26 + src/refine-rns.c | 122 ++++ src/refine-rns.h | 26 + src/refine.c | 359 +++++++++++ src/refine.h | 102 ++++ src/reflist.c | 287 +++++++++ src/reflist.h | 83 +++ src/renderer.c | 205 +++++++ src/renderer.h | 31 + src/statistics.c | 223 +++++++ src/statistics.h | 31 + src/superlattice.c | 142 +++++ src/superlattice.h | 23 + src/symmetry.c | 464 +++++++++++++++ src/symmetry.h | 89 +++ src/testmain.c | 68 +++ 77 files changed, 16042 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/amplitude-r.c create mode 100644 src/amplitude-r.h create mode 100644 src/anneal.h create mode 100644 src/argand.c create mode 100644 src/argand.h create mode 100644 src/cdm.c create mode 100644 src/cdm.h create mode 100644 src/cflip.c create mode 100644 src/cflip.h create mode 100644 src/clean.c create mode 100644 src/clean.h create mode 100644 src/colwheel.c create mode 100644 src/colwheel.h create mode 100644 src/contourise.c create mode 100644 src/contourise.h create mode 100644 src/correspondence.c create mode 100644 src/correspondence.h create mode 100644 src/data.c create mode 100644 src/data.h create mode 100644 src/displaywindow.c create mode 100644 src/displaywindow.h create mode 100644 src/dpsynth.c create mode 100644 src/dpsynth.h create mode 100644 src/elements.c create mode 100644 src/elements.h create mode 100644 src/elser.c create mode 100644 src/elser.h create mode 100644 src/geometry.c create mode 100644 src/geometry.h create mode 100644 src/gsf.c create mode 100644 src/gsf.h create mode 100644 src/gtk-symmetry.c create mode 100644 src/gtk-symmetry.h create mode 100644 src/gtk-valuegraph.c create mode 100644 src/gtk-valuegraph.h create mode 100644 src/luzzatti.c create mode 100644 src/luzzatti.h create mode 100644 src/main.c create mode 100644 src/main.h create mode 100644 src/model-display.c create mode 100644 src/model-display.h create mode 100644 src/model-editor.c create mode 100644 src/model-editor.h create mode 100644 src/model.c create mode 100644 src/model.h create mode 100644 src/multislice.c create mode 100644 src/multislice.h create mode 100644 src/normalise.c create mode 100644 src/normalise.h create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/png-file.c create mode 100644 src/png-file.h create mode 100644 src/refine-brent.c create mode 100644 src/refine-brent.h create mode 100644 src/refine-cgrad.c create mode 100644 src/refine-cgrad.h create mode 100644 src/refine-lmder.c create mode 100644 src/refine-lmder.h create mode 100644 src/refine-lsq.c create mode 100644 src/refine-lsq.h create mode 100644 src/refine-rns.c create mode 100644 src/refine-rns.h create mode 100644 src/refine.c create mode 100644 src/refine.h create mode 100644 src/reflist.c create mode 100644 src/reflist.h create mode 100644 src/renderer.c create mode 100644 src/renderer.h create mode 100644 src/statistics.c create mode 100644 src/statistics.h create mode 100644 src/superlattice.c create mode 100644 src/superlattice.h create mode 100644 src/symmetry.c create mode 100644 src/symmetry.h create mode 100644 src/testmain.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..225e23a --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,14 @@ +bin_PROGRAMS = synth2d synth2dtest +synth2d_SOURCES = main.c displaywindow.c data.c png-file.c reflist.c normalise.c contourise.c dpsynth.c argand.c symmetry.c \ + gtk-symmetry.c gsf.c cflip.c cdm.c clean.c geometry.c model.c elements.c statistics.c refine.c multislice.c \ + luzzatti.c correspondence.c amplitude-r.c superlattice.c elser.c gtk-valuegraph.c colwheel.c \ + model-editor.c model-display.c refine-rns.c refine-lmder.c refine-lsq.c refine-cgrad.c \ + refine-brent.c options.c renderer.c +synth2d_LDADD = @LIBS@ @GTK_LIBS@ -lfftw3 -lm -lgsl -lgslcblas -lgthread-2.0 +AM_CFLAGS = -Wall -g @CFLAGS@ @GTK_CFLAGS@ +AM_CPPFLAGS = -DDATADIR=\""$(datadir)"\" + +synth2dtest_SOURCES = testmain.c reflist.c data.c +synth2dtest_LDADD = @LIBS@ @GTK_LIBS@ -lfftw3 -lm -lgsl -lgslcblas -lgthread-2.0 + + diff --git a/src/amplitude-r.c b/src/amplitude-r.c new file mode 100644 index 0000000..50f6e9c --- /dev/null +++ b/src/amplitude-r.c @@ -0,0 +1,86 @@ +/* + * amplitude-r.c + * + * Plot of R-factor against amplitude + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "main.h" +#include "statistics.h" +#include "displaywindow.h" +#include "model.h" + +static void amplituder_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void amplituder_show() { + + FILE *fh; + unsigned int i; + FILE *gnuplot; + double scale; + ReflectionList *reflections; + ReflectionList *model_reflections; + + reflections = main_reflist(); + model_reflections = model_calculate_f(reflections, NULL, 69); + + scale = stat_scale(reflections, model_reflections); + fh = fopen("synth2d-amplitude-r.dat", "w"); + + for ( i=1; in_reflections; i++ ) { + + double residual; + signed int h, k, l; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + residual = 100*fabs((reflections->refs[i].amplitude - scale*model_reflections->refs[i].amplitude)/reflections->refs[i].amplitude); + if ( !isinf(residual) ) { + fprintf(fh, "%f %f\n", reflections->refs[i].amplitude, residual); + } + + } + + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + amplituder_gpwrite(gnuplot, "set autoscale\n"); + amplituder_gpwrite(gnuplot, "unset log\n"); + amplituder_gpwrite(gnuplot, "unset label\n"); + amplituder_gpwrite(gnuplot, "set xtic auto\n"); + amplituder_gpwrite(gnuplot, "set ytic auto\n"); + amplituder_gpwrite(gnuplot, "set grid\n"); + amplituder_gpwrite(gnuplot, "set ylabel 'R (%)' font \"Helvetica,10\"\n"); + amplituder_gpwrite(gnuplot, "set xlabel '|Fobs|' font \"Helvetica,10\"\n"); + amplituder_gpwrite(gnuplot, "set title 'Amplitude-R Plot' font \"Helvetica,10\"\n"); + amplituder_gpwrite(gnuplot, "plot 'synth2d-amplitude-r.dat'\n"); + amplituder_gpwrite(gnuplot, "replot\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} diff --git a/src/amplitude-r.h b/src/amplitude-r.h new file mode 100644 index 0000000..53ce9c5 --- /dev/null +++ b/src/amplitude-r.h @@ -0,0 +1,19 @@ +/* + * ampltude-r.h + * + * Plot of R-factor against amplitude + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef AMPLITUDER_H + +extern void amplituder_show(void); + +#endif /* AMPLITUDER_H */ diff --git a/src/anneal.h b/src/anneal.h new file mode 100644 index 0000000..2efcfaa --- /dev/null +++ b/src/anneal.h @@ -0,0 +1,20 @@ +/* + * anneal.h + * + * Structure solution by simulated annealing + * + * (c) 2006 Thomas White + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ANNEAL_H +#define ANNEAL_H + +extern void anneal_dialog_open(void); + +#endif /* ANNEAL_H */ diff --git a/src/argand.c b/src/argand.c new file mode 100644 index 0000000..7e94398 --- /dev/null +++ b/src/argand.c @@ -0,0 +1,118 @@ +/* + * argand.c + * + * "Argand plane tracking" + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "reflist.h" +#include "displaywindow.h" + +GtkWidget *argand_pixmap_widget = NULL; +GdkPixbuf *argand_pixbuf = NULL; +GtkWidget *argand_window = NULL; + +static fftw_complex *argand_draw(ReflectionList *reflections, unsigned int width, unsigned int height) { + + unsigned int x, y, i; + double mag = 6; + double max = 0; + fftw_complex *data; + + data = malloc(height*width*sizeof(fftw_complex)); + + for ( y=0; yn_reflections; i++ ) { + double am = reflections->refs[i].amplitude; + if ( am > max ) max = am; + } + mag = (width/2) / max; + + for ( i=0; in_reflections; i++ ) { + + unsigned int xd, yd; + double real, imag; + double am = reflections->refs[i].amplitude; + double ph = reflections->refs[i].phase_known; + + real = am * cos(ph); + imag = am * sin(ph); + + xd = (width-1-((width/2)+(real*mag))); + yd = (height-1-((height/2)+(imag*mag))); + + data[yd + height*xd][0] = 1; + data[yd + height*xd][1] = 0; + + } + + data[(height/2) + height*(width/2)][0]=-1; + data[(height/2-1) + height*(width/2)][0]=-1; + data[(height/2+1) + height*(width/2)][0]=-1; + data[(height/2-2) + height*(width/2)][0]=-1; + data[(height/2+2) + height*(width/2)][0]=-1; + data[(height/2) + height*(width/2+1)][0]=-1; + data[(height/2) + height*(width/2-1)][0]=-1; + data[(height/2) + height*(width/2+2)][0]=-1; + data[(height/2) + height*(width/2-2)][0]=-1; + + return data; + +} + +void argand_update(ReflectionList *reflections) { + + fftw_complex *data; + + if ( argand_window == NULL ) return; + + if ( argand_pixbuf ) gdk_pixbuf_unref(argand_pixbuf); + if ( argand_pixmap_widget ) gtk_widget_destroy(argand_pixmap_widget); + + data = argand_draw(reflections, 321, 321); + argand_pixbuf = displaywindow_render_pixbuf(data, 1, 321, 321, M_PI_2, 1, 1); + argand_pixmap_widget = gtk_image_new_from_pixbuf(argand_pixbuf); + gtk_container_add(GTK_CONTAINER(argand_window), argand_pixmap_widget); + gtk_widget_show(argand_pixmap_widget); + free(data); + +} + +static void argand_close() { + argand_window = NULL; + argand_pixbuf = NULL; + argand_pixmap_widget = NULL; +} + +void argand_open(ReflectionList *reflections) { + + if ( argand_window ) return; + + argand_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(argand_window), "Argand Plane"); + g_signal_connect(GTK_OBJECT(argand_window), "destroy", G_CALLBACK(argand_close), NULL); + gtk_widget_show_all(argand_window); + + argand_update(reflections); + +} + diff --git a/src/argand.h b/src/argand.h new file mode 100644 index 0000000..56c932c --- /dev/null +++ b/src/argand.h @@ -0,0 +1,26 @@ +/* + * argand.h + * + * "Argand plane tracking" + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ARGAND_H +#define ARGAND_H + +#include "reflist.h" + +extern void argand_update(ReflectionList *reflections); +extern void argand_open(ReflectionList *reflections); + +#endif /* ARGAND_H */ + diff --git a/src/cdm.c b/src/cdm.c new file mode 100644 index 0000000..d0a1a91 --- /dev/null +++ b/src/cdm.c @@ -0,0 +1,1202 @@ +/* + * cdm.c + * + * "Conventional Direct Methods" - triplet and tangent stuff + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "symmetry.h" +#include "main.h" +#include "gtk-symmetry.h" +#include "data.h" + +typedef struct { + GtkWidget *emin; + GtkWidget *gmin; + GtkWidget *amin; + GtkWidget *symmetry; + GtkWidget *refine; + GtkWidget *auto_iterate; +} CDMWindow; + +static CDMContext *the_cdmctx = NULL; + +static void cdm_solutionwindow_open(CDMContext *cdm); + +/* ........................ The Maths ........................ */ + +static ReflectionList *cdm_findstrongreflections(ReflectionList *reflections_orig, Symmetry sym, double emin) { + + ReflectionList *strongest_reflections; + ReflectionList *reflections; +// unsigned int target; + unsigned int i; + + /* Initialise reflection list */ + reflections = reflist_copy(reflections_orig); + strongest_reflections = reflist_new_parent(reflections_orig); + + for ( i=0; in_reflections; i++ ) { + reflections->refs[i].parent_index = i; + } + + for ( i=1; in_reflections; i++ ) { + if ( (reflections->refs[i].amplitude >= emin) && (reflections->refs[i].h != 0) && (reflections->refs[i].k != 0) ) { + reflist_addref_parent(strongest_reflections, reflections->refs[i].h, reflections->refs[i].k, reflections->refs[i].l, + reflections->refs[i].parent_index); + } + } + +#if 0 + while ( reflections->n_reflections > 1 ) { + + unsigned int j; + double am_max = 0; + unsigned int j_max = 0; + + /* Find the next strongest reflection */ + for ( j=1; jn_reflections; j++ ) { + + double am = reflections->refs[j].amplitude; + signed int h, k, l; + + if ( am == 0 ) continue; /* Avoid Trouble if emin is negative... */ + + h = reflections->refs[j].h; + k = reflections->refs[j].k; + l = reflections->refs[j].l; + + if ( am >= am_max ) { + am_max = am; + j_max = j; + } + + } + + /* Add reflection to the new list, with a link back to the original copy */ + reflist_addref_parent(strongest_reflections, reflections->refs[j_max].h, reflections->refs[j_max].k, reflections->refs[j_max].l, + reflections->refs[j_max].parent_index); + reflist_delref(reflections, reflections->refs[j_max].h, reflections->refs[j_max].k, reflections->refs[j_max].l); + + } + + /* Chop off everything but the first 40% of the reflections */ + target = 4*strongest_reflections->n_reflections/10; + while ( strongest_reflections->n_reflections > target ) { + reflist_delref(strongest_reflections, strongest_reflections->refs[strongest_reflections->n_reflections-1].h, + strongest_reflections->refs[strongest_reflections->n_reflections-1].k, + strongest_reflections->refs[strongest_reflections->n_reflections-1].l); + } +#endif + + return strongest_reflections; + +} + +#if 0 +static TripletList *cdm_sort_triplets(TripletList *triplet_list) { + + TripletList *sorted_triplets; + TripletList *triplet_copy; + unsigned int target; + + sorted_triplets = malloc(sizeof(TripletList)); + + triplet_copy = malloc(sizeof(TripletList)); + memcpy(triplet_copy, triplet_list, sizeof(TripletList)); + + target = 9*triplet_copy->n_triplets/10; + while ( triplet_copy->n_triplets > target ) { + + unsigned int i; + double G_max = 0; + unsigned int i_max = 0; + + /* Locate most concentrated triplet */ + for ( i=0; in_triplets; i++ ) { + if ( triplet_copy->triplets[i].G > G_max ) { + i_max = i; + G_max = triplet_copy->triplets[i].G; + } + } + + /* Copy the best triplet into the new list */ + sorted_triplets->triplets[sorted_triplets->n_triplets].G = triplet_copy->triplets[i_max].G; + sorted_triplets->triplets[sorted_triplets->n_triplets].p.h = triplet_copy->triplets[i_max].p.h; + sorted_triplets->triplets[sorted_triplets->n_triplets].p.k = triplet_copy->triplets[i_max].p.k; + sorted_triplets->triplets[sorted_triplets->n_triplets].p.l = triplet_copy->triplets[i_max].p.l; + sorted_triplets->triplets[sorted_triplets->n_triplets].q.h = triplet_copy->triplets[i_max].q.h; + sorted_triplets->triplets[sorted_triplets->n_triplets].q.k = triplet_copy->triplets[i_max].q.k; + sorted_triplets->triplets[sorted_triplets->n_triplets].q.l = triplet_copy->triplets[i_max].q.l; + sorted_triplets->n_triplets++; + + /* Remove the best triplet from the original list */ + for ( i=i_max+1; in_triplets; i++ ) { + triplet_copy->triplets[i-1].G = triplet_copy->triplets[i].G; + triplet_copy->triplets[i-1].p.h = triplet_copy->triplets[i].p.h; + triplet_copy->triplets[i-1].p.k = triplet_copy->triplets[i].p.k; + triplet_copy->triplets[i-1].p.l = triplet_copy->triplets[i].p.l; + triplet_copy->triplets[i-1].q.h = triplet_copy->triplets[i].q.h; + triplet_copy->triplets[i-1].q.k = triplet_copy->triplets[i].q.k; + triplet_copy->triplets[i-1].q.l = triplet_copy->triplets[i].q.l; + } + triplet_copy->n_triplets--; + + } + free(triplet_copy); + + return sorted_triplets; + +} +#endif + +static TripletList *cdm_findtriplets(ReflectionList *strongest_reflections, Symmetry sym, double emin, double gmin) { + + TripletList *triplet_list; + unsigned int p; + unsigned int q; +// TripletList *sorted_triplets; + unsigned int n_total_triplets; + + /* Initialise triplet list */ + triplet_list = malloc(sizeof(TripletList)); + triplet_list->n_triplets = 0; + + /* Iterate doubly over the reflections */ + n_total_triplets = 0; + for ( p=1; pn_reflections; p++ ) { + for ( q=1; qn_reflections; q++ ) { + + double E1; + double E2; + double E3; + double G; + unsigned int r; + signed int ph, pk, pl, qh, qk, ql, rh, rk, rl; + + ph = strongest_reflections->refs[p].h; + pk = strongest_reflections->refs[p].k; + pl = strongest_reflections->refs[p].l; + qh = strongest_reflections->refs[q].h; + qk = strongest_reflections->refs[q].k; + ql = strongest_reflections->refs[q].l; + + assert(ph || pk || pl); + assert(qh || qk || ql); + + /* Determine the third reflection in the triplet */ + rh = -ph-qh; + rk = -pk-qk; + if ( pl != ql ) { + printf("CDM: pl != ql: %3i %3i\n", pl, ql); + continue; + } + //assert(pl == ql); /* This failure mode should have been weeded out ages ago */ + rl = pl; + + /* See if the third reflection is available */ + if ( (r = reflist_inlist(strongest_reflections, rh, rk, rl)) ) { + + /* Trust me on this ... */ + E1 = strongest_reflections->parent->refs[strongest_reflections->refs[p].parent_index].amplitude; + E2 = strongest_reflections->parent->refs[strongest_reflections->refs[q].parent_index].amplitude; + E3 = strongest_reflections->parent->refs[strongest_reflections->refs[r].parent_index].amplitude; + + G = E1 * E2 * E3; + + /* Convincing enough? */ + if ( G > gmin ) { + triplet_list->triplets[triplet_list->n_triplets].p.h = ph; + triplet_list->triplets[triplet_list->n_triplets].p.k = pk; + triplet_list->triplets[triplet_list->n_triplets].p.l = pl; + triplet_list->triplets[triplet_list->n_triplets].q.h = qh; + triplet_list->triplets[triplet_list->n_triplets].q.k = qk; + triplet_list->triplets[triplet_list->n_triplets].q.l = ql; + triplet_list->triplets[triplet_list->n_triplets].G = G; + triplet_list->n_triplets++; + if ( triplet_list->n_triplets == MAX_TRIPLETS ) { + printf("DM: Too many triplets!\n"); + return NULL; + } + //printf("DM: Found triplet %3i,%3i %3i : %3i,%3i %3i : %3i %3i,%3i G = %f\n", + // ph, pk, pl, qh, qk, ql, rh, rk, rl, G); + } + n_total_triplets++; + + } + + } + } + + printf("DM: Have %i concentrated triplets (%0.1f%%)\n", triplet_list->n_triplets, ((double)triplet_list->n_triplets/n_total_triplets)*100); + + //sorted_triplets = cdm_sort_triplets(triplet_list); + //free(triplet_list); + //return sorted_triplets; + + return triplet_list; + +} + +static ReflectionList *cdm_definebasis(ReflectionList *reflections_orig, TripletList *triplet_list, Symmetry sym, double min_alpha) { + + signed int j, jmin; + ReflectionList *convergence; + ReflectionList *basis; + ReflectionList *reflections; + + if ( reflections_orig->n_reflections <= 1 ) { + printf("DM: No reflections!\n"); + return NULL; + } + + /* Circumvent normal creation mechanism in order to enact nastiness */ + reflections = malloc(sizeof(ReflectionList)); + if ( !reflections ) return NULL; + /* Start with the entire list of reflections and work backwards */ + memcpy(reflections, reflections_orig, sizeof(ReflectionList)); + + //printf("DM: Finding maximally connected starting set among %i reflections\n", reflections->n_reflections); + + convergence = reflist_new_parent(reflections_orig->parent); + + while ( reflections->n_reflections > 1 ) { /* Remember 0 0 0 */ + + double alpha_min = 999999; + signed int h_min = 999999; + signed int k_min = 999999; + signed int l_min = 999999; + unsigned int i; + unsigned int i_min = 0; + unsigned int p; + ReflectionList *equivalents; + + for ( i=1; in_reflections; i++ ) { + + signed int h, k, l; + unsigned int t; + double alpha = 0; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + assert( (h != 0) || (k != 0) || (l != 0) ); + + for ( t=0; tn_triplets; t++ ) { + + signed int ph = triplet_list->triplets[t].p.h; + signed int pk = triplet_list->triplets[t].p.k; + + /* Does this triplet concern this reflection? */ + if ( (ph == h) && (pk == k) ) { + + signed int qh = triplet_list->triplets[t].q.h; + signed int qk = triplet_list->triplets[t].q.k; + signed int rh = -ph-qh; + signed int rk = -pk-qk; + double G = triplet_list->triplets[t].G; + + /* Are the other reflections in the triplet still in the list? */ + if ( reflist_inlist_2d(reflections, qh, qk) && reflist_inlist_2d(reflections, rh, rk) ) { + alpha += G * (gsl_sf_bessel_I1(G) / gsl_sf_bessel_I0(G)); + //printf("%3i %3i %3i alpha=%f * (%f / %f) = %f\n", h, k, l, G, gsl_sf_bessel_I1(G), gsl_sf_bessel_I0(G), alpha); + } + + } + + } + + if ( alpha < alpha_min ) { + alpha_min = alpha; + h_min = h; + k_min = k; + l_min = l; + i_min = reflections->refs[i].parent_index; + } + + } + + if ( alpha_min == 99999 ) { + printf("DM: Convergence procedure isn't working sensibly: alpha is too high for all reflections.\n"); + printf("DM: Have you normalised properly?\n"); + return NULL; + } + + /* Remove the reflection h_min,k_min from the basis list */ + reflist_delref(reflections, h_min, k_min, l_min); + //printf("DM: %3i %3i %3i removed from convergence list, alpha = %f, %i reflections remaining\n", + // h_min, k_min, l_min, alpha_min, reflections->n_reflections); + + /* Remove all equivalent reflections from the basis list */ + equivalents = symmetry_generate_equivalent_reflections(sym, h_min, k_min, l_min); + + for ( p=1; pn_reflections; p++ ) { + + signed int h, k, l; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + reflist_delref(reflections, h, k, l); + //printf("DM: Removing symmetry equivalent %3i %3i %3i\n", h, k, l); + + } + + reflist_free(equivalents); + + /* Add the first reflections (but not the equivalents) */ + reflist_addref_alpha_parent(convergence, h_min, k_min, l_min, alpha_min, i_min); + + } + + /* "Convergence" is in increasing order of connectivity. Reverse the order, examining + the last 50 reflections on the convergence list */ + basis = reflist_new_parent(reflections_orig->parent); + jmin = (convergence->n_reflections)-13; + if ( jmin < 1 ) jmin = 1; + for ( j=(convergence->n_reflections)-1; j>=jmin; j-- ) { + if ( (convergence->refs[j].alpha < min_alpha) || ( basis->n_reflections<10) ) { + unsigned int parent; + //printf("DM: Adding %3i %3i %3i to starting set\n", convergence->refs[j].h, convergence->refs[j].k, convergence->refs[j].l); + parent = convergence->refs[j].parent_index; + reflist_addref_parent(basis, convergence->refs[j].h, convergence->refs[j].k, convergence->refs[j].l, parent); + } + } + + return basis; + +} + +static int cdm_phaserelationships(CDMContext *cdm) { + + if ( cdm->strongest_reflections == NULL ) { + /* Get the strongest reflections */ + cdm->strongest_reflections = cdm_findstrongreflections(cdm->reflections, cdm->sym, cdm->emin); + if ( !cdm->strongest_reflections ) { + printf("DM: Error while searching for strongest reflections.\n"); + return -1; + } else { + printf("DM: Have %i strong reflections (%0.1f%%).\n", cdm->strongest_reflections->n_reflections-1, + (((double)cdm->strongest_reflections->n_reflections-1)/(cdm->reflections->n_reflections-1))*100); + if ( cdm->strongest_reflections->n_reflections == 1 ) { + printf("DM: No strong reflections found!\n"); + return -1; + } + } + } else { + printf("DM: Already have strongest reflections.\n"); + } + + if ( cdm->triplet_list == NULL ) { + /* Find the triplets */ + cdm->triplet_list = cdm_findtriplets(cdm->strongest_reflections, cdm->sym, cdm->emin, cdm->gmin); + if ( !cdm->triplet_list ) { + printf("DM: Error while searching for triplets.\n"); + return -1; + } + } else { + printf("DM: Already have triplet list.\n"); + } + + if ( cdm->basis_list == NULL ) { + /* Determine an appropriate starting point */ + cdm->basis_list = cdm_definebasis(cdm->strongest_reflections, cdm->triplet_list, cdm->sym, cdm->amin); + if ( !cdm->basis_list ) { + printf("DM: Error while defining basis set.\n"); + return -1; + } + printf("DM: %i reflections in the starting set\n", cdm->basis_list->n_reflections-1); + } else { + printf("DM: Already have basis set.\n"); + } + + return 0; + +} + +static unsigned int cdm_phasebasis(ReflectionList *basis_list, ReflectionList *reflections, Symmetry sym) { + + unsigned int i; + unsigned int n_assigned = 0; + + /* Unset all phases */ + for ( i=0; in_reflections; i++ ) { + reflections->refs[i].phase_calc_set = 0; + } + + /* Assign random phases to the basis set */ + for ( i=1; in_reflections; i++ ) { + + ReflectionList *equivalents; + unsigned int p, j; + signed int h, k, l; + + j = basis_list->refs[i].parent_index; + h = reflections->refs[j].h; + k = reflections->refs[j].k; + l = reflections->refs[j].l; + + reflections->refs[j].phase_calc = (((double)random()/RAND_MAX) * (M_PI)) - (((double)random()/RAND_MAX) * (M_PI)); + reflections->refs[j].phase_calc_set = 1; + n_assigned++; + + /* Centricity */ + symmetry_centricity(reflections, j, sym, SYMFLAG_PHASES_CALC); + //printf("Set phase of %3i %3i %3i to %f\n", h, k, l, reflections->refs[j].phase_calc); + + /* Assign all symmetry equivalents as well */ + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + + for ( p=1; pn_reflections; p++ ) { + + unsigned int q; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + if ( (q = reflist_inlist(reflections, h, k, l)) ) { + + double equivalent_phase; + + equivalent_phase = (reflections->refs[j].phase_calc + equivalents->refs[p].delta_theta) * equivalents->refs[p].multiplier; + + reflections->refs[q].phase_calc = equivalent_phase; + reflections->refs[q].phase_calc_set = 1; + symmetry_centricity(reflections, q, sym, SYMFLAG_PHASES_CALC); + + //printf("DM: Setting equivalent reflection %3i %3i %3i to %f\n", h, k, l, equivalent_phase); + n_assigned++; + + } else { + //printf("DM: Need to set phase of %3i %3i %3i\n", h, k, l); + } + + } + + reflist_free(equivalents); + + } + + return n_assigned; + +} + +static unsigned int cdm_assign_code(ReflectionList *reflections, ReflectionList *basis_list, Symmetry sym, unsigned int phasing_code) { + + unsigned int i; + unsigned int n_assigned = 0; + + /* Unset all phases */ + for ( i=0; in_reflections; i++ ) { + reflections->refs[i].phase_calc_set = 0; + } + + /* Assign phases to the basis set */ + for ( i=1; in_reflections; i++ ) { + + ReflectionList *equivalents; + unsigned int p, j; + signed int h, k, l; + + j = basis_list->refs[i].parent_index; + h = reflections->refs[j].h; + k = reflections->refs[j].k; + l = reflections->refs[j].l; + + if ( phasing_code & (1<<(i-1)) ) { + reflections->refs[j].phase_calc = M_PI; + } else { + reflections->refs[j].phase_calc = 0; + } + reflections->refs[j].phase_calc_set = 1; + n_assigned++; + + /* Centricity */ + symmetry_centricity(reflections, j, sym, SYMFLAG_PHASES_CALC); + //printf("Set phase of %3i %3i %3i to %f\n", h, k, l, reflections->refs[j].phase_calc); + + /* Assign all symmetry equivalents as well */ + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + + for ( p=1; pn_reflections; p++ ) { + + unsigned int q; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + if ( (q = reflist_inlist(reflections, h, k, l)) ) { + + double equivalent_phase; + + equivalent_phase = (reflections->refs[j].phase_calc + equivalents->refs[p].delta_theta) * equivalents->refs[p].multiplier; + + reflections->refs[q].phase_calc = equivalent_phase; + reflections->refs[q].phase_calc_set = 1; + symmetry_centricity(reflections, q, sym, SYMFLAG_PHASES_CALC); + + //printf("DM: Setting equivalent reflection %3i %3i %3i to %f\n", h, k, l, equivalent_phase); + n_assigned++; + + } else { + //printf("DM: Need to set phase of %3i %3i %3i\n", h, k, l); + } + + } + + reflist_free(equivalents); + + } + + return n_assigned; + +} + +static unsigned int cdm_expand(CDMContext *cdm) { + + unsigned int total_assigned = 0; + unsigned int total_refined = 0; + unsigned int n_assigned, n_refined; + ReflectionList *reflections = cdm->reflections; + ReflectionList *strongest_reflections = cdm->strongest_reflections; + TripletList *triplet_list = cdm->triplet_list; + Symmetry sym = cdm->sym; + double min_alpha = cdm->amin; + unsigned int refinement = cdm->refine; + + /* Expand as far as possible */ + do { + + unsigned int i; + n_assigned = 0; + n_refined = 0; + + for ( i=0; in_reflections; i++ ) { + + double beta; + unsigned int t; + unsigned int n_tri = 0; + double alpha; + double sigma_sin = 0; + double sigma_cos = 0; + signed int h = strongest_reflections->refs[i].h; + signed int k = strongest_reflections->refs[i].k; + signed int l = strongest_reflections->refs[i].l; + unsigned int j = strongest_reflections->refs[i].parent_index; + unsigned int refine, assign; + + if ( !refinement && reflections->refs[j].phase_calc_set ) continue; /* Already phased? */ + + /* Calculate the most likely phase for this reflection based on the active triplets */ + for ( t=0; tn_triplets; t++ ) { + + signed int ph = triplet_list->triplets[t].p.h; + signed int pk = triplet_list->triplets[t].p.k; + + /* Does this triplet concern this reflection? */ + if ( (ph == h) && (pk == k) ) { + + signed int qh = triplet_list->triplets[t].q.h; + signed int qk = triplet_list->triplets[t].q.k; + signed int rh = -ph-qh; + signed int rk = -pk-qk; + unsigned int q = reflist_inlist_2d(reflections, qh, qk); + unsigned int r = reflist_inlist_2d(reflections, rh, rk); + + if ( !q ) { + printf("DM: q: %3i %3i not in list!\n", qh, qk); + continue; + } + if ( !r ) { + printf("DM: r: %3i %3i not in list!\n", rh, rk); + continue; + } + + /* Does this triplet concern already phased reflections? */ + if ( reflections->refs[q].phase_calc_set && reflections->refs[r].phase_calc_set ) { + + double omega = reflections->refs[q].phase_calc + reflections->refs[r].phase_calc; + + sigma_sin += triplet_list->triplets[t].G * sin(omega); + sigma_cos += triplet_list->triplets[t].G * cos(omega); + + n_tri++; + + + } + + } + + } + + alpha = sqrt(sigma_sin*sigma_sin + sigma_cos*sigma_cos); + assign = 0; refine = 0; + if ( !reflections->refs[j].phase_calc_set && (alpha > min_alpha) ) assign = 1; + if ( refinement && reflections->refs[j].phase_calc_set && (alpha > reflections->refs[j].alpha) ) refine = 1; + /* Assign phase if it's reliable enough */ + if ( assign || refine ) { + + ReflectionList *equivalents; + unsigned int p; + + beta = atan2(sigma_sin, sigma_cos); + if ( assign ) { + reflections->refs[j].phase_calc = beta; + reflections->refs[j].phase_calc_set = 1; + reflections->refs[j].alpha = alpha; + n_assigned++; + //printf("DM: Assigned %3i %3i phase %f (alpha=%f from %i triplets)\n", h, k, beta, alpha, n_tri); + } else { + reflections->refs[j].phase_calc = beta; + reflections->refs[j].alpha = alpha; + n_refined++; + //printf("DM: Refined %3i %3i phase %f (alpha=%f from %i triplets)\n", h, k, beta, alpha, n_tri); + } + + /* Centricity */ + symmetry_centricity(reflections, j, sym, SYMFLAG_PHASES_CALC); + + /* Assign all symmetry equivalents as well */ + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + + for ( p=1; pn_reflections; p++ ) { + + signed int h, k, l; + unsigned int q; + + h = equivalents->refs[p].h; + k = equivalents->refs[p].k; + l = equivalents->refs[p].l; + + if ( (q = reflist_inlist(reflections, h, k, l)) ) { + + double equivalent_phase; + + equivalent_phase = (beta + equivalents->refs[p].delta_theta) * equivalents->refs[p].multiplier; + + reflections->refs[q].phase_calc = equivalent_phase; + reflections->refs[q].phase_calc_set = 1; + reflections->refs[q].alpha = alpha; + symmetry_centricity(reflections, q, sym, SYMFLAG_PHASES_CALC); + + //printf("DM: Setting equivalent reflection %3i %3i %3i to %f\n", h, k, l, equivalent_phase); + n_assigned++; + + } else { + //printf("DM: Need to set phase of %3i %3i %3i, but it isn't in the list\n", h, k, l); + } + + } + + reflist_free(equivalents); + + } + + } + //printf("DM: This cycle: %i phases assigned, %i refined\n", n_assigned, n_refined); + total_assigned += n_assigned; + total_refined += n_refined; + + } while (n_refined+n_assigned > 0); + + cdm->n_assigned_expansion = total_assigned; + cdm->n_refined = total_refined; + return total_assigned; + +} + +static void cdm_sortsolutions(CDMContext *cdm) { + + + +} + +unsigned int cdm_systematic_expansion(CDMContext *cdm) { + + unsigned int i; + unsigned int n_solutions; + PhasingSolution *prev; + fftw_complex *cdm_in; + fftw_complex *cdm_out; + fftw_plan cdm_plan; + signed int h, k; + ReflectionList *reflections; + PhasingSolution *sol; + + if ( !(cdm->sym & SYMMETRY_CENTRE) ) { + error_report("Can only automatically iterate in a centrosymmetric planegroup"); + return 0; + } + + if ( cdm->basis_list->n_reflections > (sizeof(int)*8) ) { + error_report("Too many solutions to enumerate"); + return 0; + } + + /* Enumerate the solutions */ + n_solutions = 1<<(cdm->basis_list->n_reflections-1); + prev = NULL; + for ( i=1; i<=n_solutions; i++ ) { + + PhasingSolution *new; + + new = malloc(sizeof(PhasingSolution)); + new->n = i; + new->phasing_code = i-1; /* phasing_code might not always equal n */ + + new->next = NULL; + if ( i == 1 ) { + cdm->solutions = new; + } else { + prev->next = new; + } + prev = new; + + } + printf("DM: Enumerated %i solutions\n", n_solutions); + + /* Prepare */ + cdm_in = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + cdm_out = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + cdm_plan = fftw_plan_dft_2d(data_width(), data_height(), cdm_in, cdm_out, + FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + for ( h=0; hreflections; + sol = cdm->solutions; + do { + + unsigned int basis_assigned, expnd_assigned; + int x, y; + double max, unflatness, entropy; + + printf("%7i, phasing code %06X", sol->n, sol->phasing_code); + basis_assigned = cdm_assign_code(reflections, cdm->basis_list, cdm->sym, sol->phasing_code); + expnd_assigned = cdm_expand(cdm); + + /* Transfer to Fourier array */ + for ( i=0; in_reflections; i++ ) { + if ( reflections->refs[i].phase_calc_set ) { + h = reflections->refs[i].h; + k = reflections->refs[i].k; + if ( h < 0 ) { h = data_width()+h; } + if ( k < 0 ) { k = data_height()+k; } + cdm_in[k + data_height()*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_calc); + cdm_in[k + data_height()*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_calc); + } + } + + fftw_execute(cdm_plan); + displaywindow_switchview(); + while ( gtk_events_pending() ) gtk_main_iteration(); + + /* Calculate "unflatness" and entropy of map, and record */ + max = 0; + for ( y=0; y max ) max = am; + } + } + unflatness = 0; entropy = 0; + for ( y=0; yunflatness = unflatness; + sol->entropy = entropy; + printf(" : %i + %i (+%i) phased, unflatness=%.5f, entropy=%.5f\n", basis_assigned, expnd_assigned, cdm->n_refined, unflatness, entropy); + + sol = sol->next; + + } while ( sol ); + + /* Finalise */ + fftw_destroy_plan(cdm_plan); + fftw_free(cdm_in); + fftw_free(cdm_out); + + cdm_sortsolutions(cdm); + cdm_solutionwindow_open(cdm); + + return 0; + +} + +unsigned int cdm_tangentexpansion(CDMContext *cdm) { + + int res; + + /* Set up the phase relationships */ + res = cdm_phaserelationships(cdm); + if ( res ) return res; + + if ( !cdm->auto_iterate ) { + + unsigned int basis_assigned, expnd_assigned; + + /* Phase the initial reflections */ + basis_assigned = cdm_phasebasis(cdm->basis_list, cdm->reflections, cdm->sym); + + /* Expand */ + expnd_assigned = cdm_expand(cdm); + + /* Report */ + printf("DM: %i + %i (+%i) phases assigned\n", basis_assigned, expnd_assigned, cdm->n_refined); + + return basis_assigned + expnd_assigned; + + } else { + + return cdm_systematic_expansion(cdm); + + } + +} + +/* ..................... The Other Stuff ..................... */ + +static int cdm_window_open = 0; + +static void cdm_execute(CDMWindow *cdm) { + + char *string; + int total_assigned; + Symmetry sym; + float emin, gmin, amin; + const char *str; + CDMContext *cdmctx; + + sym = gtk_symmetry_get_symmetry(GTK_SYMMETRY(cdm->symmetry)); + + str = gtk_entry_get_text(GTK_ENTRY(cdm->emin)); + if ( !sscanf(str, "%f", &emin) ) { + error_report("Plase specify minimum |E|"); + return; + } + str = gtk_entry_get_text(GTK_ENTRY(cdm->gmin)); + if ( !sscanf(str, "%f", &gmin) ) { + error_report("Plase specify minimum G"); + return; + } + str = gtk_entry_get_text(GTK_ENTRY(cdm->amin)); + if ( !sscanf(str, "%f", &amin) ) { + error_report("Plase specify minimum α"); + return; + } + + if ( the_cdmctx == NULL ) { + printf("DM: Creating new context.\n"); + the_cdmctx = malloc(sizeof(CDMContext)); + if ( the_cdmctx == NULL ) { + error_report("Couldn't create CDM control structure"); + return; + } + the_cdmctx->auto_iterate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->auto_iterate)); + the_cdmctx->refine = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->refine)); + the_cdmctx->sym = sym; + the_cdmctx->emin = emin; + the_cdmctx->gmin = gmin; + the_cdmctx->amin = amin; + the_cdmctx->strongest_reflections = NULL; + the_cdmctx->triplet_list = NULL; + the_cdmctx->basis_list = NULL; + } else { + int restart = 0; + if ( the_cdmctx->sym != sym ) { + restart = 1; + } + if ( the_cdmctx->emin != emin ) { + restart = 1; + } + if ( the_cdmctx->gmin != gmin ) { + restart = 1; + } + /* The others do not require a re-calculation */ + if ( restart ) { + printf("DM: Conditions have changed. Recreating context...\n"); + /* Tidy up */ + free(the_cdmctx->triplet_list); + free(the_cdmctx->strongest_reflections); + free(the_cdmctx->basis_list); + free(the_cdmctx); + the_cdmctx = malloc(sizeof(CDMContext)); + if ( the_cdmctx == NULL ) { + error_report("Couldn't create CDM control structure"); + return; + } + the_cdmctx->auto_iterate = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->auto_iterate)); + the_cdmctx->refine = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cdm->refine)); + the_cdmctx->sym = sym; + the_cdmctx->emin = emin; + the_cdmctx->gmin = gmin; + the_cdmctx->amin = amin; + the_cdmctx->strongest_reflections = NULL; + the_cdmctx->triplet_list = NULL; + the_cdmctx->basis_list = NULL; + } + + } + cdmctx = the_cdmctx; + + if ( !cdmctx->auto_iterate ) { + + /* Do one expansion based on a random starting set, display the result */ + total_assigned = main_cdm_tangentexpansion(cdmctx); + displaywindow_switchview(); + + string = malloc(64); + snprintf(string, 63, "%i phases assigned", total_assigned); + displaywindow_statusbar(string); + free(string); + + } else { + + /* Systematically iterate over all possible starting sets, rank by FoM */ + main_cdm_tangentexpansion(cdmctx); + + } + +} + +static gint cdm_window_response(GtkWidget *widget, gint response, CDMWindow *cdm) { + + if ( response == GTK_RESPONSE_OK ) { + cdm_execute(cdm); + } + + if ( response != GTK_RESPONSE_OK ) { + cdm_window_open = 0; + gtk_widget_destroy(widget); + } + + return 0; + +} + +void cdm_dialog_open() { + + CDMWindow *cdm; + GtkWidget *cdm_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *emin_label; + GtkWidget *gmin_label; + GtkWidget *amin_label; + + if ( cdm_window_open ) { + return; + } + cdm_window_open = 1; + + cdm = malloc(sizeof(CDMWindow)); + + cdm_window = gtk_dialog_new_with_buttons("Tangent Formula", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, GTK_STOCK_EXECUTE, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cdm_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + cdm->symmetry = gtk_symmetry_new(2, 2, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cdm->symmetry), TRUE, TRUE, 5); + + table = gtk_table_new(5, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + emin_label = gtk_label_new("Minimum |E|:"); + gtk_misc_set_alignment(GTK_MISC(emin_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(emin_label), 1, 2, 1, 2); + cdm->emin = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->emin), 2, 3, 1, 2); + gtk_entry_set_text(GTK_ENTRY(cdm->emin), "0.0001"); + + gmin_label = gtk_label_new("Minimum G:"); + gtk_misc_set_alignment(GTK_MISC(gmin_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gmin_label), 1, 2, 2, 3); + cdm->gmin = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->gmin), 2, 3, 2, 3); + gtk_entry_set_text(GTK_ENTRY(cdm->gmin), "0.00024"); + + amin_label = gtk_label_new("Minimum α:"); + gtk_misc_set_alignment(GTK_MISC(amin_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(amin_label), 1, 2, 3, 4); + cdm->amin = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->amin), 2, 3, 3, 4); + gtk_entry_set_text(GTK_ENTRY(cdm->amin), "0.0001"); + + cdm->refine = gtk_check_button_new_with_label("Refine Phases"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->refine), 1, 3, 4, 5); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cdm->refine), TRUE); + + cdm->auto_iterate = gtk_check_button_new_with_label("Automatically Iterate"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cdm->auto_iterate), 1, 3, 5, 6); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cdm->auto_iterate), FALSE); + + g_signal_connect(G_OBJECT(cdm_window), "response", G_CALLBACK(cdm_window_response), cdm); + gtk_widget_show_all(cdm_window); + +} + +enum { + SOLUTION_COLUMN_NUMBER, + SOLUTION_COLUMN_PHASECODE, + SOLUTION_COLUMN_UNFLATNESS, + SOLUTION_COLUMN_ENTROPY, + SOLUTION_COLUMNS +}; + +void cdm_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol, ReflectionList *reflections) { + + cdm_assign_code(reflections, cdm->basis_list, cdm->sym, sol->phasing_code); + cdm_expand(cdm); + + displaywindow_switchview(); + +} + +static gint cdm_solutionwindow_response(GtkWidget *solution_window, gint response, gpointer data) { + gtk_widget_destroy(solution_window); + return 0; +} + +static void cdm_solutionwindow_selectionchanged(GtkTreeSelection *selection, CDMContext *cdm) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + gint snum; + PhasingSolution *sol_it; + PhasingSolution *sol; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(cdm->solution_tree_view), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(cdm->solution_list_store), &iter, path); + gtk_tree_model_get(GTK_TREE_MODEL(cdm->solution_list_store), &iter, SOLUTION_COLUMN_NUMBER, &snum, -1); + + sol_it = cdm->solutions; sol = NULL; + do { + if ( sol_it->n == snum ) { + sol = sol_it; + break; + } + sol_it = sol_it->next; + } while ( sol_it ); + if ( sol ) { + printf("DM: Displaying solution %7i, phasing code %06X\n", sol->n, sol->phasing_code); + main_display_phasing_solution(cdm, sol); + } else { + printf("DM: Can't find solution %i\n", snum); + error_report("Can't find solution!\n"); + } + +} + +static void cdm_solutionwindow_open(CDMContext *cdm) { + + GtkWidget *solution_window; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkWidget *scrolledwindow; + GtkWidget *solution_tree; + GtkListStore *solution_list_store; + GtkWidget *hbox; + GtkWidget *vbox; + GtkTreeIter iter; + PhasingSolution *sol; + + solution_window = gtk_dialog_new_with_buttons("Phasing Solutions", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(solution_window), 400, 500); + + /* Window layout */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(solution_window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + /* List and Tree */ + solution_list_store = gtk_list_store_new(SOLUTION_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + solution_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(solution_list_store)); + g_object_unref(G_OBJECT(solution_list_store)); + cdm->solution_tree_view = solution_tree; + cdm->solution_list_store = solution_list_store; + + /* Set up columns */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes("#", renderer, "text", SOLUTION_COLUMN_NUMBER, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + column = gtk_tree_view_column_new_with_attributes("Phase", renderer, "text", SOLUTION_COLUMN_PHASECODE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + column = gtk_tree_view_column_new_with_attributes("Unflatness", renderer, "text", SOLUTION_COLUMN_UNFLATNESS, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + column = gtk_tree_view_column_new_with_attributes("Entropy", renderer, "text", SOLUTION_COLUMN_ENTROPY, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(solution_tree), column); + + /* Scrollbar */ + scrolledwindow = gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(solution_tree)), + gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(solution_tree))); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolledwindow), GTK_WIDGET(solution_tree)); + + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolledwindow), TRUE, TRUE, 5); + + /* Add the solutions */ + sol = cdm->solutions; + do { + char tmp[32]; + gtk_list_store_append(solution_list_store, &iter); + snprintf(tmp, 31, "%06X", sol->phasing_code); + gtk_list_store_set(solution_list_store, &iter, SOLUTION_COLUMN_NUMBER, sol->n, SOLUTION_COLUMN_PHASECODE, tmp, + SOLUTION_COLUMN_UNFLATNESS, sol->unflatness, SOLUTION_COLUMN_ENTROPY, sol->entropy, -1); + sol = sol->next; + } while ( sol ); + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(solution_tree))), "changed", + G_CALLBACK(cdm_solutionwindow_selectionchanged), cdm); + + + g_signal_connect(G_OBJECT(solution_window), "response", G_CALLBACK(cdm_solutionwindow_response), cdm); + gtk_widget_show_all(solution_window); + +} diff --git a/src/cdm.h b/src/cdm.h new file mode 100644 index 0000000..a0cae01 --- /dev/null +++ b/src/cdm.h @@ -0,0 +1,77 @@ +/* + * cdm.h + * + * "Conventional" Direct Methods + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CDM_H +#define CDM_H + +#include + +#include "reflist.h" +#include "symmetry.h" + +#define MAX_TRIPLETS 65536 + +typedef struct { + Reflection p; + Reflection q; + /* The other reflection in the triplet is -p-q */ + double G; +} Triplet; + +typedef struct { + Triplet triplets[MAX_TRIPLETS]; + unsigned int n_triplets; +} TripletList; + +typedef struct s_phase_sol { + + unsigned int n; /* Reference number */ + unsigned int phasing_code; /* Phase values, binary 1->Pi, 0->0. Only works for centro */ + double unflatness; /* Luzzatti 'unflatness': sigma(rho^4) */ + double entropy; /* Entropy -sigma(P*ln(P)) */ + + struct s_phase_sol *next; + +} PhasingSolution; + +typedef struct { + + unsigned int auto_iterate; + unsigned int refine; + double emin; + double gmin; + double amin; + Symmetry sym; + + ReflectionList *reflections; /* Overall list of reflections (used to store results) */ + ReflectionList *strongest_reflections; /* Strongest reflections (used for phasing) */ + TripletList *triplet_list; /* List of active triplets */ + ReflectionList *basis_list; /* Starting set of reflections */ + + GtkWidget *solution_tree_view; + GtkListStore *solution_list_store; + + PhasingSolution *solutions; + + unsigned int n_assigned_basis; + unsigned int n_assigned_expansion; + unsigned int n_refined; + +} CDMContext; + +extern void cdm_dialog_open(void); +extern unsigned int cdm_tangentexpansion(CDMContext *cdm); +extern void cdm_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol, ReflectionList *reflections); + +#endif /* CDM_H */ diff --git a/src/cflip.c b/src/cflip.c new file mode 100644 index 0000000..2b3dfd6 --- /dev/null +++ b/src/cflip.c @@ -0,0 +1,910 @@ +/* + * cflip.c + * + * Charge flipping iteration algorithms + * + * (c) 2006-2007 Thomas White + * (c) 2008 Alex Eggeman + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "displaywindow.h" +#include "symmetry.h" +#include "gtk-symmetry.h" +#include "reflist.h" +#include "dpsynth.h" + +static int cf_window_open = 0; + +typedef struct struct_cflipdialog { + + /* Dialog box bits */ + GtkWidget *cf_window; + GtkWidget *cf_go; + GtkWidget *cf_stop; + GtkWidget *cf_next; + GtkWidget *cf_reset; + GtkWidget *cf_iterations; + + /* Algorithm guts */ + ReflectionList *cf_reflections; /* List of 'modulus constraints' */ + ReflectionList *cf_listout; /* List of structure factors returned at the end of an iteration */ + Symmetry cf_symmetry; /* Symmetry of the algorithm */ + fftw_complex *cf_drive_old; + fftw_complex *cf_drive; + fftw_complex *cf_in; /* Input realspace array */ + fftw_complex *cf_out; /* Array on the other end of the FFT of 'in' */ + unsigned int *cf_support; + unsigned int cf_tangent; /*Coehlo Constraints*/ + double *intensities; /*Array of pixel intensities for auto-thresholding*/ + double *amplitudes; /*Array of reflection intiensities for auto-thresholding*/ + fftw_plan cf_plan_i2d; + fftw_plan cf_plan_d2i; + unsigned int width; /* Width of the array in pixels */ + unsigned int height; /* Height of the array in pixels */ + double delta; + //DPSynthWindow *dpsynth; + + /* Tracking */ + + unsigned int running; + unsigned int n_iterations; /* Number of iterations performed so far */ + unsigned int period; /* Number of iterations per symmetry application */ + +} CFlipDialog; + +int compare_doubles (const void *xp,const void *yp) { + + const double *x, *y; + int z; + + x=xp; + y=yp; + + + if (*x > *y) { + z = 1; + } else if (*x < *y) { + z = -1; + } else { + z = 0; + } + return z; +} + +static int cflip_tangent (ReflectionList *listout, double emin, double gmin) { + + ReflectionList *strongest_reflections; + TripletList *triplet_list; + unsigned int i; + unsigned int p; + unsigned int q; + unsigned int n_total_triplets; + double E1; + double E2; + double E3; + double G; + double Th; + double Bh; + double *Mh; + double maxMh; + double *phase_tangent; + double alpha_tan, phi_a, phi_tan; + unsigned int r, t; + signed int ph, pk, pl, pph, qh, qk, ql, qph, rh, rk, rl, rph; + + strongest_reflections = reflist_new_parent(listout); + /* Initialise triplet list */ + triplet_list = malloc(sizeof(TripletList)); + triplet_list->n_triplets = 0; + + for ( i=0; in_reflections; i++ ) { + listout->refs[i].parent_index = i; + } + + for ( i=1; in_reflections; i++ ) { + if ( (listout->refs[i].amplitude >= emin) && (listout->refs[i].h != 0) && (listout->refs[i].k != 0) ) { + reflist_addref_parent(strongest_reflections, listout->refs[i].h, listout->refs[i].k, listout->refs[i].l, + listout->refs[i].parent_index); + } + } + + Mh = malloc(strongest_reflections->n_reflections*sizeof(double)); + + phase_tangent = malloc(strongest_reflections->n_reflections*sizeof(double)); + + /* Iterate doubly over the reflections */ + n_total_triplets = 0; + for ( p=1; pn_reflections; p++ ) { + for ( q=1; qn_reflections; q++ ) { + + ph = strongest_reflections->refs[p].h; + pk = strongest_reflections->refs[p].k; + pl = strongest_reflections->refs[p].l; + pph = strongest_reflections->refs[p].phase_known; + qh = strongest_reflections->refs[q].h; + qk = strongest_reflections->refs[q].k; + ql = strongest_reflections->refs[q].l; + qph = strongest_reflections->refs[q].phase_known; + + assert(ph || pk || pl); + assert(qh || qk || ql); + + /* Determine the third reflection in the triplet */ + rh = -ph-qh; + rk = -pk-qk; + if ( pl != ql ) { + printf("Coelho: pl != ql: %3i %3i\n", pl, ql); + continue; + } + //assert(pl == ql); /* This failure mode should have been weeded out ages ago */ + rl = pl; + + /* See if the third reflection is available */ + if ( (r = reflist_inlist(strongest_reflections, rh, rk, rl)) ) { + + /* Trust me on this ... */ + E1 = strongest_reflections->parent->refs[strongest_reflections->refs[p].parent_index].amplitude; + E2 = strongest_reflections->parent->refs[strongest_reflections->refs[q].parent_index].amplitude; + E3 = strongest_reflections->parent->refs[strongest_reflections->refs[r].parent_index].amplitude; + + G = E1 * E2 * E3; + + rph = strongest_reflections->parent->refs[strongest_reflections->refs[r].parent_index].phase_known; + + /* Convincing enough? */ + if ( G > gmin ) { + triplet_list->triplets[triplet_list->n_triplets].p.h = ph; + triplet_list->triplets[triplet_list->n_triplets].p.k = pk; + triplet_list->triplets[triplet_list->n_triplets].p.l = pl; + triplet_list->triplets[triplet_list->n_triplets].q.h = qh; + triplet_list->triplets[triplet_list->n_triplets].q.k = qk; + triplet_list->triplets[triplet_list->n_triplets].q.l = ql; + triplet_list->triplets[triplet_list->n_triplets].G = G; + triplet_list->n_triplets++; + if ( triplet_list->n_triplets == MAX_TRIPLETS ) { + printf("DM: Too many triplets!\n"); + return NULL; + } + + } + n_total_triplets++; + + } + + } + } + maxMh = 0; + + for ( i=0; in_reflections; i++ ) { + + signed int h = strongest_reflections->refs[i].h; + signed int k = strongest_reflections->refs[i].k; + + for ( t=0; tn_triplets; t++ ) { + + signed int ph = triplet_list->triplets[t].p.h; + signed int pk = triplet_list->triplets[t].p.k; + + + /* Does this triplet concern this reflection? */ + if ( (ph == h) && (pk == k) ) { + + signed int qh = triplet_list->triplets[t].q.h; + signed int qk = triplet_list->triplets[t].q.k; + signed int rh = -ph-qh; + signed int rk = -pk-qk; + double G = triplet_list->triplets[t].G; + + /* Are the other reflections in the triplet still in the list? */ + if ( reflist_inlist_2d(listout, qh, qk) && reflist_inlist_2d(listout, rh, rk) ) { + Th += G * sin(( qph - rph)); + Bh += G * cos(( qph - rph)); + } + + + } + + } + phase_tangent[i] = atan( Th / Bh ); + Mh[i] = sqrt( (Th*Th) / (Bh*Bh) ); + if ( Mh[i] > maxMh ) { + maxMh = Mh[i]; + } + + } + + for ( i=0; in_reflections; i++ ) { + + alpha_tan = Mh[i] / maxMh; + + phi_a = strongest_reflections->parent->refs[strongest_reflections->refs[i].parent_index].phase_known; + + phi_tan = phi_a + alpha_tan*(phase_tangent[i]-phi_a); + + listout->refs[strongest_reflections->refs[i].parent_index].phase_known = phi_tan; + + } + + free(strongest_reflections); + free(triplet_list); + + return 0; + +} + +void cflip_mask(CFlipDialog *cflip_dialog, fftw_complex *in) { + + + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + unsigned int i; + fftw_complex *in_new = malloc(width*height*sizeof(fftw_complex)); + bzero(in_new, width*height*sizeof(fftw_complex)); + ReflectionList *reflections; + + reflections = cflip_dialog->cf_reflections; + + for ( i=1; in_reflections; i++ ) { + + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + in_new[k + height*h][0] = in[k + height*h][0]; + in_new[k + height*h][1] = in[k + height*h][1]; + + } + + memcpy(in, in_new, width*height*sizeof(fftw_complex)); + free(in_new); + +} + +/* Perform a single CF iteration */ +void cflip_iteration(CFlipDialog *cflip_dialog) { + + double re, im; + double am, ph, max, i_max; + signed int h, k; + unsigned int j, indexj; + unsigned int x, y; + double fzero; + double emin = 0; + double gmin = 0; + double mean, neg; + double coehlo_thresh; + unsigned int mean_n; + double meansq; + double sig; + double delta; + unsigned int i; + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + ReflectionList *reflections = cflip_dialog->cf_reflections; + ReflectionList *listout = cflip_dialog->cf_listout; + + cflip_dialog->intensities = malloc(width*height*sizeof(double)); + cflip_dialog->amplitudes = malloc(reflections->n_reflections*sizeof(double)); + +// listout = malloc(sizeof(ReflectionList)); + listout = NULL; + assert(cflip_dialog->cf_drive_old != NULL); + assert(cflip_dialog->cf_drive != NULL); + assert(cflip_dialog->cf_in != NULL); + assert(cflip_dialog->cf_out != NULL); + + if ( !cflip_dialog->cf_tangent ) { + + /* Transform back to reciprocal space and scale. This time, the driving function is + being transformed. */ + fftw_execute(cflip_dialog->cf_plan_d2i); + for ( x=0; xcf_in[y+((height/2)+1)*x][0] = cflip_dialog->cf_in[y+((height/2)+1)*x][0] / (width*height); /* Re */ + cflip_dialog->cf_in[y+((height/2)+1)*x][1] = cflip_dialog->cf_in[y+((height/2)+1)*x][1] / (width*height); /* Im */ + } + } + + /* Impose observed intensities */ + for ( i=0; in_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + re = cflip_dialog->cf_in[k + height*h][0]; + im = cflip_dialog->cf_in[k + height*h][1]; + ph = atan2(im, re); + am = reflections->refs[i].amplitude; + + cflip_dialog->cf_in[k + height*h][0] = am*cos(ph); + cflip_dialog->cf_in[k + height*h][1] = am*sin(ph); + + } + + fzero = cflip_dialog->cf_in[0][0]; + cflip_mask(cflip_dialog, cflip_dialog->cf_in); + + /* Impose symmetry */ + if ( cflip_dialog->n_iterations % cflip_dialog->period == 0 ) { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + + } else { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry & SYMMETRY_FRIEDEL, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + } + + + + /* Transform to real space */ + fftw_execute(cflip_dialog->cf_plan_i2d); + + memcpy(cflip_dialog->cf_out, cflip_dialog->cf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + mean = 0; mean_n = 0; meansq = 0; sig = 0; delta = 0; + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt(re*re + im*im); + mean += am; + mean_n += 1; + meansq += am * am; + } + } + + sig = sqrt((meansq/mean_n)-((mean/mean_n) * (mean/mean_n))); + delta = sig * cflip_dialog->delta; + + /* Determine the support */ + for ( y=0; ycf_drive[y+height*x][0] > delta ) { + cflip_dialog->cf_support[y+height*x] = 1; + } else { + cflip_dialog->cf_support[y+height*x] = 0; + } + } + } + + + /* Impose non-negativity, sharpen peaks and apply feedback */ + + /*mean = 0; neg = 0; + + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt(re*re + im*im); + + mean += am; + if ( re<0 ) { + neg += am; + } + + } + } + + mean = mean /(width * height); + + double entropy; + entropy = 0; + for ( y=0; yn_iterations, entropy, neg);*/ + + + for ( y=0; ycf_support[y+height*x] ) { + + cflip_dialog->cf_drive[y+height*x][0] = -cflip_dialog->cf_drive[y+height*x][0]; + cflip_dialog->cf_drive[y+height*x][1] = -cflip_dialog->cf_drive[y+height*x][1]; + + } + + } + } + + } else { /*Rework the Algorithm from here to incorporate the Coehlo constraints*/ + + /* Transform back to reciprocal space and scale. This time, the driving function is being transformed. */ + fftw_execute(cflip_dialog->cf_plan_d2i); + for ( x=0; xcf_in[y+((height/2)+1)*x][0] = cflip_dialog->cf_in[y+((height/2)+1)*x][0] / (width*height); /* Re */ + cflip_dialog->cf_in[y+((height/2)+1)*x][1] = cflip_dialog->cf_in[y+((height/2)+1)*x][1] / (width*height); /* Im */ + } + } + + /* Do Tangent Formula Wizardry */ + cflip_mask(cflip_dialog, cflip_dialog->cf_in); + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + + i_max = 0; + + for ( i=0; in_reflections; i++ ) { + + cflip_dialog->amplitudes[i]=listout->refs[i].amplitude; + + if ( listout->refs[i].amplitude > i_max ) { + i_max = listout->refs[i].amplitude; + } + } + + emin = 0.5 * i_max; + gmin = 0.2 * i_max; + + cflip_tangent (listout, emin, gmin); + + i_max = 0; j=0; max = 0; indexj=0; + + for ( i=0; in_reflections; i++ ) { + + cflip_dialog->amplitudes[i]=reflections->refs[i].amplitude; + if ( reflections->refs[i].amplitude > i_max ) { + i_max = reflections->refs[i].amplitude; + } + } + + qsort(cflip_dialog->amplitudes, listout->n_reflections, sizeof(double), compare_doubles); + + j = (int)(listout->n_reflections / 2); + j = (int)(listout->n_reflections / 4); + max = cflip_dialog->amplitudes[j]; + + /* Impose observed intensities */ + //unsigned int n_sub = 0; + for ( i=0; in_reflections; i++ ) { + + h = listout->refs[i].h; + k = listout->refs[i].k; + + indexj = reflist_inlist(reflections, h, k, 0); + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + ph = listout->refs[i].phase_known; + am = reflections->refs[indexj].amplitude; + if (am < max ) { + am = 0; + //n_sub += 1; + } + + //printf("number of Zero reflections is %i.\n", n_sub); + cflip_dialog->cf_in[k + height*h][0] = am*cos(ph); + cflip_dialog->cf_in[k + height*h][1] = am*sin(ph); + + } + + fzero = cflip_dialog->cf_in[0][0]; + //free(listout); + + /* Impose symmetry */ + if ( cflip_dialog->n_iterations % cflip_dialog->period == 0 ) { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + + } else { + + listout = reflist_new_from_array(cflip_dialog->cf_in, width, height); + symmetry_symmetrise(listout, cflip_dialog->cf_symmetry & SYMMETRY_FRIEDEL, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(cflip_dialog->cf_in, listout, width, height); + //dpsynth_update(cflip_dialog->dpsynth, listout); + reflist_free(listout); + + } + + /* Transform to real space */ + fftw_execute(cflip_dialog->cf_plan_i2d); + + max = 0; + + memcpy(cflip_dialog->cf_out, cflip_dialog->cf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + + /* Find the Threshold Real-Space Intensity */ + mean = 0; mean_n = 0; sig = 0; delta = 0; + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt((re*re) + (im*im)); + if ( am > max ) { + max = am; + } + + } + } + + for ( y=0; ycf_drive[y+height*x][0]; + im = cflip_dialog->cf_drive[y+height*x][1]; + am = sqrt(re*re + im*im) / max; + mean += am; + cflip_dialog->intensities[y+height*x]=am; + } + } + + qsort(cflip_dialog->intensities, mean_n, sizeof(double), compare_doubles); + + coehlo_thresh = 0; + i=0; + while ( coehlo_thresh < (0.6 * mean) ) { + + coehlo_thresh += cflip_dialog->intensities[i]; + delta = cflip_dialog->intensities[i]; + i += 1; + } + + /* Determine the support */ + for ( y=0; ycf_drive[y+height*x][0] > delta ) { + cflip_dialog->cf_support[y+height*x] = 1; + } else { + cflip_dialog->cf_support[y+height*x] = 0; + } + } + } + + /* Perform the Charge Flip */ + for ( y=0; ycf_support[y+height*x] ) { + + cflip_dialog->cf_drive[y+height*x][0] = -cflip_dialog->cf_drive[y+height*x][0]; + cflip_dialog->cf_drive[y+height*x][1] = -cflip_dialog->cf_drive[y+height*x][1]; + + } else { + //cflip_dialog->cf_drive[y+height*x][0] = delta + sqrt((cflip_dialog->cf_drive[y+height*x][0])-delta); + cflip_dialog->cf_drive[y+height*x][0] = cflip_dialog->cf_drive[y+height*x][0]; + cflip_dialog->cf_drive[y+height*x][1] = cflip_dialog->cf_drive[y+height*x][1]; + + } + } + } + } +free(cflip_dialog->intensities); +free(cflip_dialog->amplitudes); +} + +static gint cflip_threshsup_deltachanged(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->delta = gtk_range_get_value(GTK_RANGE(widget)); + return 0; +} + +void cflip_finalise(CFlipDialog *cflip_dialog) { + + free(cflip_dialog->cf_drive); cflip_dialog->cf_drive = NULL; + free(cflip_dialog->cf_drive_old); cflip_dialog->cf_drive_old = NULL; + free(cflip_dialog->cf_support); cflip_dialog->cf_support = NULL; + free(cflip_dialog->cf_in); cflip_dialog->cf_in = NULL; + reflist_free(cflip_dialog->cf_reflections); cflip_dialog->cf_reflections = NULL; + /* Don't free cf_out, since it is now backing up the display */ + + fftw_destroy_plan(cflip_dialog->cf_plan_i2d); + fftw_destroy_plan(cflip_dialog->cf_plan_d2i); + +} + + +static gint cflip_window_periodchanged(GtkWidget *cfw_period, CFlipDialog *cflip_dialog) { + + const char *str; + + str = gtk_entry_get_text(GTK_ENTRY(cfw_period)); + cflip_dialog->period = atoi(str); + return 0; +} + +static void cflip_window_close(GtkWidget *widget, gint arg1, CFlipDialog *cflip_dialog) { + + if ( cflip_dialog->running ) { + cflip_dialog->running = 0; + } + cf_window_open = 0; + + cflip_finalise(cflip_dialog); + + gtk_widget_destroy(widget); + +} + +static void cflip_update_iterations(CFlipDialog *cflip_dialog) { + + char *text = malloc(40); + snprintf(text, 39, "Iterations performed so far: %i", cflip_dialog->n_iterations); + gtk_label_set_text(GTK_LABEL(cflip_dialog->cf_iterations), text); + free(text); + +} + +void cflip_reset(CFlipDialog *cflip_dialog) { + + reflist_free(cflip_dialog->cf_reflections); + cflip_dialog->cf_reflections = reflist_copy(main_reflist()); + + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + cflip_dialog->n_iterations = 0; + cflip_update_iterations(cflip_dialog); + ReflectionList *reflections = cflip_dialog->cf_reflections; + + unsigned int i; + + for ( i=0; in_reflections; i++ ) { + + double am, ph; + signed int h, k; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + am = reflections->refs[i].amplitude; + ph = (((double)random())/RAND_MAX) * (2*M_PI); + + cflip_dialog->cf_in[k + height*h][0] = am*cos(ph); + cflip_dialog->cf_in[k + height*h][1] = am*sin(ph); + + } + symmetry_symmetrise_array(cflip_dialog->cf_in, width, height, cflip_dialog->cf_symmetry & SYMMETRY_FRIEDEL); + fftw_execute(cflip_dialog->cf_plan_i2d); + + memcpy(cflip_dialog->cf_out, cflip_dialog->cf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + + //dpsynth_update(cflip_dialog->dpsynth, reflections); + +} + +void cflip_initialise(CFlipDialog *cflip_dialog) { + + ReflectionList *reflections = reflist_copy(main_reflist()); + Symmetry symmetry; + cflip_dialog->width = data_width(); + cflip_dialog->height = data_height(); + cflip_dialog->n_iterations = 0; + cflip_dialog->period = 1; + unsigned int width = cflip_dialog->width; + unsigned int height = cflip_dialog->height; + symmetry = cflip_dialog->cf_symmetry; + + cflip_dialog->cf_drive_old = fftw_malloc(width * height * sizeof(fftw_complex)); + cflip_dialog->cf_drive = fftw_malloc(width * height * sizeof(fftw_complex)); + cflip_dialog->cf_support = fftw_malloc(width * height * sizeof(unsigned int)); + cflip_dialog->cf_in = fftw_malloc(width * height * sizeof(fftw_complex)); + memset(cflip_dialog->cf_in, 0, width * height * sizeof(fftw_complex)); + cflip_dialog->cf_out = fftw_malloc(width * height * sizeof(fftw_complex)); + + cflip_dialog->cf_plan_i2d = fftw_plan_dft_2d(width, height, cflip_dialog->cf_in, cflip_dialog->cf_drive, FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + cflip_dialog->cf_plan_d2i = fftw_plan_dft_2d(width, height, cflip_dialog->cf_drive, cflip_dialog->cf_in, FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + cflip_dialog->cf_reflections = reflections; + + displaywindow_set_realspace(cflip_dialog->cf_out, DWR_GSF); + //cflip_dialog->dpsynth = dpsynth_open(reflections, "Current Diffraction Pattern", 1); + + cflip_reset(cflip_dialog); + +} + +static gint cflip_window_go(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + + if ( cflip_dialog->running ) { + return 0; + } + cflip_dialog->running = 1; + while ( cflip_dialog->running ) { + cflip_dialog->n_iterations++; + cflip_update_iterations(cflip_dialog); + cflip_iteration(cflip_dialog); + while ( gtk_events_pending() ) gtk_main_iteration(); + } + return 0; + +} + +static gint cflip_window_next(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->n_iterations++; + cflip_update_iterations(cflip_dialog); + cflip_iteration(cflip_dialog); + + return 0; +} + +static gint cflip_window_stop(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->running = 0; + return 0; + +} + +static gint cflip_window_reset(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->running = 0; + cflip_reset(cflip_dialog); + return 0; +} + +static gint cflip_symmetry_changed(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->cf_symmetry = gtk_symmetry_get_symmetry(GTK_SYMMETRY(widget)); + return 0; +} + +GtkWidget *cfw_tangent; +void cflip_tangent_toggled(GtkWidget *widget, CFlipDialog *cflip_dialog) { + + cflip_dialog->cf_tangent = 1-cflip_dialog->cf_tangent; + +} + +void cflip_dialog_open() { + + GtkWidget *cfw_window; + GtkWidget *cfw_period; + GtkWidget *cfw_period_hbox; + GtkWidget *cfw_threshsup_delta; + GtkWidget *cfw_threshsup_delta_label; + GtkWidget *cfw_threshsup_hbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *sym_define; + GtkWidget *cfw_support_label; + GtkWidget *cfw_process_label; + GtkWidget *cfw_period_label; + GtkWidget *cfw_gostop_hbox; + GtkWidget *cfw_tangent; + char *text; + CFlipDialog *cflip_dialog; + + cflip_dialog = malloc(sizeof(CFlipDialog)); + + if ( cf_window_open ) { + return; + } + cf_window_open = 1; + + cflip_dialog->running = 0; + cflip_dialog->cf_tangent = 0; + cflip_dialog->n_iterations = 0; + cflip_dialog->delta = 0; + + cfw_window = gtk_dialog_new_with_buttons("Charge Flipping", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cfw_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + cflip_dialog->cf_iterations = gtk_label_new(""); + text = malloc(40); + snprintf(text, 39, "Iterations performed so far: %i", cflip_dialog->n_iterations); + gtk_label_set_text(GTK_LABEL(cflip_dialog->cf_iterations), text); + free(text); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cflip_dialog->cf_iterations), FALSE, FALSE, 5); + + sym_define = gtk_symmetry_new(2, 2, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(sym_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(sym_define), "changed", G_CALLBACK(cflip_symmetry_changed), cflip_dialog); + cflip_dialog->cf_symmetry = PLANEGROUP_P1; + + cfw_period_label = gtk_label_new("Iterations to symmetrize:"); + gtk_misc_set_alignment(GTK_MISC(cfw_period_label), 1, 0.5); + cfw_period = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(cfw_period), 5); + gtk_entry_set_text(GTK_ENTRY(cfw_period), "1"); + cfw_period_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(cfw_period_hbox), GTK_WIDGET(cfw_period_label), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_period_hbox), GTK_WIDGET(cfw_period), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_period_hbox), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(cfw_period), "activate", G_CALLBACK(cflip_window_periodchanged), cflip_dialog); + + cfw_support_label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(cfw_support_label), "Threshold"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_support_label), FALSE, FALSE, 10); + + cfw_tangent = gtk_check_button_new_with_label("Coehlo Constraints"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_tangent), FALSE, FALSE, 5); + if ( cflip_dialog->cf_tangent ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_tangent), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_tangent), FALSE); + } + + cfw_process_label = gtk_label_new("Maximum unflipped intensity"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_process_label), FALSE, FALSE, 5); + + cfw_threshsup_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_threshsup_hbox), FALSE, FALSE, 5); + cfw_threshsup_delta_label = gtk_label_new("δ:"); + gtk_box_pack_start(GTK_BOX(cfw_threshsup_hbox), GTK_WIDGET(cfw_threshsup_delta_label), FALSE, FALSE, 5); + cfw_threshsup_delta = gtk_hscale_new_with_range(-5, 5, 0.1); + gtk_scale_set_value_pos(GTK_SCALE(cfw_threshsup_delta), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(cfw_threshsup_hbox), GTK_WIDGET(cfw_threshsup_delta), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(cfw_threshsup_delta), cflip_dialog->delta); + + gtk_widget_set_sensitive(cfw_threshsup_delta, TRUE); + gtk_widget_set_sensitive(cfw_threshsup_delta_label, TRUE); + + cfw_gostop_hbox = gtk_hbox_new(FALSE, 0); + cflip_dialog->cf_go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(cflip_dialog->cf_go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + cflip_dialog->cf_next = gtk_button_new_with_label("Next"); + gtk_button_set_image(GTK_BUTTON(cflip_dialog->cf_next), gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, GTK_ICON_SIZE_BUTTON)); + cflip_dialog->cf_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + cflip_dialog->cf_reset = gtk_button_new_with_label("Reset"); + gtk_button_set_image(GTK_BUTTON(cflip_dialog->cf_reset), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_reset), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_stop), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_next), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(cfw_gostop_hbox), GTK_WIDGET(cflip_dialog->cf_go), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cfw_gostop_hbox), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(cflip_dialog->cf_go), "clicked", G_CALLBACK(cflip_window_go), cflip_dialog); + g_signal_connect(G_OBJECT(cflip_dialog->cf_stop), "clicked", G_CALLBACK(cflip_window_stop), cflip_dialog); + g_signal_connect(G_OBJECT(cflip_dialog->cf_next), "clicked", G_CALLBACK(cflip_window_next), cflip_dialog); + g_signal_connect(G_OBJECT(cflip_dialog->cf_reset), "clicked", G_CALLBACK(cflip_window_reset), cflip_dialog); + g_signal_connect(G_OBJECT(cfw_tangent), "toggled", G_CALLBACK(cflip_tangent_toggled), cflip_dialog); + g_signal_connect(G_OBJECT(cfw_window), "response", G_CALLBACK(cflip_window_close), cflip_dialog); + g_signal_connect(G_OBJECT(cfw_threshsup_delta), "value-changed", G_CALLBACK(cflip_threshsup_deltachanged), cflip_dialog); + g_signal_connect(G_OBJECT(displaywindow_gtkwindow()), "delete-event", G_CALLBACK(cflip_window_stop), cflip_dialog); + + cflip_initialise(cflip_dialog); + displaywindow_forceview(DWV_REALSPACE); + + gtk_widget_show_all(cfw_window); + +} diff --git a/src/cflip.h b/src/cflip.h new file mode 100644 index 0000000..e39e07b --- /dev/null +++ b/src/cflip.h @@ -0,0 +1,28 @@ +/* + * cflip.h + * + * Charge -flipping algorithms + * + * (c) 2006-2007 Thomas White + * (c) 2008 Alexander Eggeman + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CFLIP_H +#define CFLIP_H + +#include "reflist.h" +#include "symmetry.h" +#include "gtk-symmetry.h" +#include "cdm.h" + + +extern void cflip_dialog_open(void); + +#endif /* CFLIP_H */ diff --git a/src/clean.c b/src/clean.c new file mode 100644 index 0000000..1335a2f --- /dev/null +++ b/src/clean.c @@ -0,0 +1,544 @@ +/* + * clean.c + * + * CLEAN Algorithm + * + * (c) 2006-2007 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include "displaywindow.h" +#include "main.h" +#include "data.h" +#include "png-file.h" + +static void clean_noise_open(fftw_complex *dirty, double max_am); + +/* ........................ The Maths ........................ */ + + +static double clean_tau = 0.1; + +static void clean_reconvolve(fftw_complex *cleaned, double ax, double ay) { + + signed int h, k; + unsigned int x, y; + fftw_complex *in; + signed int height = data_height(); + signed int width = data_width(); + fftw_plan plan, planr; + fftw_complex *clean_beam_reciprocal; + + in = fftw_malloc(width*height*sizeof(fftw_complex)); + clean_beam_reciprocal = fftw_malloc(width*height*sizeof(fftw_complex)); + for ( x=0; xn_reflections; i++ ) { + signed int h, k; + h = reflections->refs[i].h; + k = reflections->refs[i].k; + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + delta_array[k + height*h][0] = 1; + delta_array[k + height*h][1] = 0; /* Phase = zero */ + } + aperture = fftw_malloc(width*height*sizeof(fftw_complex)); + plan = fftw_plan_dft_2d(width, height, delta_array, aperture, FFTW_BACKWARD, FFTW_MEASURE); + fftw_execute(plan); + fftw_destroy_plan(plan); + fftw_free(delta_array); + + /* Find the maximum *amplitude* in the function */ + aperture_max = 0; + for ( x=0; x aperture_max ) aperture_max = am; + } + } + + /* Normalise the aperture function */ + for ( x=0; x fabs(max_am) ) { + if ( positive ) { + /* See if this subtraction would result in a negative function */ + if ( (cleaned[y+height*x][0] + tau*re ) < 0 ) continue; + } + max_am = am; max_re = re; max_im = im; max_x = x; max_y = y; + } + + } + } + + /* Add tau*peak to clean map */ + cleaned[max_y+height*max_x][0] += tau * max_re; + cleaned[max_y+height*max_x][1] += tau * max_im; + + /* Subtract tau*peak*aperture from the dirty map */ + for ( x=0; x fabs(cutoff) ); + + fftw_free(aperture); + + printf("CL: %i iterations performed\n", npx); + + clean_noise_open(dirty, max_am); + free(dirty); + + max_am = 0; + for ( x=0; x fabs(max_am) ) max_am = am; + } + } + + if ( convolve ) clean_reconvolve(cleaned, ax, ay); + + displaywindow_set_realspace(cleaned, DWR_CLEAN); + displaywindow_forceview(DWV_REALSPACE); + displaywindow_switchview(); + +} + +/* ..................... The Other Stuff ..................... */ + +typedef struct { + GtkWidget *tau; /* Loop gain */ + GtkWidget *cutoff; /* Cutoff */ + GtkWidget *conv; /* Reconvolve? */ + GtkWidget *pos; /* Constrain positive */ + GtkWidget *ax; /* CLEAN beam ax */ + GtkWidget *ay; /* CLEAN beam ay */ +} CleanDialog; + +CleanDialog *clean_dialog = NULL; + +static void clean_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +static void clean_plot_graph(char dim) { + + FILE *gnuplot; + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + clean_gpwrite(gnuplot, "set autoscale\n"); + clean_gpwrite(gnuplot, "unset log\n"); + clean_gpwrite(gnuplot, "unset label\n"); + clean_gpwrite(gnuplot, "set xtic auto\n"); + clean_gpwrite(gnuplot, "set ytic auto\n"); + clean_gpwrite(gnuplot, "set grid\n"); + clean_gpwrite(gnuplot, "set ylabel 'Amplitude' font \"Helvetica,14\"\n"); + if ( dim == 'x' ) clean_gpwrite(gnuplot, "set xlabel 'x / pixels' font \"Helvetica,14\"\n"); + if ( dim == 'y' ) clean_gpwrite(gnuplot, "set xlabel 'y / pixels' font \"Helvetica,14\"\n"); + if ( dim == 'x' ) clean_gpwrite(gnuplot, "set title 'x-component of aperture' font \"Helvetica,14\"\n"); + if ( dim == 'y' ) clean_gpwrite(gnuplot, "set title 'y-component of aperture' font \"Helvetica,14\"\n"); + clean_gpwrite(gnuplot, "plot 'synth2d-aperturefit.dat' with lines\n"); + clean_gpwrite(gnuplot, "replot 'synth2d-aperturefit-peaks.dat' with points\n"); + clean_gpwrite(gnuplot, "a=0.01\n"); + clean_gpwrite(gnuplot, "fit a**2/(a**2+x**2) 'synth2d-aperturefit-peaks.dat' via a\n"); + clean_gpwrite(gnuplot, "set label 'a = %3.5g px',a at graph 0.6, graph 0.7 font \"Helvetica,14\"\n"); + clean_gpwrite(gnuplot, "replot a**2/(a**2+x**2)\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} + +static gint clean_calculate_clean_beam(GtkWidget *cleanw_fit, gpointer data) { + + fftw_complex *aperture; + FILE *fh; + FILE *fh_fit; + unsigned int i, width, height; + double last_am; + + width = data_width(); height = data_height(); + aperture = clean_aperture(main_reflist(), width, height); + + fh = fopen("synth2d-aperturefit.dat", "w"); + fh_fit = fopen("synth2d-aperturefit-peaks.dat", "w"); + last_am = INFINITY; + for ( i=0; i last_am) && (am > next_am) ) fprintf(fh_fit, "%i %f\n", i, am); + + last_am = am; + + } + fclose(fh); + fclose(fh_fit); + clean_plot_graph('x'); + + fh = fopen("synth2d-aperturefit.dat", "w"); + fh_fit = fopen("synth2d-aperturefit-peaks.dat", "w"); + last_am = INFINITY; + for ( i=0; i last_am) && (am > next_am) ) fprintf(fh_fit, "%i %f\n", i, am); + + last_am = am; + + } + fclose(fh); + fclose(fh_fit); + clean_plot_graph('y'); + + fftw_free(aperture); + return 0; + +} + +static gint clean_window_close(GtkWidget *widget, gint response, CleanDialog *this_clean_dialog) { + + if ( response == GTK_RESPONSE_OK ) { + + int n; + double tau; + float cutoff, ax, ay; + const char *tmp; + + n = 1; + tmp = gtk_entry_get_text(GTK_ENTRY(this_clean_dialog->cutoff)); + if ( sscanf(tmp, "%f", &cutoff) != 1 ) n = 0; + tmp = gtk_entry_get_text(GTK_ENTRY(this_clean_dialog->ax)); + if ( sscanf(tmp, "%f", &ax) != 1 ) n = 0; + tmp = gtk_entry_get_text(GTK_ENTRY(this_clean_dialog->ay)); + if ( sscanf(tmp, "%f", &ay) != 1 ) n = 0; + + if ( n == 1 ) { + tau = gtk_range_get_value(GTK_RANGE(this_clean_dialog->tau)); + clean_execute(displaywindow_outarray(), main_reflist(), + tau, + cutoff, + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(this_clean_dialog->pos)), + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(this_clean_dialog->conv)), + ax, ay); + } else { + return 1; + } + + } + + gtk_widget_destroy(widget); + free(clean_dialog); + clean_dialog = NULL; + + return 0; + +} + +void clean_dialog_open(GtkWidget *widget, gpointer data) { + + GtkWidget *clean_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *cleanw_tau_label; + GtkWidget *cleanw_cutoff_label; + GtkWidget *cleanw_fit; + GtkWidget *cleanw_xa_label; + GtkWidget *cleanw_xa_unit_label; + GtkWidget *cleanw_ya_label; + GtkWidget *cleanw_ya_unit_label; + double max_peak = 2000; + char str[16]; + + if ( clean_dialog ) return; + clean_dialog = malloc(sizeof(CleanDialog)); + + clean_window = gtk_dialog_new_with_buttons("CLEAN Image", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(clean_window)->vbox), hbox, FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 5); + table = gtk_table_new(7, 3, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 5); + + cleanw_tau_label = gtk_label_new("Loop Gain:"); + gtk_misc_set_alignment(GTK_MISC(cleanw_tau_label), 1, 0.5); + clean_dialog->tau = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_value_pos(GTK_SCALE(clean_dialog->tau), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(clean_dialog->tau), clean_tau); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_tau_label, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->tau, 2, 4, 1, 2); + + cleanw_cutoff_label = gtk_label_new("Cutoff:"); + gtk_misc_set_alignment(GTK_MISC(cleanw_cutoff_label), 1, 0.5); + clean_dialog->cutoff = gtk_entry_new(); + max_peak = displaywindow_maxpeak(); + snprintf(str, 15, "%f", max_peak/200); + gtk_entry_set_text(GTK_ENTRY(clean_dialog->cutoff), str); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_cutoff_label, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->cutoff, 2, 4, 2, 3); + + clean_dialog->pos = gtk_check_button_new_with_label("Constrain to be Positive"); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->pos, 1, 4, 3, 4); + + clean_dialog->conv = gtk_check_button_new_with_label("Reconvolve with CLEAN beam"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(clean_dialog->conv), TRUE); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->conv, 1, 4, 4, 5); + + cleanw_xa_label = gtk_label_new("a_x = "); + gtk_label_set_markup(GTK_LABEL(cleanw_xa_label), "ax = "); + gtk_misc_set_alignment(GTK_MISC(cleanw_xa_label), 1, 0.5); + clean_dialog->ax = gtk_entry_new(); + cleanw_xa_unit_label = gtk_label_new("px"); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_xa_label, 1, 2, 5, 6); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->ax, 2, 3, 5, 6); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_xa_unit_label, 3, 4, 5, 6); + + cleanw_ya_label = gtk_label_new("a_y = "); + gtk_label_set_markup(GTK_LABEL(cleanw_ya_label), "ay = "); + gtk_misc_set_alignment(GTK_MISC(cleanw_ya_label), 1, 0.5); + clean_dialog->ay = gtk_entry_new(); + cleanw_ya_unit_label = gtk_label_new("px"); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_ya_label, 1, 2, 6, 7); + gtk_table_attach_defaults(GTK_TABLE(table), clean_dialog->ay, 2, 3, 6, 7); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_ya_unit_label, 3, 4, 6, 7); + + cleanw_fit = gtk_button_new_with_label("Determine CLEAN beam"); + gtk_table_attach_defaults(GTK_TABLE(table), cleanw_fit, 1, 4, 7, 8); + g_signal_connect(G_OBJECT(cleanw_fit), "clicked", G_CALLBACK(clean_calculate_clean_beam), clean_dialog); + + g_signal_connect(G_OBJECT(clean_window), "response", G_CALLBACK(clean_window_close), clean_dialog); + gtk_widget_show_all(clean_window); + +} + +void clean_aperture_open(ReflectionList *reflections) { + + GtkWidget *clean_aperture_window; + GdkPixbuf *clean_aperture_pixbuf; + GtkWidget *clean_aperture_pixmap_widget; + fftw_complex *aperture; + signed int width = data_width(); + signed int height = data_height(); + + aperture = clean_aperture(reflections, width, height); + + clean_aperture_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(clean_aperture_window), "Aperture Function"); + clean_aperture_pixbuf = displaywindow_render_pixbuf(aperture, 0.05, data_width(), data_height(), data_gamma(), 1, 1); + fftw_free(aperture); + clean_aperture_pixmap_widget = gtk_image_new_from_pixbuf(clean_aperture_pixbuf); + gtk_container_add(GTK_CONTAINER(clean_aperture_window), clean_aperture_pixmap_widget); + gtk_widget_show_all(clean_aperture_window); + +} + +static void clean_noise_open(fftw_complex *dirty, double max_am) { + + GtkWidget *clean_aperture_window; + GdkPixbuf *clean_aperture_pixbuf; + GtkWidget *clean_aperture_pixmap_widget; + + clean_aperture_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(clean_aperture_window), "CLEAN Residual"); + clean_aperture_pixbuf = displaywindow_render_pixbuf(dirty, max_am, data_width(), data_height(), data_gamma(), 1, 1); + clean_aperture_pixmap_widget = gtk_image_new_from_pixbuf(clean_aperture_pixbuf); + gtk_container_add(GTK_CONTAINER(clean_aperture_window), clean_aperture_pixmap_widget); + gtk_widget_show_all(clean_aperture_window); + +} + diff --git a/src/clean.h b/src/clean.h new file mode 100644 index 0000000..c8dc4af --- /dev/null +++ b/src/clean.h @@ -0,0 +1,27 @@ +/* + * clean.h + * + * CLEAN Algorithm + * + * (c) 2006-2007 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CLEAN_H +#define CLEAN_H + +#include + +#include "reflist.h" + +extern void clean_dialog_open(GtkWidget *widget, gpointer data); +extern void clean_aperture_open(ReflectionList *reflections); + +#endif /* CLEAN_H */ + diff --git a/src/colwheel.c b/src/colwheel.c new file mode 100644 index 0000000..95f551c --- /dev/null +++ b/src/colwheel.c @@ -0,0 +1,153 @@ +/* + * colwheel.c + * + * Colour wheel definition and visualisation + * + * (c) 2006-2007 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "displaywindow.h" + +#define COLWHEEL_WIDTH 256 +#define COLWHEEL_SIZE ((COLWHEEL_WIDTH)-1) +#define COLWHEEL_HALF ((COLWHEEL_WIDTH/2)-1) + +#define rad2deg(a) ((a)*180.0/M_PI) +#define deg2rad(a) ((a)*M_PI/180.0) + +gint colwheel_show(GtkWidget *widget, gpointer data) { + + GtkWidget *colwheel_window; + GdkPixbuf *colwheel_pixbuf; + GtkWidget *colwheel_pixmap_widget; + fftw_complex *colwheel; + + unsigned int x, y; + double am, ph; + + /* This isn't being transformed, so no need to use fftw_malloc() */ + colwheel = malloc(COLWHEEL_SIZE*COLWHEEL_SIZE*sizeof(fftw_complex)); + for ( x=0; x= 360.0 ) ph = 0.0; + d = ph / 60.0; + + hi = floor(d); /* Divide the colour wheel into six sections */ + f = d - hi; /* Distance into the current section of the colour wheel */ + if ( hi == 6 ) hi = 0; + switch ( hi ) { + case 0 : return am; + case 1 : return am; + case 2 : return am*(1.0-f); + case 3 : return 0.0; + case 4 : return am*f/2.0; + case 5 : return am/2.0+am*f/2.0; + default : return 0.0; + } + +} + +double colwheel_green(double am, double ph) { + + double f; + unsigned int hi; + double d; + + ph = rad2deg(ph); /* Convert to degrees */ + ph += 180.0; /* Rotation of colour wheel */ + if ( ph >= 360.0 ) ph = 0.0; + d = ph / 60.0; + + hi = floor(d); /* Divide the colour wheel into six sections */ + f = d - hi; /* Distance into the current section of the colour wheel */ + if ( hi == 6 ) hi = 0; + switch ( hi ) { + case 0 : return am*f/2.0; + case 1 : return am/2.0+am*f/2.0; + case 2 : return am; + case 3 : return am*(1.0-f); + case 4 : return 0.0; + case 5 : return 0.0; + default : return 0.0; + } + +} + +double colwheel_red(double am, double ph) { + + double f; + unsigned int hi; + double d; + + ph = rad2deg(ph); /* Convert to degrees */ + ph += 180.0; /* Rotation of colour wheel */ + if ( ph >= 360.0 ) ph = 0.0; + d = ph / 60.0; + + hi = floor(d); /* Divide the colour wheel into six sections */ + f = d - hi; /* Distance into the current section of the colour wheel */ + if ( hi == 6 ) hi = 0; + switch ( hi ) { + case 0 : return 0.0; + case 1 : return 0.0; + case 2 : return 0.0; + case 3 : return am*f; + case 4 : return am; + case 5 : return am*(1.0-f); + default : return 0.0; + } + +} diff --git a/src/colwheel.h b/src/colwheel.h new file mode 100644 index 0000000..d925477 --- /dev/null +++ b/src/colwheel.h @@ -0,0 +1,26 @@ +/* + * colwheel.h + * + * Colour wheel definition and visualisation + * + * (c) 2006-2008 Thomas White + * + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef COLWHEEL_H +#define COLWHEEL_H + +#include + +extern gint colwheel_show(GtkWidget *widget, gpointer data); +extern double colwheel_red(double am, double ph); +extern double colwheel_green(double am, double ph); +extern double colwheel_blue(double am, double ph); + +#endif /* COLWHEEL_H */ diff --git a/src/contourise.c b/src/contourise.c new file mode 100644 index 0000000..56e10b8 --- /dev/null +++ b/src/contourise.c @@ -0,0 +1,441 @@ +/* + * contourise.c + * + * Draw contour maps + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "model.h" +#include "data.h" +#include "renderer.h" + +typedef struct { + GtkWidget *rad_mod; + GtkWidget *rad_mod_sign; + GtkWidget *rad_real; + GtkWidget *rad_imag; + GtkWidget *n_contours; + int nx; + int ny; +} ContourDialog; + +typedef enum { + VALUES_NONE, + VALUES_MODULUS, + VALUES_MODULUS_SIGN, + VALUES_REAL, + VALUES_IMAGINARY +} ContourValues; + +static GtkWidget *contourise_dialog = NULL; + +static void griwrite(FILE *gri, const char *string) +{ + fwrite(string, strlen(string), 1, gri); +} + +static void contourise_map(fftw_complex *out, unsigned int width, + unsigned int height, ContourValues vals, + int nx, int ny, int ncont) +{ + FILE *gri; + char tmp[1024]; + double maxval; + int model_marks; + int xn, yn; + pid_t pid; + int status; + size_t width_n, height_n; + double gamma; + double aspect; + int landscape; /* 1=landscape, 0=portrait */ + int fit_x; /* 1=fit the x-axis to the page, 0=fit the y-axis */ + ComplexArray cxar; + double xsize, ysize, scale; + + gamma = data_gamma(); + + if ( ( displaywindow_mode() == DWV_MODEL ) + || ( displaywindow_mode() == DWV_DIFFERENCE ) + || ( displaywindow_mode() == DWV_EXITWAVE ) + || ( displaywindow_mode() == DWV_REFSYN ) ) { + model_marks = 1; + } else { + model_marks = 0; + } + + width_n = renderer_width(width, height, gamma, nx, ny); + height_n = renderer_height(width, height, gamma, nx, ny); + + maxval = displaywindow_max(); + + gri = popen("gri -b -no_startup_message", "w"); + if ( !gri ) { + fprintf(stderr, "Couldn't invoke gri. Please check your PATH."); + return; + } + + /* Initialise */ + griwrite(gri, "set postscript filename synth2d.ps\n"); + griwrite(gri, "set page size A4\n"); + snprintf(tmp, 1023, "set x grid 0 1 /%i\n", width_n); + griwrite(gri, tmp); + snprintf(tmp, 1023, "set y grid 0 1 /%i\n", height_n); + griwrite(gri, tmp); + + /* Figure out the size */ + aspect = (double)height_n/width_n; + if ( width_n > height_n ) { + landscape = 1; + if ( aspect < (19.0/27.7) ) { + fit_x = 1; + } else { + fit_x = 0; + } + } else { + landscape = 0; + if ( aspect < (27.7/19.0) ) { + fit_x = 1; + } else { + fit_x = 0; + } + } + if ( landscape ) { + if ( fit_x ) { + xsize = 27.7; + ysize = 27.7*aspect; + scale = 27.7/width_n; + } else { + xsize = 19.0/aspect; + ysize = 19.0; + scale = 19.0/height_n; + } + } else { + if ( fit_x ) { + xsize = 19.0; + ysize = 19.0*aspect; + scale = 19.0/width_n; + } else { + xsize = 27.7/aspect; + ysize = 27.7; + scale = 27.7/height_n; + } + } + printf("CO: aspect=%f => selected ", aspect); + if ( landscape ) { + printf("landscape, "); + griwrite(gri, "set page landscape\n"); + } else { + printf("portrait, "); + griwrite(gri, "set page portrait\n"); + } + if ( fit_x ) { + printf("fitting x, "); + } else { + printf("fitting y, "); + } + snprintf(tmp, 1023, "set x size %f\n", xsize); + griwrite(gri, tmp); + snprintf(tmp, 1023, "set y size %f\n", ysize); + griwrite(gri, tmp); + printf("width=%f height=%f\n", xsize, ysize); + + griwrite(gri, "set x margin 1\n"); /* cm */ + griwrite(gri, "set y margin 1\n"); /* cm */ + griwrite(gri, "set axes style none\n"); + griwrite(gri, "set line width 3.0\n"); + if ( landscape ) { + if ( fit_x ) { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+27.7, 1.0+27.7*aspect); + griwrite(gri, tmp); + } else { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+19.0/aspect, 1.0+19.0); + griwrite(gri, tmp); + } + } else { + if ( fit_x ) { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+19.0, 1.0+19.0*aspect); + griwrite(gri, tmp); + } else { + snprintf(tmp, 1023, "draw box 1 1 %f %f cm\n", + 1.0+27.7/aspect, 1.0+27.7); + griwrite(gri, tmp); + } + } + + /* Read the data and plot contours */ + griwrite(gri, "read grid data\n"); + + /* Give it the data to read... */ + cxar = renderer_draw(out, width, height, gamma, nx, ny); + for ( yn=height_n-1; yn>=0; yn-- ) { + + int first = 1; + + for ( xn=0; xnn_atoms; i++ ) { + + double p, q; + int xc, yc; + + p = model->atoms[i].x; + q = model->atoms[i].y; + + for ( xc=0; xcrad_mod)) ) + vals = VALUES_MODULUS; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + cd->rad_mod_sign)) ) + vals = VALUES_MODULUS_SIGN; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + cd->rad_real)) ) + vals = VALUES_REAL; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON( + cd->rad_imag)) ) + vals = VALUES_IMAGINARY; + if ( vals == VALUES_NONE ) error = 1; + + ncont_s = gtk_entry_get_text(GTK_ENTRY(cd->n_contours)); + if ( sscanf(ncont_s, "%i", &ncont) != 1 ) { + error_report("Invalid number of contours"); + error = 1; + } else { + if ( ncont < 1 ) { + error_report("Invalid number of contours"); + error = 1; + } + } + + if ( error == 0 ) { + contourise_map(displaywindow_outarray(), + data_width(), data_height(), + vals, + cd->nx, cd->ny, ncont); + } + + } + + if ( error == 0 ) { + gtk_widget_destroy(window); + contourise_dialog = NULL; + free(cd); + } + + return 0; +} + +void contourise_dialog_open(int nx, int ny) +{ + ContourDialog *cd; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *text; + GtkWidget *ncont_hbox; + + if ( contourise_dialog ) { + return; + } + + contourise_dialog = gtk_dialog_new_with_buttons("Contour Map", + GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + cd = malloc(sizeof(ContourDialog)); + if ( !cd ) return; + cd->nx = nx; /* Number of unit cells */ + cd->ny = ny; /* Store these for later */ + + /* Structure */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(contourise_dialog)->vbox), + GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 5); + + text = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(text), + "" + "What do you want to plot?"); + gtk_label_set_line_wrap(GTK_LABEL(text), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, FALSE, 5); + + cd->rad_mod_sign = gtk_radio_button_new_with_label(NULL, + "Modulus with sign: √[Re(V)² + Im(V)²] × sign[Re(V)]"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_mod_sign, FALSE, FALSE, 2); + cd->rad_mod = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(cd->rad_mod_sign), + "Modulus alone: √[Re(V)² + Im(V)²]"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_mod, FALSE, FALSE, 2); + cd->rad_real = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(cd->rad_mod_sign), + "Real part: Re(V)"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_real, FALSE, FALSE, 2); + cd->rad_imag = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(cd->rad_mod_sign), + "Imaginary part: Im(V)"); + gtk_box_pack_start(GTK_BOX(vbox), cd->rad_imag, FALSE, FALSE, 2); + + text = gtk_label_new("Number of contours:"); + gtk_misc_set_alignment(GTK_MISC(text), 1.0, 0.5); + ncont_hbox = gtk_hbox_new(FALSE, 2); + gtk_box_pack_start(GTK_BOX(ncont_hbox), text, FALSE, TRUE, 2); + gtk_box_pack_end(GTK_BOX(vbox), ncont_hbox, FALSE, FALSE, 2); + cd->n_contours = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(cd->n_contours), 4); + gtk_box_pack_start(GTK_BOX(ncont_hbox), cd->n_contours, FALSE, TRUE, 2); + gtk_entry_set_text(GTK_ENTRY(cd->n_contours), "20"); + + g_signal_connect(G_OBJECT(contourise_dialog), "response", + G_CALLBACK(contourise_dialog_close), cd); + gtk_widget_show_all(contourise_dialog); +} diff --git a/src/contourise.h b/src/contourise.h new file mode 100644 index 0000000..9d81922 --- /dev/null +++ b/src/contourise.h @@ -0,0 +1,21 @@ +/* + * contourise.h + * + * Draw contour maps + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CONTOURISE_H +#define CONTOURISE_H + +extern void contourise_dialog_open(int nx, int ny); + +#endif /* CONTOURISE_H */ diff --git a/src/correspondence.c b/src/correspondence.c new file mode 100644 index 0000000..e745d42 --- /dev/null +++ b/src/correspondence.c @@ -0,0 +1,73 @@ +/* + * correspondence.c + * + * Plot of calculated against measured structure factors + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "main.h" +#include "statistics.h" +#include "displaywindow.h" +#include "model.h" + +static void correspondence_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void correspondence_show() { + + FILE *fh; + unsigned int i; + FILE *gnuplot; + ReflectionList *reflections; + ReflectionList *model_reflections; + double scale; + + reflections = main_reflist(); + model_reflections = model_calculate_f(reflections, NULL, 69); + + scale = stat_scale(reflections, model_reflections); + + fh = fopen("synth2d-correspondence.dat", "w"); + for ( i=1; in_reflections; i++ ) { + fprintf(fh, "%f %f\n", scale*model_reflections->refs[i].amplitude, reflections->refs[i].amplitude); + } + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + correspondence_gpwrite(gnuplot, "set autoscale\n"); + correspondence_gpwrite(gnuplot, "unset log\n"); + correspondence_gpwrite(gnuplot, "unset label\n"); + correspondence_gpwrite(gnuplot, "set xtic auto\n"); + correspondence_gpwrite(gnuplot, "set ytic auto\n"); + correspondence_gpwrite(gnuplot, "set grid\n"); + correspondence_gpwrite(gnuplot, "set ylabel '|Fobs|' font \"Helvetica,10\"\n"); + correspondence_gpwrite(gnuplot, "set xlabel '|Fcalc|' font \"Helvetica,10\"\n"); + correspondence_gpwrite(gnuplot, "set title 'Correspondence Plot' font \"Helvetica,10\"\n"); + correspondence_gpwrite(gnuplot, "plot [0:] [0:] 'synth2d-correspondence.dat'\n"); + correspondence_gpwrite(gnuplot, "replot\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} diff --git a/src/correspondence.h b/src/correspondence.h new file mode 100644 index 0000000..fdc4622 --- /dev/null +++ b/src/correspondence.h @@ -0,0 +1,20 @@ +/* + * correspondence.h + * + * Plot of calculated against measured structure factors + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef CORRESPONDENCE_H +#define CORRESPONDENCE_H + +extern void correspondence_show(void); + +#endif /* CORRESPONDENCE_H */ diff --git a/src/data.c b/src/data.c new file mode 100644 index 0000000..5a2829f --- /dev/null +++ b/src/data.c @@ -0,0 +1,162 @@ +/* + * data.c + * + * Handle the input data + * + * (c) 2006-2008 Thomas White + * + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "reflist.h" +#include "displaywindow.h" + +signed int data_width_hidden = 512; +signed int data_height_hidden = 512; +float data_a_hidden = 1; +float data_b_hidden = 1; +float data_c_hidden = 1; +double data_gamma_hidden = M_PI_2; +ReflectionList *data_reflections = NULL; +int data_image_scale = 500; + +unsigned int data_width() { return data_width_hidden; } +unsigned int data_height() { return data_height_hidden; } +double data_a() { return data_a_hidden; } +double data_b() { return data_b_hidden; } +double data_c() { return data_c_hidden; } +double data_gamma() { return data_gamma_hidden; } +unsigned int data_get_image_scale() { return data_image_scale; } + +static void data_calculatesize() { + data_width_hidden = data_a_hidden*data_image_scale; + data_height_hidden = data_b_hidden*data_image_scale; +} + +void data_dividecell(unsigned int na, unsigned int nb, unsigned int nc) { + data_a_hidden /= na; + data_b_hidden /= nb; + data_c_hidden /= nc; + data_calculatesize(); +} + +int data_read(const char *filename) { + + FILE *fh; + char *line; + char *rval; + unsigned int whoops = 0; + float gamma_angle = 90; + signed int laue = 0; + + if ( data_reflections ) { + free(data_reflections); + } + data_reflections = reflist_new(); + + fh = fopen(filename, "r"); + if ( !fh ) { + fprintf(stderr, "DA: Couldn't open file %s\n", filename); + return 1; + } + line = malloc(1024); + rval = "hello"; + while ( rval ) { + + signed int h, k, l; + float am, intensity; + float ph = 0; + int res; + + rval = fgets(line, 1023, fh); + if ( ferror(fh) && !feof(fh) ) { + whoops = 1; + break; + } + if ( strlen(line) > 1 ) { + if ( line[strlen(line)-1] == '\n' ) { + line[strlen(line)-1] = '\0'; /* Cut off trailing newline. */ + } + } else { + line[0] = '\0'; + } + + res = sscanf(line, "%3i %3i %3i %f %f", &h, &k, &l, &intensity, &ph); + if ( intensity > 0.0) am = sqrt(intensity); + if ( (res < 4) && gamma_angle ) { + sscanf(line, "angle %f", &gamma_angle); + sscanf(line, "a %f", &data_a_hidden); + sscanf(line, "b %f", &data_b_hidden); + sscanf(line, "c %f", &data_c_hidden); + sscanf(line, "scale %i", &data_image_scale); + } else { + if ( res == 5 ) { + + if ( (h==0) && (k==0) && (l==0) ) { + printf("DA: Data contains a zero-order beam value.\n"); + } else if ( (h==0) && (k==0) ) { + printf("DA: Data may contain a zero-order beam value.\n"); + } + if ( (l != laue) && (laue != 999999) ) { + //printf("DA: WARNING: Data is from more than one Laue zone\n"); + } else { + laue = l; + reflist_addref_am_ph(data_reflections, h, k, l, am, ph); + } + } else { + if ( (h==0) && (k==0) && (l==0) ) { + printf("DA: Data contains a zero-order beam value.\n"); + } else if ( (h==0) && (k==0) ) { + printf("DA: Data may contain a zero-order beam value.\n"); + } + if ( (l != laue) && (laue != 999999) ) { + //printf("DA: WARNING: Data is from more than one Laue zone\n"); + } else { + laue = l; + reflist_addref_am(data_reflections, h, k, l, am); + } + } + } + + line[0] = '\0'; /* Prevent this line from being parsed twice. */ + + } + + fclose(fh); + free(line); + + if ( whoops ) { + fprintf(stderr, "Couldn't read file %s\n", filename); + return 1; + } + + data_calculatesize(); + printf("a=%f, b=%f, width=%i, height=%i, gamma=%f\n", data_a_hidden, data_b_hidden, data_width_hidden, data_height_hidden, gamma_angle); + data_gamma_hidden = (gamma_angle/180)*M_PI; + + return 0; + +} + +ReflectionList *data_getreflections() { + return data_reflections; +} + +void data_free() { + + reflist_free(data_reflections); + +} diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..94b6d72 --- /dev/null +++ b/src/data.h @@ -0,0 +1,34 @@ +/* + * data.h + * + * Handle the input data + * + * (c) 2006-2008 Thomas White + * + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef DATA_H +#define DATA_H + +#include "reflist.h" + +extern double data_a(); +extern double data_b(); +extern double data_c(); +extern double data_gamma(); +extern unsigned int data_width(void); +extern unsigned int data_height(void); +unsigned int data_get_image_scale(void); +extern void data_dividecell(unsigned int na, unsigned int nb, unsigned int nc); +extern int data_read(const char *filename); +extern ReflectionList *data_getreflections(void); +extern void data_free(); + +#endif /* DATA_H */ + diff --git a/src/displaywindow.c b/src/displaywindow.c new file mode 100644 index 0000000..a4ceec0 --- /dev/null +++ b/src/displaywindow.c @@ -0,0 +1,1609 @@ +/* + * displaywindow.c + * + * The main display window + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "data.h" +#include "png-file.h" +#include "reflist.h" +#include "main.h" +#include "normalise.h" +#include "contourise.h" +#include "gtk-symmetry.h" +#include "gsf.h" +#include "cflip.h" +#include "cdm.h" +#include "clean.h" +#include "geometry.h" +#include "luzzatti.h" +#include "correspondence.h" +#include "amplitude-r.h" +#include "superlattice.h" +#include "refine.h" +#include "elser.h" +#include "colwheel.h" +#include "model.h" +#include "model-display.h" +#include "dpsynth.h" +#include "renderer.h" + +typedef struct { + + GtkWidget *window; + GtkWidget *xcells; + GtkWidget *ycells; + +} UnitCellsWindow; + +GtkUIManager *displaywindow_ui; +GtkActionGroup *displaywindow_action_group; +GtkWidget *displaywindow_window; +GtkWidget *displaywindow_bigvbox; +GtkWidget *displaywindow_image_widget = NULL; +GdkPixbuf *displaywindow_pixbuf = NULL; +double displaywindow_brightness = 700; +gint displaywindow_autobrightness = FALSE; +GtkWidget *displaywindow_status_bar; + +int displaywindow_model_names = TRUE; +int displaywindow_model_heights = FALSE; + +fftw_complex *displaywindow_in = NULL; +fftw_complex *displaywindow_out = NULL; +fftw_plan displaywindow_plan_i2o; +fftw_plan displaywindow_plan_o2i; +unsigned int displaywindow_width = 0; +unsigned int displaywindow_height = 0; + +fftw_complex *displaywindow_realspace = NULL; +DisplayWindowRealSpace displaywindow_rs = DWR_NONE; +int displaywindow_view = DWV_PATTERSON; + +int displaywindow_xc = 1; +int displaywindow_yc = 1; /* Size of synthesis in unit cells */ +UnitCellsWindow *displaywindow_unitcellswindow = NULL; + +static void displaywindow_switchview_real(unsigned int was_rescale); + +GtkWidget *displaywindow_gtkwindow() { return displaywindow_window; } + +int displaywindow_mode() { return displaywindow_view; } + +double displaywindow_max() { return displaywindow_brightness; } + +void error_report(const char *message) { + + GtkWidget *window; + + window = gtk_message_dialog_new(GTK_WINDOW(displaywindow_window), GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, message); + + g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window); + gtk_widget_show(window); + +} + +static void displaywindow_about() { + + GtkWidget *window; + + const gchar *authors[] = { + "Thomas White ", + NULL + }; + + window = gtk_about_dialog_new(); + + gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(window), PACKAGE_NAME); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(window), PACKAGE_VERSION); + gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(window), "(c) 2006-2007 Thomas White "); + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window), "Crystallographic Fourier Synthesis Utility"); + gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(window), "(c) 2006-2007 Thomas White \n" + "\n" + "Research funded by:\n" + "The Engineering and Physical Sciences Research Council\n" + "FEI Electron Optics B.V."); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window), "http://www-hrem.msm.cam.ac.uk/"); + gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors); + + g_signal_connect(window, "response", G_CALLBACK(gtk_widget_destroy), NULL); + + gtk_widget_show_all(window); + +} + +static void displaywindow_close() { + gtk_exit(0); +} + +void displaywindow_disablephasegrabbing() { + GtkWidget *disable; + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/tools/grabphases"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); +} + +void displaywindow_enablephasegrabbing() { + GtkWidget *enable; + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/tools/grabphases"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); +} + +static gint displaywindow_changeview(GtkWidget *widget, GtkRadioAction *action) { + + displaywindow_view = gtk_radio_action_get_current_value(action); + displaywindow_switchview(); + + switch ( displaywindow_view ) { + case DWV_PATTERSON: displaywindow_statusbar("Patterson transform"); break; + case DWV_PATTERSONE: displaywindow_statusbar("E²-1 Patterson transform"); break; + case DWV_KNOWNPHASE: displaywindow_statusbar("Known phases"); break; + case DWV_CALCPHASE: displaywindow_statusbar("Calculated phases"); break; + case DWV_REALSPACE: displaywindow_statusbar("Real space estimate"); break; + case DWV_MODEL: displaywindow_statusbar("Atomic model"); break; + case DWV_DIFFERENCE: displaywindow_statusbar("Fourier difference synthesis"); break; + case DWV_REFSYN: displaywindow_statusbar("Fourier refinement synthesis"); break; + case DWV_SIMPATT: displaywindow_statusbar("Simulated Patterson transform"); break; + case DWV_SIMFOLZPATT: displaywindow_statusbar("Simulated FOLZ Patterson transform"); break; + case DWV_EXITWAVE: displaywindow_statusbar("Simulated exit wave"); break; + } + + if ( displaywindow_view == DWV_REALSPACE ) { + displaywindow_enablephasegrabbing(); + } else { + displaywindow_disablephasegrabbing(); + } + + /* Saving images from the model display is not allowed. Use Save PDF in this case... */ + if ( displaywindow_view == DWV_MODEL ) { + GtkWidget *disable; + #if HAVE_CAIRO + GtkWidget *enable; + #endif + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/save"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + #if HAVE_CAIRO + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/savepdf"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); + #else + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/savepdf"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + #endif + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/contour"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + } else { + GtkWidget *enable; + GtkWidget *disable; + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/save"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); + disable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/file/savepdf"); + gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); + enable = gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/contour"); + gtk_widget_set_sensitive(GTK_WIDGET(enable), TRUE); + } + + return 0; + +} + +void displaywindow_forceview(int new_view) { + + GtkAction *action; + + if ( new_view == displaywindow_view ) return; /* Nothing to do */ + + action = gtk_action_group_get_action(displaywindow_action_group, "ExitWaveAction"); + #ifdef HAVE_GTK_TEN + gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), new_view); + #endif /* HAVE_GTK_TEN */ + +} + +static gint displaywindow_wilsonplot() { + main_wilsonplot(); + return 0; +} + +static gint displaywindow_falloffplot() { + main_falloffplot(); + return 0; +} + +static gint displaywindow_dpsynth() { + main_dpsynth(); + return 0; +} + +static gint displaywindow_simdp() { + dpsynth_simdp_open(model_calculate_f(main_reflist(), NULL, 0)); + return 0; +} + +static gint displaywindow_argand() { + main_argand(); + return 0; +} + +static void displaywindow_addui_callback(GtkUIManager *ui, GtkWidget *widget, GtkContainer *container) { + + gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0); + + /* Enable overflow menu if this is a toolbar */ + if ( GTK_IS_TOOLBAR(widget) ) { + gtk_toolbar_set_show_arrow(GTK_TOOLBAR(widget), TRUE); + } + +} + +unsigned int symmetrise_window_open = 0; +static gint displaywindow_symmetrise_response(GtkWidget *widget, gint response, GtkWidget *symmetry) { + + if ( response == GTK_RESPONSE_OK ) { + main_symmetrise(gtk_symmetry_get_symmetry(GTK_SYMMETRY(symmetry))); + } + + symmetrise_window_open = 0; + gtk_widget_destroy(widget); + + return 0; + +} + +static void displaywindow_symmetrise() { + + GtkWidget *symmetrise_window; + GtkWidget *symmetry; + + if ( symmetrise_window_open ) { + return; + } + symmetrise_window_open = 1; + + symmetry = gtk_symmetry_new(2, 2, TRUE); + + symmetrise_window = gtk_dialog_new_with_buttons("Symmetrise", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(symmetrise_window)->vbox), symmetry, FALSE, FALSE, 7); + + g_signal_connect(G_OBJECT(symmetrise_window), "response", G_CALLBACK(displaywindow_symmetrise_response), symmetry); + + gtk_widget_show_all(symmetrise_window); + +} + +static gint displaywindow_closedown(GtkWidget *widget, gpointer data) { + + fftw_destroy_plan(displaywindow_plan_i2o); + fftw_destroy_plan(displaywindow_plan_o2i); + fftw_free(displaywindow_in); + fftw_free(displaywindow_out); + + displaywindow_in = NULL; + displaywindow_out = NULL; + + return 0; + +} + +void displaywindow_statusbar(const char *message) { + gtk_statusbar_pop(GTK_STATUSBAR(displaywindow_status_bar), 0); + gtk_statusbar_push(GTK_STATUSBAR(displaywindow_status_bar), 0, message); +} + +static void displaywindow_free_data(guchar *image_data, gpointer data) { + free(image_data); +} + +GdkPixbuf *displaywindow_render_pixbuf(fftw_complex *out, double brightness, size_t width, size_t height, double gamma, int nx, int ny) { + + GdkPixbuf *pixbuf; + guchar *data; + int width_n, height_n; /* Size of the image without border */ + int width_i, height_i; /* Size of the image, including border */ + int xn, yn; /* Iterators */ + int bx, by; /* Border width in x and y */ + ComplexArray cxar; + size_t data_len; + + bx = 20; by = 20; + + width_n = (int)renderer_width(width, height, gamma, nx, ny); + height_n = (int)renderer_height(width, height, gamma, nx, ny); + width_i = width_n + 2*bx; + height_i = height_n + 2*by; + + data_len = height_i*width_i*3*sizeof(guchar); + data = malloc(data_len); + memset(data, 0xE5, data_len); + + cxar = renderer_draw(out, width, height, gamma, nx, ny); + for ( yn=0; yn 1.0 ) am = 1.0; + + data[3*((xn+bx)+width_i*(height_i-1-(yn+by))) + 0] = + (guchar)(255.0*colwheel_red(am, ph)); + data[3*((xn+bx)+width_i*(height_i-1-(yn+by))) + 1] = + (guchar)(255.0*colwheel_green(am, ph)); + data[3*((xn+bx)+width_i*(height_i-1-(yn+by))) + 2] = + (guchar)(255.0*colwheel_blue(am, ph)); + + } + } + + free(cxar.re); + free(cxar.im); + + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, FALSE, 8, width_i, height_i, 3*width_i, + (GdkPixbufDestroyNotify)displaywindow_free_data, NULL); + + return pixbuf; + +} + +static void displaywindow_update() { + + if ( displaywindow_pixbuf ) { + gdk_pixbuf_unref(displaywindow_pixbuf); + } + + displaywindow_pixbuf = displaywindow_render_pixbuf(displaywindow_outarray(), + displaywindow_brightness, data_width(), data_height(), data_gamma(), displaywindow_xc, displaywindow_yc); + + if ( displaywindow_image_widget ) { + g_object_set(G_OBJECT(displaywindow_image_widget), "pixbuf", displaywindow_pixbuf, NULL); + } else { + displaywindow_image_widget = gtk_image_new_from_pixbuf(displaywindow_pixbuf); + gtk_box_pack_end(GTK_BOX(displaywindow_bigvbox), GTK_WIDGET(displaywindow_image_widget), 0, FALSE, FALSE); + gtk_widget_show(displaywindow_image_widget); + } + +} + +void displaywindow_blank() { + + fftw_complex *blank; + + if ( displaywindow_pixbuf ) { + gdk_pixbuf_unref(displaywindow_pixbuf); + } + if ( displaywindow_image_widget ) { + gtk_widget_destroy(displaywindow_image_widget); + } + + blank = malloc(data_width()*data_height()*sizeof(fftw_complex)); + bzero(blank, data_width()*data_height()*sizeof(fftw_complex)); + displaywindow_pixbuf = displaywindow_render_pixbuf(blank, 1000000, data_width(), data_height(), data_gamma(), + displaywindow_xc, displaywindow_yc); + displaywindow_image_widget = gtk_image_new_from_pixbuf(displaywindow_pixbuf); + gtk_box_pack_end(GTK_BOX(displaywindow_bigvbox), GTK_WIDGET(displaywindow_image_widget), 0, FALSE, FALSE); + gtk_widget_show(displaywindow_image_widget); + + displaywindow_realspace = blank; + +} + +static unsigned int displaywindow_brightnesswindow_open = 0; + +static gint displaywindow_brightnesswindow_activate(GtkWidget *brightness_entry, GtkWidget *brightness_window) { + + float brightness_new; + const char *brightness_string = gtk_entry_get_text(GTK_ENTRY(brightness_entry)); + if ( sscanf(brightness_string, "%f", &brightness_new) == 1 ) { + + displaywindow_brightnesswindow_open = 0; + gtk_widget_destroy(brightness_window); + if ( displaywindow_brightness != brightness_new ) { + displaywindow_brightness = brightness_new; + displaywindow_update(); + //displaywindow_statusbar("Display brightness changed"); + if ( displaywindow_brightness < 0.000000001 ) { + error_report("Watch out for rounding errors!"); + } + } + + } + + return FALSE; + +} + +static gint displaywindow_brightnesswindow_response(GtkWidget *brightness_window, gint response, GtkWidget *brightness_entry) { + + if ( response == GTK_RESPONSE_OK ) { + displaywindow_brightnesswindow_activate(brightness_entry, brightness_window); + } + + displaywindow_brightnesswindow_open = 0; + gtk_widget_destroy(brightness_window); + + return FALSE; + +} + +static void displaywindow_brightness_open() { + + GtkWidget *brightness_window; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *brightness_label; + GtkWidget *brightness_entry; + char *brightness_string; + + if ( displaywindow_brightnesswindow_open ) { + return; + } + displaywindow_brightnesswindow_open = 1; + + brightness_entry = gtk_entry_new(); + brightness_window = gtk_dialog_new_with_buttons("Brightness", GTK_WINDOW(displaywindow_window), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(brightness_window)->vbox), GTK_WIDGET(vbox), FALSE, FALSE, 7); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + + brightness_label = gtk_label_new("Full brightness = "); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(brightness_label), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(brightness_entry), FALSE, FALSE, 5); + + brightness_string = malloc(32); + snprintf(brightness_string, 31, "%f", displaywindow_brightness); + gtk_entry_set_text(GTK_ENTRY(brightness_entry), brightness_string); + free(brightness_string); + + g_signal_connect(G_OBJECT(brightness_window), "response", G_CALLBACK(displaywindow_brightnesswindow_response), brightness_entry); + g_signal_connect(G_OBJECT(brightness_entry), "activate", G_CALLBACK(displaywindow_brightnesswindow_activate), brightness_window); + + gtk_widget_show_all(brightness_window); + +} + +gint displaywindow_brightness_auto(GtkWidget *widget, gpointer data) { + displaywindow_brightness = displaywindow_maxpeak(); + displaywindow_switchview_real(TRUE); + return 0; +} + +static gint displaywindow_brightness_autotoggle(GtkWidget *widget, gpointer data) { + displaywindow_autobrightness = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(widget)); + if ( displaywindow_autobrightness) displaywindow_brightness_auto(NULL, NULL); + return 0; +} + +static gint displaywindow_model_names_toggle(GtkWidget *widget, gpointer data) { + displaywindow_model_names = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(widget)); + displaywindow_switchview(); + return 0; +} + +static gint displaywindow_model_heights_toggle(GtkWidget *widget, gpointer data) { + displaywindow_model_heights = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(widget)); + displaywindow_switchview(); + return 0; +} + +/* Display the Patterson transform based on the given reflections */ +void displaywindow_show_patterson(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + /* Amplitudes squared since this is a Patterson transform */ + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * reflections->refs[i].amplitude; + /* Imaginary component is zero */ + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "PattersonAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +/* for ( h=0; hn_reflections; i++ ) { + if ( reflections->refs[i].amplitude > 0 ) { + sigma += reflections->refs[i].amplitude * reflections->refs[i].amplitude; + n++; + } + } + sigma = sigma / n; + + for ( i=1; in_reflections; i++ ) { + + double Esq; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( (abs(h) > displaywindow_width/2) || (abs(k) > displaywindow_height/2) ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + if ( reflections->refs[i].amplitude != 0 ) { + Esq = (reflections->refs[i].amplitude * reflections->refs[i].amplitude) / sigma; + displaywindow_in[k + displaywindow_height*h][0] = Esq - 1; + /* Imaginary component is zero */ + dev += Esq - 1; + } + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "PattersonEAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Display the known phase synthesis based on the given reflections */ +void displaywindow_show_knownphases(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + unsigned int n = 0; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + if ( reflections->refs[i].phase_known_set ) { + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + n++; + //printf("%3i %3i = %f %f\n", reflections->refs[i].h, reflections->refs[i].k, reflections->refs[i].amplitude, reflections->refs[i].phase_known); + } else { + /* else don't include it */ + //printf("%3i %3i not included\n", reflections->refs[i].h, reflections->refs[i].k); + } + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "KnownPhaseAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Display the calculated phase synthesis based on the given reflections */ +void displaywindow_show_calcphases(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + unsigned int n = 0; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( reflections->refs[i].phase_calc_set ) { + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_calc); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_calc); + n++; + + } /* else don't include it */ + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "CalcPhaseAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Display the current "real space estimate" */ +static void displaywindow_show_realspace() { + + if ( displaywindow_realspace ) { + /* displaywindow_update() knows to look in displaywindow_realspace instead of displaywindow_out */ + displaywindow_update(); + } else { + displaywindow_blank(); + } + +} + +void displaywindow_set_realspace(fftw_complex *out, DisplayWindowRealSpace rs) { + //if ( rs != DWR_NONE ) fftw_free(displaywindow_realspace); + displaywindow_realspace = out; + displaywindow_rs = rs; +} + +/* Show the current real-space atomic structure model */ +static void displaywindow_show_model() { + + GtkAction *action; + + #ifdef HAVE_CAIRO + if ( displaywindow_pixbuf ) gdk_pixbuf_unref(displaywindow_pixbuf); + + displaywindow_pixbuf = model_display_render_pixbuf(model_get_current(), data_width(), data_height(), data_gamma(), + displaywindow_model_names, displaywindow_model_heights, displaywindow_xc, displaywindow_yc); + if ( !displaywindow_pixbuf ) return; + + if ( displaywindow_image_widget ) { + g_object_set(G_OBJECT(displaywindow_image_widget), "pixbuf", displaywindow_pixbuf, NULL); + } else { + displaywindow_image_widget = gtk_image_new_from_pixbuf(displaywindow_pixbuf); + gtk_box_pack_end(GTK_BOX(displaywindow_bigvbox), GTK_WIDGET(displaywindow_image_widget), 0, FALSE, FALSE); + gtk_widget_show(displaywindow_image_widget); + } + #else + displaywindow_blank(); + #endif + + action = gtk_action_group_get_action(displaywindow_action_group, "ModelAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Show the simulated Patterson based on the structure model */ +static void displaywindow_show_simpatt() { + + signed int h, k, l; + unsigned int i; + GtkAction *action; + ReflectionList *model_reflections; + + for ( h=0; hn_reflections; i++ ) { + + h = model_reflections->refs[i].h; + k = model_reflections->refs[i].k; + l = model_reflections->refs[i].l; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, model_reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = model_reflections->refs[i].amplitude * model_reflections->refs[i].amplitude; + + } + + free(model_reflections); + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "SimPattAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +static void displaywindow_show_simfolzpatt() { + + signed int h, k, l; + unsigned int i; + GtkAction *action; + ReflectionList *model_reflections; + + for ( h=0; hn_reflections; i++ ) { + + h = model_reflections->refs[i].h; + k = model_reflections->refs[i].k; + l = model_reflections->refs[i].l; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, model_reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = model_reflections->refs[i].amplitude * model_reflections->refs[i].amplitude; + + } + + free(model_reflections); + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "SimFOLZPattAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* This works because of dodginess in main.c */ +void displaywindow_show_difference(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + if ( reflections->refs[i].phase_known_set == 0 ) continue; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "DifferenceAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* This works because of dodginess in main.c */ +void displaywindow_show_diffpatt(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude*reflections->refs[i].amplitude; + displaywindow_in[k + displaywindow_height*h][1] = 0; + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "DifferencePattAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* This works because of dodginess in main.c */ +void displaywindow_show_refsyn(ReflectionList *reflections) { + + signed int h, k; + unsigned int i; + GtkAction *action; + + for ( h=0; hn_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + + } + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "RefSynAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +/* Show the simulated specimen exit wave based on the structure model */ +static void displaywindow_show_exitwave() { + + signed int h, k, l; + unsigned int i; + GtkAction *action; + ReflectionList *model_reflections; + + for ( h=0; hn_reflections; i++ ) { + + h = model_reflections->refs[i].h; + k = model_reflections->refs[i].k; + l = model_reflections->refs[i].l; + + if ( abs(h) > displaywindow_width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, model_reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = displaywindow_width+h; } + if ( k < 0 ) { k = displaywindow_height+k; } + + displaywindow_in[k + displaywindow_height*h][0] = model_reflections->refs[i].amplitude * cos(model_reflections->refs[i].phase_known); + displaywindow_in[k + displaywindow_height*h][1] = model_reflections->refs[i].amplitude * sin(model_reflections->refs[i].phase_known); + + } + + free(model_reflections); + + /* Transform, preserving input array */ + fftw_execute(displaywindow_plan_i2o); + + displaywindow_update(); + + action = gtk_action_group_get_action(displaywindow_action_group, "ExitWaveAction"); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); + +} + +static void displaywindow_switchview_real(unsigned int was_rescale) { + + switch ( displaywindow_view ) { + case DWV_PATTERSON: main_show_patterson(); break; + case DWV_PATTERSONE: main_show_pattersone(); break; + case DWV_KNOWNPHASE: main_show_knownphases(); break; + case DWV_CALCPHASE: main_show_calcphases(); break; + case DWV_REALSPACE: displaywindow_show_realspace(); break; + case DWV_MODEL: displaywindow_show_model(); break; + case DWV_DIFFERENCE: main_show_difference(); break; + case DWV_REFSYN: main_show_refsyn(); break; + case DWV_DIFFPATT: main_show_diffpatt(); break; + case DWV_SIMPATT: displaywindow_show_simpatt(); break; + case DWV_SIMFOLZPATT: displaywindow_show_simfolzpatt(); break; + case DWV_EXITWAVE: displaywindow_show_exitwave(); break; + } + + if ( !was_rescale && displaywindow_autobrightness) displaywindow_brightness_auto(NULL, NULL); + + if ( !was_rescale ) { + /* Things which need updating... */ + main_displayr(); + main_argand_update(); + main_dpsynth_update(); + } + +} + +void displaywindow_switchview() { + displaywindow_switchview_real(FALSE); +} + +static gint displaywindow_geometry_open(GtkWidget *widget, gpointer data) { + geometry_dialog_open(); + return 0; +} + +static gint displaywindow_aperture(GtkWidget *widget, gpointer data) { + main_aperture_open(); + return 0; +} + +static gint displaywindow_luzzatti(GtkWidget *widget, gpointer data) { + luzzatti_show(); + return 0; +} + +static gint displaywindow_correspondence(GtkWidget *widget, gpointer data) { + correspondence_show(); + return 0; +} + +static gint displaywindow_hpfilter(GtkWidget *widget, gpointer data) { + main_hpfilter(); + return 0; +} + +static gint displaywindow_saveimage_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + printf("DW: Saving to '%s'\n", filename); + if ( displaywindow_view != DWV_REALSPACE ) { + png_write(filename, displaywindow_out, displaywindow_brightness, displaywindow_xc, displaywindow_yc); + } else { + png_write(filename, displaywindow_realspace, displaywindow_brightness, displaywindow_xc, displaywindow_yc); + } + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +static gint displaywindow_saveimage_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save Image", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + + #ifdef HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-output.png"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(displaywindow_saveimage_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint displaywindow_savepdf_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + //printf("DW: Saving to '%s'\n", filename); + + if ( displaywindow_view == DWV_MODEL ) { + if ( model_display_render_pdf(model_get_current(), data_width(), data_height(), data_gamma(), filename, + displaywindow_model_names, displaywindow_model_heights, displaywindow_xc, displaywindow_yc) ) { + error_report("Failed to save PDF"); + } + } else { + error_report("Can only (currently) save a PDF from the atomic model display"); + } + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +static gint displaywindow_savehkl_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + printf("DW: Saving to '%s'\n", filename); + + if ( displaywindow_view == DWV_REALSPACE ) { + + signed int h, k; + signed int h1, h2, k1, k2; + FILE *fh; + + memcpy(displaywindow_out, displaywindow_realspace, displaywindow_width*displaywindow_height*sizeof(fftw_complex)); + fftw_execute(displaywindow_plan_o2i); + fh = fopen(filename, "w"); + fprintf(fh, "a %f\n", data_a()); + fprintf(fh, "b %f\n", data_b()); + fprintf(fh, "c %f\n", data_c()); + fprintf(fh, "scale %i\n", data_get_image_scale()); + h1 = (signed)-main_max_h(); + h2 = main_max_h(); + k1 = (signed)-main_max_k(); + k2 = main_max_k(); + + printf("\nWARNING: calculated structure factor amplitudes are being square-rooted.\n"); + printf("This is the right thing to do to get structure factors from (eg) a CLEANed Patterson map\n"); + printf("If the real-space estimate is a structure model, you'll need to square them.\n"); + + printf("\nWARNING: l index is fixed at zero. Check this is what you wanted.\n"); + + for ( h=h1; h<=h2; h++ ) { + for ( k=k1; k<=k2; k++ ) { + + double re, im; + signed int hd, kd; + + hd = h; kd = k; + if ( h < 0 ) hd = displaywindow_width+h; + if ( k < 0 ) kd = displaywindow_height+k; + re = displaywindow_in[kd + displaywindow_height*hd][0]; + im = displaywindow_in[kd + displaywindow_height*hd][1]; + + fprintf(fh, "%3i %3i %3i %f %f\n", h, k, 0, sqrt(sqrt(re*re + im*im)), atan2(im, re)); + + } + } + fclose(fh); + + } else { + main_savereflections(filename); + } + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +static gint displaywindow_savehkl_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save Reflections", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + #ifdef HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-output.hkl"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(displaywindow_savehkl_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint displaywindow_savepdf_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save PDF", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + #ifdef HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-model.pdf"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(displaywindow_savepdf_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint displaywindow_amplituder(GtkWidget *widget, gpointer data) { + amplituder_show(); + return 0; +} + +static gint displaywindow_refine_open(GtkWidget *widget, gpointer data) { + refine_open(model_get_current()); + return 0; +} + +static gint displaywindow_grabphases(GtkWidget *widget, gpointer data) { + + if ( displaywindow_view != DWV_REALSPACE ) { + /* Should never happen because option is disabled */ + error_report("Can only grab phases from Real Space Estimate"); + return 0; + } + + if ( displaywindow_rs != DWR_ELSER ) { + error_report("Can only grab phases from Elser Difference Map"); + return 0; + } + elser_grab_phases(main_reflist()); + + displaywindow_forceview(DWV_KNOWNPHASE); + + return 0; + +} + +static gint displaywindow_contourise(GtkWidget *widget, gpointer data) { + contourise_dialog_open(displaywindow_xc, displaywindow_yc); + return 0; +} + +void displaywindow_kicksize() { + gtk_window_resize(GTK_WINDOW(displaywindow_window), 1, 1); +} + +static gint displaywindow_unitcells_response(GtkWidget *widget, gint response, UnitCellsWindow *cw) { + + int done = 1; + + if ( response == GTK_RESPONSE_OK ) { + const char *xcells; + const char *ycells; + unsigned int xc, yc; + int scanval; + xcells = gtk_entry_get_text(GTK_ENTRY(cw->xcells)); + ycells = gtk_entry_get_text(GTK_ENTRY(cw->ycells)); + scanval = sscanf(xcells, "%u", &xc); + scanval += sscanf(ycells, "%u", &yc); + if ( scanval != 2 ) { + error_report("Please enter valid values for both dimensions."); + done = 0; + } else { + displaywindow_xc = xc; + displaywindow_yc = yc; + displaywindow_switchview(); + displaywindow_kicksize(); + } + } + + if ( done ) { + gtk_widget_destroy(cw->window); + free(cw); + displaywindow_unitcellswindow = NULL; + } + + return 0; +} + +static gint displaywindow_unitcells_response_ac(GtkWidget *widget, UnitCellsWindow *cw) { + return displaywindow_unitcells_response(widget, GTK_RESPONSE_OK, cw); +} + +static gint displaywindow_stripzero(GtkWidget *widget, gpointer data) { + main_stripzero(); + displaywindow_switchview(); + return 0; +} + +static gint displaywindow_antialias(GtkWidget *widget, gpointer data) { + main_antialias(); + displaywindow_switchview(); + return 0; +} + +static gint displaywindow_unitcells_open(GtkWidget *widget, gpointer data) { + + UnitCellsWindow *cw; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *xcells_label; + GtkWidget *ycells_label; + char tmp[32]; + + if ( displaywindow_unitcellswindow ) { + return 0; + } + cw = malloc(sizeof(UnitCellsWindow)); + if ( !cw ) return 1; + displaywindow_unitcellswindow = cw; + + cw->window = gtk_dialog_new_with_buttons("Number of Unit Cells", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cw->window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + xcells_label = gtk_label_new("Number of cells along x:"); + gtk_misc_set_alignment(GTK_MISC(xcells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(xcells_label), 1, 2, 1, 2); + + cw->xcells = gtk_entry_new(); + snprintf(tmp, 31, "%i", displaywindow_xc); + gtk_entry_set_text(GTK_ENTRY(cw->xcells), tmp); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cw->xcells), 2, 3, 1, 2); + + ycells_label = gtk_label_new("Number of cells along y:"); + gtk_misc_set_alignment(GTK_MISC(ycells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ycells_label), 1, 2, 2, 3); + + cw->ycells = gtk_entry_new(); + snprintf(tmp, 31, "%i", displaywindow_yc); + gtk_entry_set_text(GTK_ENTRY(cw->ycells), tmp); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(cw->ycells), 2, 3, 2, 3); + + g_signal_connect(G_OBJECT(cw->ycells), "activate", G_CALLBACK(displaywindow_unitcells_response_ac), cw); + g_signal_connect(G_OBJECT(cw->window), "response", G_CALLBACK(displaywindow_unitcells_response), cw); + gtk_widget_show_all(cw->window); + gtk_widget_grab_focus(GTK_WIDGET(cw->xcells)); + + return 0; + +} + +static void displaywindow_addmenubar(GtkWidget *vbox) { + + GtkActionEntry entries[] = { + + { "FileAction", NULL, "_File", NULL, NULL, NULL }, + { "SaveAction", GTK_STOCK_SAVE, "_Save Image...", "", NULL, G_CALLBACK(displaywindow_saveimage_open) }, + { "SaveHKLAction", GTK_STOCK_SAVE, "_Save HKL...", "", NULL, G_CALLBACK(displaywindow_savehkl_open) }, + { "SavePDFAction", GTK_STOCK_SAVE, "_Save PDF...", "", NULL, G_CALLBACK(displaywindow_savepdf_open) }, + { "CloseAction", GTK_STOCK_QUIT, "_Quit", NULL, NULL, G_CALLBACK(displaywindow_close) }, + + { "ViewAction", NULL, "_View", NULL, NULL, NULL }, + { "CellsAction", NULL, "Number of Unit Cells...", "F3", NULL, G_CALLBACK(displaywindow_unitcells_open) }, + { "ScaleMenuAction", NULL, "Display Brightness", NULL, NULL, G_CALLBACK(NULL) }, + { "ModelMenuAction", NULL, "Model Display Features", NULL, NULL, G_CALLBACK(NULL) }, + { "ScaleAction", NULL, "Set Display _Brightness Manually...", "F5", NULL, G_CALLBACK(displaywindow_brightness_open) }, + { "AutoScaleOnceAction", NULL, "Automatic Brightness Once Only", "F5", NULL, G_CALLBACK(displaywindow_brightness_auto) }, + { "ContourAction", NULL, "C_ontour Map...", NULL, NULL, G_CALLBACK(displaywindow_contourise) }, + { "ArgandAction", NULL, "Ar_gand Plane...", NULL, NULL, G_CALLBACK(displaywindow_argand) }, + { "DPAction", NULL, "_Diffraction Pattern...", NULL, NULL, G_CALLBACK(displaywindow_dpsynth) }, + { "SimDPAction", NULL, "Simulated Diffraction Pattern...", NULL, NULL, G_CALLBACK(displaywindow_simdp) }, + { "ApertureAction", NULL, "Aper_ture Function...", NULL, NULL, G_CALLBACK(displaywindow_aperture) }, + { "ColWheelAction", NULL, "Co_lour Wheel...", NULL, NULL, G_CALLBACK(colwheel_show) }, + + { "PlotsAction", NULL, "_Plots", NULL, NULL, NULL }, + { "WilsonAction", "gnome-calc3", "_Wilson...", NULL, NULL, G_CALLBACK(displaywindow_wilsonplot) }, + { "FalloffAction", "gnome-calc3", "_Resolution Falloff...", NULL, NULL, G_CALLBACK(displaywindow_falloffplot) }, + { "LuzzattiAction", "gnome-calc3", "Lu_zzatti...", NULL, NULL, G_CALLBACK(displaywindow_luzzatti) }, + { "CorrespondenceAction", "gnome-calc3", "Correspondence...", NULL, NULL, G_CALLBACK(displaywindow_correspondence) }, + { "AmplitudeRAction", "gnome-calc3", "Amplitude-R...", NULL, NULL, G_CALLBACK(displaywindow_amplituder) }, + + { "SolveAction", NULL, "_Solve", NULL, NULL, NULL }, + { "GSFAction", GTK_STOCK_EXECUTE, "_GSF Iteration...", NULL, NULL, G_CALLBACK(gsf_dialog_open) }, + { "CFAction", GTK_STOCK_EXECUTE, "_Charge Flipping...", NULL, NULL, G_CALLBACK(cflip_dialog_open) }, + { "CDMAction", GTK_STOCK_EXECUTE, "_Tangent Formula...", NULL, NULL, G_CALLBACK(cdm_dialog_open) }, + { "ElserAction", GTK_STOCK_EXECUTE, "_Elser Difference Map...", NULL, NULL, G_CALLBACK(elser_dialog_open) }, + { "CLEANAction", NULL, "_CLEAN...", NULL, NULL, G_CALLBACK(clean_dialog_open) }, + { "ModelRefineAction", NULL, "_Refine Atomic Model...", NULL, NULL, G_CALLBACK(displaywindow_refine_open) }, + + { "ToolsAction", NULL, "_Tools", NULL, NULL, NULL }, + { "SymmAction", GTK_STOCK_CONVERT, "_Symmetrise...", NULL, NULL, G_CALLBACK(displaywindow_symmetrise) }, + { "SharpenAction", NULL, "_Deconvolve...", NULL, NULL, G_CALLBACK(normalise_dialog_open) }, + { "DethermaliseAction", NULL, "_Normalise...", NULL, NULL, G_CALLBACK(normalise_dethermalise_open) }, + { "ExpNormaliseAction", NULL, "_Exponential Normalisation...", NULL, NULL, G_CALLBACK(normalise_exponential_open) }, + { "GeometryCorrAction", NULL, "_Geometrical Correction...", NULL, NULL, G_CALLBACK(displaywindow_geometry_open) }, + { "SuperlatticeSplitAction", NULL, "Superlattice S_plit...", NULL, NULL, G_CALLBACK(superlattice_split_open) }, + { "HighPassFilterAction", NULL, "_High Pass Filter", NULL, NULL, G_CALLBACK(displaywindow_hpfilter) }, + { "GrabPhaseAction", GTK_STOCK_COPY, "_Grab Phases as Known", NULL, NULL, G_CALLBACK(displaywindow_grabphases) }, + { "StripZeroAction", NULL, "Strip Zero-Order Beam", NULL, NULL, G_CALLBACK(displaywindow_stripzero) }, + { "AntiAliasAction", NULL, "Anti-alias Filter", NULL, NULL, G_CALLBACK(displaywindow_antialias) }, + { "ModelAtomAction", NULL, "_Edit Atomic Model...", NULL, NULL, G_CALLBACK(model_open_editor) }, + { "ModelLoadAction", GTK_STOCK_OPEN, "_Load Atomic Model...", NULL, NULL, G_CALLBACK(model_load_open) }, + { "ModelSaveAction", GTK_STOCK_SAVE, "_Save Atomic Model...", NULL, NULL, G_CALLBACK(model_save_open) }, + + { "HelpAction", NULL, "_Help", NULL, NULL, NULL }, + { "AboutAction", GTK_STOCK_ABOUT, "_About Synth2D...", NULL, NULL, G_CALLBACK(displaywindow_about) }, + + }; + guint n_entries = G_N_ELEMENTS(entries); + GError *error = NULL; + GtkRadioActionEntry radios[] = { + { "PattersonAction", NULL, "_Patterson Transform", NULL, NULL, DWV_PATTERSON }, + { "PattersonEAction", NULL, "Patterson Transform with _E²-1", NULL, NULL, DWV_PATTERSONE }, + { "KnownPhaseAction", NULL, "_Known Phases", NULL, NULL, DWV_KNOWNPHASE }, + { "CalcPhaseAction", NULL, "_Calculated Phases", NULL, NULL, DWV_CALCPHASE }, + { "RealSpaceAction", NULL, "_Real Space Estimate", NULL, NULL, DWV_REALSPACE }, + { "ModelAction", NULL, "Atomic _Model", NULL, NULL, DWV_MODEL }, + { "DifferenceAction", NULL, "Fourier _Difference Synthesis", NULL, NULL, DWV_DIFFERENCE }, + { "RefSynAction", NULL, "_Fourier Refinement Synthesis", NULL, NULL, DWV_REFSYN }, + { "DifferencePattAction", NULL, "Difference Patterson Map", NULL, NULL, DWV_DIFFPATT }, + { "SimPattAction", NULL, "_Simulated Patterson Transform", NULL, NULL, DWV_SIMPATT }, + { "SimFOLZPattAction", NULL, "_Simulated FOLZ Patterson Transform", NULL, NULL, DWV_SIMFOLZPATT }, + { "ExitWaveAction", NULL, "Simulated _Exit Wave", NULL, NULL, DWV_EXITWAVE }, + }; + guint n_radios = G_N_ELEMENTS(radios); + GtkToggleActionEntry toggles[] = { + { "AutoScaleAction", NULL, "Automatic Brightness", NULL, NULL, G_CALLBACK(displaywindow_brightness_autotoggle), FALSE }, + { "ModelNamesAction", NULL, "Element Names", NULL, NULL, G_CALLBACK(displaywindow_model_names_toggle), TRUE }, + { "ModelHeightsAction", NULL, "Atom Heights", NULL, NULL, G_CALLBACK(displaywindow_model_heights_toggle), FALSE }, + }; + guint n_toggles = G_N_ELEMENTS(toggles); + + displaywindow_action_group = gtk_action_group_new("synth2Ddisplaywindow"); + gtk_action_group_add_actions(displaywindow_action_group, entries, n_entries, displaywindow_window); + /* Weird choice of initial selected action ensures signal gets sent on opening window to sort out menu enabling/disabling */ + gtk_action_group_add_radio_actions(displaywindow_action_group, radios, n_radios, DWV_SIMFOLZPATT, G_CALLBACK(displaywindow_changeview), NULL); + gtk_action_group_add_toggle_actions(displaywindow_action_group, toggles, n_toggles, NULL); + + displaywindow_ui = gtk_ui_manager_new(); + gtk_ui_manager_insert_action_group(displaywindow_ui, displaywindow_action_group, 0); + g_signal_connect(displaywindow_ui, "add_widget", G_CALLBACK(displaywindow_addui_callback), vbox); + if ( gtk_ui_manager_add_ui_from_file(displaywindow_ui, DATADIR"/synth2d/displaywindow.ui", &error) == 0 ) { + fprintf(stderr, "Error loading message window menu bar: %s\n", error->message); + return; + } + + gtk_window_add_accel_group(GTK_WINDOW(displaywindow_window), gtk_ui_manager_get_accel_group(displaywindow_ui)); + gtk_ui_manager_ensure_update(displaywindow_ui); + + #ifndef HAVE_CAIRO + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/model"), FALSE); + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/dp"), FALSE); + gtk_widget_set_sensitive(gtk_ui_manager_get_widget(displaywindow_ui, "/ui/displaywindow/view/simdp"), FALSE); + #endif + +} + +void displaywindow_createfourier() { + + /* (Re-)Create Fourier data structures */ + + if ( displaywindow_in ) { + fftw_free(displaywindow_in); + fftw_free(displaywindow_out); + fftw_destroy_plan(displaywindow_plan_i2o); + fftw_destroy_plan(displaywindow_plan_o2i); + } + + displaywindow_in = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + displaywindow_out = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + + displaywindow_plan_i2o = fftw_plan_dft_2d(data_width(), data_height(), displaywindow_in, displaywindow_out, + FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + displaywindow_plan_o2i = fftw_plan_dft_2d(data_width(), data_height(), displaywindow_out, displaywindow_in, + FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + displaywindow_width = data_width(); + displaywindow_height = data_height(); + +} + +void displaywindow_open(const char *filenamefull) { + + const char *filename; + char *title; + GtkWidget *vbox; + + filename = basename(filenamefull); + title = malloc(11+strlen(filename)); + strcpy(title, filename); + strcat(title, " - synth2d"); + + displaywindow_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(displaywindow_window), title); + free(title); + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(displaywindow_window), vbox); + displaywindow_addmenubar(vbox); + + displaywindow_status_bar = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(displaywindow_status_bar), FALSE); + gtk_box_pack_end(GTK_BOX(vbox), displaywindow_status_bar, FALSE, FALSE, 0); + + displaywindow_bigvbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), displaywindow_bigvbox, FALSE, TRUE, 0); + + g_signal_connect(GTK_OBJECT(displaywindow_window), "destroy", G_CALLBACK(displaywindow_closedown), NULL); + g_signal_connect_after(GTK_OBJECT(displaywindow_window), "destroy", G_CALLBACK(gtk_main_quit), NULL); + + displaywindow_disablephasegrabbing(); + + gtk_widget_show_all(displaywindow_window); + + displaywindow_createfourier(); + +} + +double displaywindow_maxpeak() { + + fftw_complex *out; + double max; + unsigned int x, y; + + out = displaywindow_outarray(); + + max = 0; + for ( x=0; x fabs(max) ) max = am; + } + } + + return max; + +} + +/* To be used with caution */ +fftw_complex *displaywindow_outarray() { + + if ( displaywindow_view != DWV_REALSPACE ) { + return displaywindow_out; + } else { + return displaywindow_realspace; + } + +} diff --git a/src/displaywindow.h b/src/displaywindow.h new file mode 100644 index 0000000..bdd339d --- /dev/null +++ b/src/displaywindow.h @@ -0,0 +1,76 @@ +/* + * displaywindow.h + * + * The main display window + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef DISPLAYWINDOW_H +#define DISPLAYWINDOW_H + +#include +#include + +#include "reflist.h" + +typedef enum { + DWR_NONE, + DWR_CLEAN, + DWR_GSF, + DWR_ELSER +} DisplayWindowRealSpace; + +extern GtkWidget *displaywindow_gtkwindow(void); +extern int displaywindow_mode(void); +extern void displaywindow_open(const char *filenameull); +extern void displaywindow_statusbar(const char *message); +extern void error_report(const char *message); +extern void displaywindow_show_patterson(ReflectionList *reflections); +extern void displaywindow_show_pattersone(ReflectionList *reflections); +extern void displaywindow_show_knownphases(ReflectionList *reflections); +extern void displaywindow_show_calcphases(ReflectionList *reflections); +extern void displaywindow_show_difference(ReflectionList *reflections); +extern void displaywindow_show_refsyn(ReflectionList *reflections); +extern void displaywindow_show_diffpatt(ReflectionList *reflections); +extern void displaywindow_switchview(void); +extern void displaywindow_set_realspace(fftw_complex *out, DisplayWindowRealSpace rs); +extern double displaywindow_maxpeak(void); +extern double displaywindow_max(void); +extern fftw_complex *displaywindow_outarray(void); +extern void displaywindow_createfourier(void); +extern void displaywindow_forceview(int new_view); +extern void displaywindow_disablephasegrabbing(void); +extern void displaywindow_enablephasegrabbing(void); +extern void displaywindow_set_gamma(double gamma); +extern GdkPixbuf *displaywindow_render_pixbuf(fftw_complex *out, double brightness, + size_t width_o, size_t height_o, double gamma, int nx, int ny); +extern gint displaywindow_brightness_auto(GtkWidget *widget, gpointer data); +extern fftw_complex *displaywindow_outarray(void); +extern void displaywindow_kicksize(void); + +enum { + DWV_PATTERSON, + DWV_PATTERSONE, + DWV_KNOWNPHASE, + DWV_CALCPHASE, + DWV_REALSPACE, + DWV_MODEL, + DWV_DIFFERENCE, + DWV_REFSYN, + DWV_DIFFPATT, + DWV_SIMPATT, + DWV_SIMFOLZPATT, + DWV_EXITWAVE +}; + +#endif /* DISPLAYWINDOW_H */ + diff --git a/src/dpsynth.c b/src/dpsynth.c new file mode 100644 index 0000000..33e9c3e --- /dev/null +++ b/src/dpsynth.c @@ -0,0 +1,291 @@ +/* + * dpsynth.c + * + * Draw synthetic diffraction patterns + * + * (c) 2006-2009 Thomas White + * (c) 2007-2008 Alex Eggeman + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#if HAVE_CAIRO +#include +#endif + +#include + +#include "reflist.h" +#include "data.h" +#include "displaywindow.h" +#include "colwheel.h" +#include "gsf.h" +#include "normalise.h" + +typedef struct { + GtkWidget *image_widget; + GdkPixbuf *pixbuf; + GtkWidget *window; + unsigned int width; + unsigned int height; + unsigned int colour; + +} DPSynthWindow; + +#if HAVE_CAIRO +static void dpsynth_swizzle_data(unsigned char *data, + size_t width, size_t height) +{ + size_t i, x, y; + uint32_t *dataw; + + for ( i=0; i<4*width*height; i+=4 ) { + unsigned char a, r, g, b; + r = data[i]; g = data[i+1]; b = data[i+2]; a = data[i+3]; + data[i] = b; data[i+1] = g; data[i+2] = r; data[i+3] = a; + } + + /* Now make it be the right way up */ + dataw = (uint32_t *)data; + for ( y=0; ywidth, dpsynth->height); + + if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, "Couldn't create Cairo surface\n"); + cairo_surface_destroy(surface); + return NULL; + } + + dctx = cairo_create(surface); + + /* Black background */ + cairo_rectangle(dctx, 0.0, 0.0, dpsynth->width, dpsynth->height); + cairo_set_source_rgb(dctx, 0.0, 0.0, 0.0); + + cairo_fill(dctx); + + max_u = 0.0; max_v = 0.0; max_intensity = 0.0; + max_res = 0.0; + /* NB This isn't really "the angle between a* and b*" */ + theta = data_gamma()-M_PI_2; + as = 1/(data_a() * cos(theta)); + bs = 1/(data_b() * cos(theta)); + for ( i=0; in_reflections; i++ ) { + + double u, v, intensity, ph, res; + + /* Convert to intensity */ + intensity = pow(reflections->refs[i].amplitude, 2.0); + ph = reflections->refs[i].phase_known; + + res = resolution(reflections->refs[i].h, + reflections->refs[i].k, + reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + if ( res > max_res ) max_res = res; + + if ( intensity != 0 ) { + u = (double)reflections->refs[i].h * as * cos(theta); + v = (double)reflections->refs[i].h * as * sin(theta) + + reflections->refs[i].k * bs; + if ( fabs(u) > fabs(max_u) ) max_u = fabs(u); + if ( fabs(v) > fabs(max_v) ) max_v = fabs(v); + if ( fabs(intensity) > fabs(max_intensity) ) + max_intensity = fabs(intensity); + } + + } + + max_u *= 2.5; + max_v *= 2.5; + printf("DP: Maximum resolution is %f nm^-1\n", max_res); + + if ( max_intensity > 0 ) { + + scale = ((double)dpsynth->width-50.0) / (2*max_u); + if ( ((double)dpsynth->height-50.0) / (2*max_v) < scale ) + scale = ((double)dpsynth->height-50.0) / (2*max_v); + + sep_u = as * scale * cos(theta); + sep_v = bs * scale; + max_r = ((sep_u < sep_v)?sep_u:sep_v) / 2; + + for ( i=0; in_reflections; i++ ) { + + double u, v, intensity, ph, val; + + intensity = pow(reflections->refs[i].amplitude, 2.0); + ph = reflections->refs[i].phase_known; + val = 2.0*intensity/max_intensity; + + if ( intensity != 0 ) { + + u = (double)reflections->refs[i].h * as + * cos(theta); + v = (double)reflections->refs[i].h * as + * sin(theta) + reflections->refs[i].k * bs; + + cairo_arc(dctx, ((double)dpsynth->width/2) + +u*scale*2, + ((double)dpsynth->height/2) + +v*scale*2, max_r, 0, 2*M_PI); + + + if ( dpsynth->colour == 0 ) { + cairo_set_source_rgb(dctx, val, val, val); } else { + cairo_set_source_rgb(dctx, + colwheel_red(val, ph), + colwheel_green(val, ph), + colwheel_blue(val, ph) + ); + } + cairo_fill(dctx); + + } + + } + + } else { + max_r = 4.0; + } + + /* Centre marker */ + cairo_arc(dctx, (double)dpsynth->width/2, + (double)dpsynth->height/2, max_r, 0, 2*M_PI); + cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0); + cairo_fill(dctx); + + cairo_surface_flush(surface); + + data = cairo_image_surface_get_data(surface); + dpsynth_swizzle_data(data, cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface)); + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface), + cairo_image_surface_get_stride(surface), + (GdkPixbufDestroyNotify)dpsynth_free_data, dctx); + + return pixbuf; +#else + /* Cannot do this without Cairo */ + return NULL; +#endif +} + +static void dpsynth_close(GtkWidget *widget, DPSynthWindow *dpsynth) +{ + free(dpsynth); +} + +void dpsynth_update(DPSynthWindow *dpsynth, ReflectionList *reflections) +{ + if ( dpsynth->pixbuf ) gdk_pixbuf_unref(dpsynth->pixbuf); + + dpsynth->pixbuf = dpsynth_render_pixbuf(dpsynth, reflections); + if ( !dpsynth->pixbuf ) return; + + if ( dpsynth->image_widget ) { + g_object_set(G_OBJECT(dpsynth->image_widget), "pixbuf", + dpsynth->pixbuf, NULL); + } else { + dpsynth->image_widget = gtk_image_new_from_pixbuf( + dpsynth->pixbuf); + gtk_container_add(GTK_CONTAINER(dpsynth->window), + GTK_WIDGET(dpsynth->image_widget)); + gtk_widget_show(dpsynth->image_widget); + } +} + +DPSynthWindow *dpsynth_open(ReflectionList *reflections, const char *title, + int colour) +{ + DPSynthWindow *dpsynth; + + dpsynth = malloc(sizeof(DPSynthWindow)); + dpsynth->width = 512; + dpsynth->height = 512; + dpsynth->pixbuf = NULL; + dpsynth->image_widget = NULL; + dpsynth->colour = colour; + + dpsynth->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(dpsynth->window), title); + g_signal_connect(GTK_OBJECT(dpsynth->window), "destroy", + G_CALLBACK(dpsynth_close), dpsynth); + gtk_widget_show_all(dpsynth->window); + + dpsynth_update(dpsynth, reflections); + + return dpsynth; +} + +/* "Glue" - to be removed eventually... */ +DPSynthWindow *dpsynth_main_dpsynth = NULL; +static void dpsynth_main_close(GtkWidget *widget, DPSynthWindow *dpsynth) { + dpsynth_main_dpsynth = NULL; +} +void dpsynth_main_update(ReflectionList *reflections) { + if ( !dpsynth_main_dpsynth ) return; + dpsynth_update(dpsynth_main_dpsynth, reflections); +} +void dpsynth_main_open(ReflectionList *reflections) { + if ( dpsynth_main_dpsynth ) return; + dpsynth_main_dpsynth = dpsynth_open(reflections, "Diffraction Pattern", 0); + g_signal_connect(GTK_OBJECT(dpsynth_main_dpsynth->window), "destroy", G_CALLBACK(dpsynth_main_close), dpsynth_main_dpsynth); +} + +DPSynthWindow *dpsynth_sim_dpsynth = NULL; +static void dpsynth_simdp_close(GtkWidget *widget, DPSynthWindow *dpsynth) { + dpsynth_sim_dpsynth = NULL; +} +void dpsynth_simdp_update(ReflectionList *reflections) { + if ( !dpsynth_sim_dpsynth ) return; + dpsynth_update(dpsynth_sim_dpsynth, reflections); +} +void dpsynth_simdp_open(ReflectionList *reflections) { + if ( dpsynth_sim_dpsynth ) return; + dpsynth_sim_dpsynth = dpsynth_open(reflections, "Simulated Diffraction Pattern", 0); + g_signal_connect(GTK_OBJECT(dpsynth_sim_dpsynth->window), "destroy", G_CALLBACK(dpsynth_simdp_close), dpsynth_sim_dpsynth); +} diff --git a/src/dpsynth.h b/src/dpsynth.h new file mode 100644 index 0000000..b10d978 --- /dev/null +++ b/src/dpsynth.h @@ -0,0 +1,37 @@ +/* + * dpsynth.h + * + * Draw synthetic diffracion patterns + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef DPSYNTH_H +#define DPSYNTH_H + +typedef struct { + GtkWidget *image_widget; + GdkPixbuf *pixbuf; + GtkWidget *window; + unsigned int width; + unsigned int height; + unsigned int colour; + +} DPSynthWindow; + +extern void dpsynth_main_update(ReflectionList *reflections); +extern void dpsynth_main_open(ReflectionList *reflections); +extern DPSynthWindow *dpsynth_open(ReflectionList *reflections, const char *title, int colour); +extern void dpsynth_update(DPSynthWindow *dpsynth, ReflectionList *reflections); + +extern void dpsynth_simdp_open(ReflectionList *reflections); +extern void dpsynth_simdp_update(ReflectionList *reflections); + +#endif /* DPSYNTH_H */ + diff --git a/src/elements.c b/src/elements.c new file mode 100644 index 0000000..96b33e6 --- /dev/null +++ b/src/elements.c @@ -0,0 +1,86 @@ +/* + * elements.c + * + * Elemental Data + * + * (c) 2006-2007 Thomas White + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "elements.h" + +Element elements[255]; +static unsigned int elements_initialised = 0; + +void elements_initialise() { + + int i, whoops; + FILE *fh; + + if ( elements_initialised ) return; + + i = 0; whoops = 0; + fh = fopen(DATADIR"/synth2d/elements", "r"); + do { + + char line[512]; + char buf[512]; + float a1, b1, a2, b2, a3, b3, a4, b4, c; + + fgets(line, 511, fh); + if ( ferror(fh) || feof(fh) ) { + whoops = 1; + break; + } + if ( strlen(line) > 1 ) { + + if ( line[strlen(line)-1] == '\n' ) { + line[strlen(line)-1] = '\0'; + } + + sscanf(line, "%s\t%i\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f\t%9f", buf, &elements[i].z, &a1, &b1, &a2, &b2, &a3, &b3, &a4, &b4, &c); + elements[i].element_name = strdup(buf); + elements[i].sfac_a1 = a1; elements[i].sfac_b1 = b1; + elements[i].sfac_a2 = a2; elements[i].sfac_b2 = b2; + elements[i].sfac_a3 = a3; elements[i].sfac_b3 = b3; + elements[i].sfac_a4 = a4; elements[i].sfac_b4 = b4; + elements[i].sfac_c = c; + + i++; + + } else { + line[0] = '\0'; + } + + } while ( !whoops && !feof(fh) ); + + elements[i].element_name = "EOF"; + elements_initialised = 1; + +} + +unsigned int elements_lookup(const char *name) { + + size_t i; + + i = 0; + while ( strcmp(elements[i].element_name, "EOF") != 0 ) { + if ( strcmp(elements[i].element_name, name) == 0 ) return i; + i++; + } + + fprintf(stderr, "Failed to look up element\n"); + return i; + +} + diff --git a/src/elements.h b/src/elements.h new file mode 100644 index 0000000..525e399 --- /dev/null +++ b/src/elements.h @@ -0,0 +1,33 @@ +/* + * elements.c + * + * Elemental Data + * + * (c) 2006-2007 Thomas White + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ELEMENTS_H +#define ELEMENTS_H + +typedef struct { + char *element_name; + unsigned int z; + /* Units of Angstroms and Degrees */ + double sfac_a1; double sfac_b1; double sfac_a2; double sfac_b2; + double sfac_a3; double sfac_b3; double sfac_a4; double sfac_b4; + double sfac_c; +} Element; +extern Element elements[255]; + +extern void elements_initialise(void); +extern unsigned int elements_lookup(const char *name); + +#endif /* ELEMENTS_H */ + diff --git a/src/elser.c b/src/elser.c new file mode 100644 index 0000000..d562f9f --- /dev/null +++ b/src/elser.c @@ -0,0 +1,811 @@ +/* + * elser.c + * + * Elser Difference Map Algorithm + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "displaywindow.h" +#include "reflist.h" +#include "symmetry.h" +#include "gtk-valuegraph.h" + +#define ELSER_START_PHASES 1 + +typedef enum { + RELAXATION_LOCALLY_ORTHOGONAL, + RELAXATION_FIENUP_HIO, + RELAXATION_CHARGE_FLIPPING +} ElserRelaxationType; + +typedef struct stuct_elserdialog { + + /* Dialog box bits */ + GtkWidget *window; + GtkWidget *go; + GtkWidget *stop; + GtkWidget *reset; + GtkWidget *solution; + GtkWidget *beta_widget; + GtkWidget *itnum; + GtkWidget *graph; + + /* Thread control */ + GThread *work_thread; + unsigned int running; + unsigned int run_semaphore; + GStaticMutex display_mutex; + guint display_callback; + + /* Algorithm control */ + double beta; + double gam1; + double gam2; + gint track_best; + ElserRelaxationType relaxation; + + /* Algorithm guts */ + ReflectionList *reflections; /* List of 'modulus constraints' */ + fftw_complex *in; /* Holds structure factors when needed */ + fftw_complex *trans_out; /* Array on the other end of the FFT of 'in' */ + fftw_plan plan_i2t; /* Plan to get from 'in' to 'trans_out' */ + fftw_plan plan_t2i; /* Plan to get from 'trans_out' to 'in', i.e. calculate structure factors */ + fftw_complex *out; /* Current iterate */ + fftw_complex *display; /* What's currently on the display */ + size_t array_size; /* Size of the arrays in bytes */ + fftw_complex *f1; /* f1 semi-iterate */ + fftw_complex *f2; /* f2 semi-iterate */ + fftw_complex *scratch; /* Scratchpad for calculations */ + unsigned int width; /* Width of the array in pixels */ + unsigned int height; /* Height of the array in pixels */ + + /* Tracking */ + unsigned int n_iterations; /* Number of iterations performed so far */ + double *e_evolution; /* Array of values tracking ||e|| */ + size_t e_evolution_size; /* Size of the e_evolution array */ + double best_norm; /* Lowest value of ||e|| so far encountered */ + unsigned int best_norm_iteration; /* Iteration number on which the best norm was achieved */ + fftw_complex *best_norm_grid; + +} ElserDialog; + +typedef struct struct_elserpeak { + unsigned int x; + unsigned int y; +} ElserPeak; + +ElserDialog *elser_dialog = NULL; + +static void elser_grid_multiply(fftw_complex *a, double b, size_t size) { + size_t i; + for ( i=0; iIteration number: %i", elser_dialog->n_iterations); + gtk_label_set_markup(GTK_LABEL(elser_dialog->itnum), tmp); + +} + +/* Scale up from the (small) grid size to the (large) display size */ +static void elser_do_display(fftw_complex *display, fftw_complex *grid, unsigned int grid_width, unsigned int grid_height, + unsigned int disp_width, unsigned int disp_height) { + + double sx, sy; + unsigned int dx, dy; + + sx = (double)disp_width/grid_width; + sy = (double)disp_height/grid_height; + + for ( dx=0; dxgraph), elser_dialog->e_evolution, elser_dialog->n_iterations); + + elser_dialog->display_callback = 0; + g_static_mutex_unlock(&elser_dialog->display_mutex); + + return FALSE; + +} + +#if 0 +/* Constrain the origin to prevent wandering in a conditional projection */ +static void elser_projection_origin(fftw_complex *f, ElserDialog *elser_dialog) { + + int width, height; + unsigned int x, y; + double re, im, ph_offs; + + width = elser_dialog->width; + height = elser_dialog->height; + + re = f[0][0]; + im = f[0][1]; + ph_offs = atan2(im, re); + printf("ph_offs = %f deg\n", (ph_offs/M_PI)*180); + + for ( y=0; y fabs(max) ) { + unsigned int p, allowed; + allowed = 1; + for ( p=0; pwidth; + height = elser_dialog->height; + N = 8; ssx=3; ssy=3; css=2; /* Gives 2px radius circular atom supports */ + + peaks = elser_projection_atomicity_findatoms(f, width, height, N, ssx, ssy); + grid_copy = malloc(elser_dialog->array_size); + bzero(grid_copy, elser_dialog->array_size); + + for ( i=0; iarray_size); + free(grid_copy); + +// elser_projection_origin(f, elser_dialog); + +} + +static void elser_projection_modulus(fftw_complex *f, ElserDialog *elser_dialog) { + + unsigned int width, height, i, x, y; + + width = elser_dialog->width; + height = elser_dialog->height; + + /* Transform */ + memcpy(elser_dialog->trans_out, f, elser_dialog->array_size); + fftw_execute(elser_dialog->plan_t2i); + + /* Scale */ + for ( x=0; xin[y+height*x][0] /= (width*height); + elser_dialog->in[y+height*x][1] /= (width*height); + } + } + + /* Apply constraints */ + for ( i=1; ireflections->n_reflections; i++ ) { + + double re, im; + double am, ph; + signed int h, k; + + h = elser_dialog->reflections->refs[i].h; + k = elser_dialog->reflections->refs[i].k; + + //if ( reflist_inlist(elser_dialog->reflections, -h, -k, 0) == 0 ) { + // printf("Friedel error for %3i %3i\n", h, k); + //} + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + re = elser_dialog->in[k + height*h][0]; + im = elser_dialog->in[k + height*h][1]; + ph = atan2(im, re); + am = elser_dialog->reflections->refs[i].amplitude; + + elser_dialog->in[k + height*h][0] = am*cos(ph); + elser_dialog->in[k + height*h][1] = am*sin(ph); + + } + printf("F00 = %f\n", sqrt(elser_dialog->in[0][0]*elser_dialog->in[0][0] + elser_dialog->in[0][1]*elser_dialog->in[0][1])); + symmetry_symmetrise_array(elser_dialog->in, width, height, SYMMETRY_FRIEDEL); + + /* Back transform */ + fftw_execute(elser_dialog->plan_i2t); + memcpy(f, elser_dialog->trans_out, elser_dialog->array_size); + +} + +static double elser_calculate_norm(fftw_complex *delta, unsigned int array_size) { + + unsigned int i; + double total, norm; + + total = 0; + for ( i=0; irunning = 1; + + width = data_width(); + height = data_height(); + + f1 = elser_dialog->f1; + f2 = elser_dialog->f2; + + while ( elser_dialog->run_semaphore ) { + + double norm, gam1, gam2, beta; + + beta = elser_dialog->beta; + gam1 = elser_dialog->gam1; + gam2 = elser_dialog->gam2; + + memcpy(f1, elser_dialog->out, elser_dialog->array_size); + memcpy(f2, elser_dialog->out, elser_dialog->array_size); + + elser_projection_atomicity(f1, elser_dialog); + elser_grid_multiply(f1, 1+gam1, elser_dialog->array_size); + memcpy(elser_dialog->scratch, elser_dialog->out, elser_dialog->array_size); + elser_grid_multiply(elser_dialog->scratch, gam1, elser_dialog->array_size); + elser_grid_subtract(f1, elser_dialog->scratch, elser_dialog->array_size); /* f1 = (1+gam1)P1(Xn) - gam1*Xn */ + + elser_projection_modulus(f2, elser_dialog); + elser_grid_multiply(f2, 1+gam2, elser_dialog->array_size); + memcpy(elser_dialog->scratch, elser_dialog->out, elser_dialog->array_size); + elser_grid_multiply(elser_dialog->scratch, gam2, elser_dialog->array_size); + elser_grid_subtract(f2, elser_dialog->scratch, elser_dialog->array_size); /* f2 = (1+gam2)P2(Xn) - gam2*Xn */ + + elser_projection_atomicity(f2, elser_dialog); + elser_projection_modulus(f1, elser_dialog); + elser_grid_subtract(f2, f1, elser_dialog->array_size); /* 'f2' now contains delta = P1(f2) - P2(f1) */ + + norm = elser_calculate_norm(f2, elser_dialog->array_size); + elser_dialog->e_evolution[elser_dialog->n_iterations] = log(norm); + if ( (elser_dialog->n_iterations % 100) == 99 ) { + elser_dialog->e_evolution = realloc(elser_dialog->e_evolution, elser_dialog->e_evolution_size + 100*sizeof(double)); + elser_dialog->e_evolution_size += 100*sizeof(double); + } + + elser_grid_multiply(f2, beta, elser_dialog->array_size); /* 'f2' now contains beta(P1(f2)-P2(f1)) */ + elser_grid_add(elser_dialog->out, f2, elser_dialog->array_size); /* Xn+1 = Xn + delta */ + + if ( norm < elser_dialog->best_norm ) { + elser_dialog->best_norm = norm; + elser_dialog->best_norm_iteration = elser_dialog->n_iterations+1; + memcpy(elser_dialog->best_norm_grid, elser_dialog->out, elser_dialog->array_size); + } + + /* Tell the main thread to update the display */ + g_static_mutex_lock(&elser_dialog->display_mutex); + elser_dialog->n_iterations++; + if ( elser_dialog->track_best ) { + elser_do_display(elser_dialog->display, elser_dialog->best_norm_grid, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + } else { + elser_do_display(elser_dialog->display, elser_dialog->out, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + } + elser_dialog->display_callback = g_idle_add(elser_update_display, elser_dialog); + + } + + elser_dialog->running = 0; + return NULL; + +} + +static gint elser_control_reset(GtkWidget *reset, ElserDialog *elser_dialog) { + + unsigned int i, width, height; + + /* Create structures */ + width = 2*main_max_h() + 1; + height = 2*main_max_k() + 1; + printf("EL: Grid size is %i x %i\n", width, height); + elser_dialog->array_size = width*height*sizeof(fftw_complex); + if ( !elser_dialog->in ) elser_dialog->in = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->trans_out ) elser_dialog->trans_out = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->out ) elser_dialog->out = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->f1 ) elser_dialog->f1 = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->f2 ) elser_dialog->f2 = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->scratch ) elser_dialog->scratch = fftw_malloc(elser_dialog->array_size); + if ( !elser_dialog->best_norm_grid ) elser_dialog->best_norm_grid = fftw_malloc(elser_dialog->array_size); + elser_dialog->width = width; + elser_dialog->height = height; + elser_dialog->reflections = reflist_copy(main_reflist()); + if ( !elser_dialog->plan_i2t ) + elser_dialog->plan_i2t = fftw_plan_dft_2d(width, height, elser_dialog->in, elser_dialog->trans_out, FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + if ( !elser_dialog->plan_t2i ) + elser_dialog->plan_t2i = fftw_plan_dft_2d(width, height, elser_dialog->trans_out, elser_dialog->in, FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + /* Set the initial iterate */ + for ( i=0; iarray_size/sizeof(fftw_complex); i++ ) { + elser_dialog->out[i][0] = random() - random(); + elser_dialog->out[i][1] = random() - random(); + } +#if ELSER_START_PHASES +// elser_projection_modulus(elser_dialog->out, elser_dialog); +#endif + elser_projection_atomicity(elser_dialog->out, elser_dialog); + + /* The old display array is going to get freed by displaywindow_set_realspace() */ + elser_dialog->display = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + elser_do_display(elser_dialog->display, elser_dialog->out, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + displaywindow_set_realspace(elser_dialog->display, DWR_ELSER); + displaywindow_forceview(DWV_REALSPACE); + displaywindow_switchview(); + elser_dialog->n_iterations = 0; + elser_update_itnum(elser_dialog); + elser_dialog->best_norm_iteration = 0; + elser_dialog->best_norm = INFINITY; + + /* Always exists - was set up when the dialog was created */ + free(elser_dialog->e_evolution); + elser_dialog->e_evolution = malloc(100*sizeof(double)); + elser_dialog->e_evolution_size = 100*sizeof(double); + gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), elser_dialog->e_evolution, 0); + + gtk_widget_set_sensitive(elser_dialog->go, TRUE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + return 0; + +} + +static gint elser_control_stop(GtkWidget *stop, ElserDialog *elser_dialog) { + + if ( !elser_dialog->running ) return 0; + + assert(elser_dialog->run_semaphore == 1); + assert(elser_dialog->work_thread != NULL); + elser_dialog->run_semaphore = 0; + if ( elser_dialog->display_callback ) { + g_idle_remove_by_data(elser_dialog); + g_static_mutex_unlock(&elser_dialog->display_mutex); + } + g_thread_join(elser_dialog->work_thread); + elser_dialog->work_thread = NULL; + + gtk_widget_set_sensitive(elser_dialog->go, TRUE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, TRUE); + + return 0; +} + +static gint elser_control_go(GtkWidget *go, ElserDialog *elser_dialog) { + + if ( elser_dialog->running ) return 0; + + assert(elser_dialog->run_semaphore == 0); + elser_dialog->run_semaphore = 1; + g_static_mutex_init(&elser_dialog->display_mutex); + assert(elser_dialog->work_thread == NULL); + elser_dialog->display_callback = 0; + elser_dialog->work_thread = g_thread_create(elser_work, elser_dialog, TRUE, NULL); + + gtk_widget_set_sensitive(elser_dialog->go, FALSE); + gtk_widget_set_sensitive(elser_dialog->stop, TRUE); + gtk_widget_set_sensitive(elser_dialog->reset, FALSE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + displaywindow_disablephasegrabbing(); + + return 0; +} + +static gint elser_control_solution(GtkWidget *solution, ElserDialog *elser_dialog) { + + if ( elser_dialog->running ) return 0; + + if ( elser_dialog->track_best ) { + memcpy(elser_dialog->out, elser_dialog->best_norm_grid, elser_dialog->array_size); + elser_dialog->n_iterations = elser_dialog->best_norm_iteration; + elser_update_itnum(elser_dialog); + } + + memcpy(elser_dialog->f1, elser_dialog->out, elser_dialog->array_size); + elser_projection_atomicity(elser_dialog->f1, elser_dialog); + elser_grid_multiply(elser_dialog->f1, 1+elser_dialog->gam1, elser_dialog->array_size); + memcpy(elser_dialog->scratch, elser_dialog->out, elser_dialog->array_size); + elser_grid_multiply(elser_dialog->scratch, elser_dialog->gam1, elser_dialog->array_size); + elser_grid_subtract(elser_dialog->f1, elser_dialog->scratch, elser_dialog->array_size); /* f1 = (1+gam1)P1(Xn) - gam1*Xn */ + elser_projection_modulus(elser_dialog->f1, elser_dialog); + memcpy(elser_dialog->out, elser_dialog->f1, elser_dialog->array_size); + + elser_do_display(elser_dialog->display, elser_dialog->out, elser_dialog->width, elser_dialog->height, data_width(), data_height()); + gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), elser_dialog->e_evolution, elser_dialog->n_iterations); + displaywindow_switchview(); + + gtk_widget_set_sensitive(elser_dialog->go, FALSE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + displaywindow_enablephasegrabbing(); + + return 0; + +} + +static gint elser_control_close(GtkWidget *window, gint response, ElserDialog *this_elserdialog) { + + elser_control_stop(this_elserdialog->stop, this_elserdialog); + + gtk_widget_destroy(window); + + if ( this_elserdialog->in ) fftw_free(this_elserdialog->in); + if ( this_elserdialog->out ) fftw_free(this_elserdialog->out); + if ( this_elserdialog->f1 ) fftw_free(this_elserdialog->f1); + if ( this_elserdialog->f2 ) fftw_free(this_elserdialog->f2); + if ( this_elserdialog->scratch ) fftw_free(this_elserdialog->scratch); + if ( this_elserdialog->best_norm_grid ) fftw_free(this_elserdialog->best_norm_grid); + if ( this_elserdialog->trans_out ) fftw_free(this_elserdialog->trans_out); + if ( this_elserdialog->plan_i2t ) fftw_destroy_plan(this_elserdialog->plan_i2t); + if ( this_elserdialog->plan_t2i ) fftw_destroy_plan(this_elserdialog->plan_t2i); + if ( this_elserdialog->reflections ) reflist_free(this_elserdialog->reflections); + /* Don't free this_elserdialog->display, it's backing up the display */ + + free(this_elserdialog); + elser_dialog = NULL; + + return 0; + +} + +static gint elser_control_beta_changed(GtkWidget *beta, ElserDialog *elser_dialog) { + + elser_dialog->beta = gtk_range_get_value(GTK_RANGE(beta)); + + switch ( elser_dialog->relaxation ) { + case RELAXATION_LOCALLY_ORTHOGONAL : { elser_dialog->gam1 = -1/elser_dialog->beta; elser_dialog->gam2 = 1/elser_dialog->beta; break; } + case RELAXATION_FIENUP_HIO : { elser_dialog->gam1 = -1; elser_dialog->gam2 = 1/elser_dialog->beta; break; } + case RELAXATION_CHARGE_FLIPPING : { elser_dialog->gam1 = 1; elser_dialog->gam2 = -1; break; } + } + + return 0; + +} + +static gint elser_control_trackbest_toggled(GtkWidget *track_best, ElserDialog *elser_dialog) { + elser_dialog->track_best = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(track_best)); + return 0; +} + +static gint elser_control_relaxation_change(GtkWidget *relaxation_combo, ElserDialog *elser_dialog) { + + switch ( gtk_combo_box_get_active(GTK_COMBO_BOX(relaxation_combo)) ) { + case 0 : elser_dialog->relaxation = RELAXATION_LOCALLY_ORTHOGONAL; break; + case 1 : elser_dialog->relaxation = RELAXATION_FIENUP_HIO; break; + case 2 : elser_dialog->relaxation = RELAXATION_CHARGE_FLIPPING; break; + default : abort(); /* Only in the event of a screw-up */ + } + + elser_control_beta_changed(elser_dialog->beta_widget, elser_dialog); + + return 0; + +} + +void elser_dialog_open() { + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *buttons; + GtkWidget *beta_label; + GtkWidget *beta_hbox; + GtkWidget *relaxation_hbox; + GtkWidget *relaxation_label; + GtkWidget *relaxation_combo; + GtkWidget *track_best; + + if ( elser_dialog ) { + return; + } + elser_dialog = malloc(sizeof(ElserDialog)); + elser_dialog->reflections = NULL; + elser_dialog->in = NULL; + elser_dialog->out = NULL; + elser_dialog->trans_out = NULL; + elser_dialog->f1 = NULL; + elser_dialog->f2 = NULL; + elser_dialog->scratch = NULL; + elser_dialog->plan_i2t = NULL; + elser_dialog->plan_t2i = NULL; + elser_dialog->best_norm_grid = NULL; + elser_dialog->display = NULL; + elser_dialog->running = 0; + elser_dialog->work_thread = NULL; + elser_dialog->run_semaphore = 0; + elser_dialog->running = 0; + elser_dialog->beta = 0.7; + elser_dialog->best_norm = INFINITY; + elser_dialog->gam1 = -1/elser_dialog->beta; + elser_dialog->gam2 = 1/elser_dialog->beta; + elser_dialog->track_best = FALSE; + + elser_dialog->window = gtk_dialog_new_with_buttons("Elser Difference Map", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + /* Structure */ + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(elser_dialog->window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + /* Parameters */ + beta_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(beta_hbox), FALSE, FALSE, 5); + beta_label = gtk_label_new("ß:"); + gtk_misc_set_padding(GTK_MISC(beta_label), 3, 0); + gtk_box_pack_start(GTK_BOX(beta_hbox), GTK_WIDGET(beta_label), FALSE, FALSE, 0); + elser_dialog->beta_widget = gtk_hscale_new_with_range(0, 1, 0.01); + gtk_scale_set_value_pos(GTK_SCALE(elser_dialog->beta_widget), GTK_POS_RIGHT); + gtk_range_set_value(GTK_RANGE(elser_dialog->beta_widget), elser_dialog->beta); + gtk_box_pack_start(GTK_BOX(beta_hbox), GTK_WIDGET(elser_dialog->beta_widget), TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(elser_dialog->beta_widget), "value-changed", G_CALLBACK(elser_control_beta_changed), elser_dialog); + + relaxation_hbox = gtk_hbox_new(FALSE, 0); + relaxation_label = gtk_label_new("Relaxation:"); + gtk_misc_set_alignment(GTK_MISC(relaxation_label), 1, 0.5); + gtk_box_pack_start(GTK_BOX(relaxation_hbox), relaxation_label, FALSE, FALSE, 5); + relaxation_combo = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(relaxation_combo), "Locally Orthogonal Constraints"); + gtk_combo_box_append_text(GTK_COMBO_BOX(relaxation_combo), "Fienup Hybrid Input-Output"); + gtk_combo_box_append_text(GTK_COMBO_BOX(relaxation_combo), "Charge Flipping"); + gtk_box_pack_start(GTK_BOX(relaxation_hbox), relaxation_combo, TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(vbox), relaxation_hbox, FALSE, FALSE, 5); + gtk_combo_box_set_active(GTK_COMBO_BOX(relaxation_combo), 0); + g_signal_connect(G_OBJECT(relaxation_combo), "changed", G_CALLBACK(elser_control_relaxation_change), elser_dialog); + + track_best = gtk_check_button_new_with_label("Track best solution"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(track_best), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(track_best), "toggled", G_CALLBACK(elser_control_trackbest_toggled), elser_dialog); + + /* Progress graph */ + elser_dialog->graph = gtk_value_graph_new(); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(elser_dialog->graph), TRUE, TRUE, 5); + elser_dialog->e_evolution = malloc(100*sizeof(double)); + elser_dialog->e_evolution_size = 100*sizeof(double); + gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), elser_dialog->e_evolution, 0); + + /* Iteration number */ + elser_dialog->itnum = gtk_label_new("(uninitialised)"); + gtk_label_set_markup(GTK_LABEL(elser_dialog->itnum), "(uninitialised)"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(elser_dialog->itnum), FALSE, FALSE, 5); + + /* Control buttons */ + buttons = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(buttons), FALSE, FALSE, 5); + /* Reset */ + elser_dialog->reset = gtk_button_new_with_label("Reset"); + gtk_button_set_image(GTK_BUTTON(elser_dialog->reset), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->reset), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->reset), "clicked", G_CALLBACK(elser_control_reset), elser_dialog); + /* Stop */ + elser_dialog->stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->stop), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->stop), "clicked", G_CALLBACK(elser_control_stop), elser_dialog); + /* Go */ + elser_dialog->go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(elser_dialog->go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->go), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->go), "clicked", G_CALLBACK(elser_control_go), elser_dialog); + /* Iterate fixed point->solution */ + elser_dialog->solution = gtk_button_new_with_label("Solution"); + gtk_button_set_image(GTK_BUTTON(elser_dialog->solution), gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(elser_dialog->solution), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(elser_dialog->solution), "clicked", G_CALLBACK(elser_control_solution), elser_dialog); + + g_signal_connect(G_OBJECT(elser_dialog->window), "response", G_CALLBACK(elser_control_close), elser_dialog); + + gtk_widget_set_sensitive(elser_dialog->go, FALSE); + gtk_widget_set_sensitive(elser_dialog->stop, FALSE); + gtk_widget_set_sensitive(elser_dialog->reset, TRUE); + gtk_widget_set_sensitive(elser_dialog->solution, FALSE); + + gtk_widget_show_all(elser_dialog->window); + displaywindow_forceview(DWV_REALSPACE); + + displaywindow_disablephasegrabbing(); + +} + +void elser_grab_phases(ReflectionList *reflections) { + + size_t i; + + if ( elser_dialog == NULL ) { + error_report("You need to keep the Elser dialog open"); + return; + } + + memcpy(elser_dialog->trans_out, elser_dialog->out, elser_dialog->array_size); + fftw_execute(elser_dialog->plan_t2i); + + for ( i=0; in_reflections; i++ ) { + + signed int h, k, l; + double re, im; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = 0; + + if ( abs(h) > elser_dialog->width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + reflections->refs[i].phase_known_set = 0; + continue; + } + if ( abs(k) > elser_dialog->height/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + reflections->refs[i].phase_known_set = 0; + continue; + } + if ( h < 0 ) { h = elser_dialog->width+h; } + if ( k < 0 ) { k = elser_dialog->height+k; } + + re = elser_dialog->in[k+elser_dialog->height*h][0]; + im = elser_dialog->in[k+elser_dialog->height*h][1]; + + reflections->refs[i].phase_known = atan2(im, re); + reflections->refs[i].phase_known_set = 1; + + } + +} + diff --git a/src/elser.h b/src/elser.h new file mode 100644 index 0000000..258b70d --- /dev/null +++ b/src/elser.h @@ -0,0 +1,25 @@ +/* + * elser.h + * + * Elser Difference Map Algorithm + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef ELSER_H +#define ELSER_H + +#include "reflist.h" + +extern void elser_dialog_open(); +extern void elser_grab_phases(ReflectionList *reflections); + +#endif /* ELSER_H */ + diff --git a/src/geometry.c b/src/geometry.c new file mode 100644 index 0000000..7a2d621 --- /dev/null +++ b/src/geometry.c @@ -0,0 +1,324 @@ +/* + * geometry.c + * + * Geometrical (Lorentz) Corrections + * + * (c) 2006 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "main.h" + +#include "displaywindow.h" +#include "data.h" + +typedef struct { + + GtkWidget *wn_entry; /* Electon wavenumber */ + GtkWidget *cr_entry; /* Laue Circle radius */ + GtkWidget *wn_label; + GtkWidget *cr_label; + GtkWidget *wn_nm_label; + GtkWidget *cr_nm_label; + GeometryCorrectionType correction_type; + +} GeometryCorrectionWindow; + +static int geometry_window_open = 0; + +/* Apply "Lorentz" factor to reflections */ +void geometry_correct(ReflectionList *reflections, GeometryCorrectionType correction_type, double wavenumber, double R) { + + unsigned int i; + double a = data_a(); + double b = data_b(); + double c = data_c(); + + for ( i=1; in_reflections; i++ ) { + + double corr = 1; + double g, g_xy, g_z; + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + + /* Calculate g, g_xy and g_z */ + g = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + g_xy = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b))); + g_z = l/c; + + switch ( correction_type ) { + + case GEOMETRY_CORRECTION_TYPE_VINCENT_MIDGLEY : { + + /* Vincent-Midgley */ + /* eqn (9) from Ultramicroscopy vol 53 1994 p271-282 */ + double corr1, corr2, corr3; + + corr1 = g/(2*R); + corr2 = pow(corr1, 2); + corr3 = 1 - corr2; + corr = sqrt(corr3); + + break; + + } + + case GEOMETRY_CORRECTION_TYPE_GJONNES : { + + /* Gjonnes (without divergence) */ + /* eqn (9) from Ultramicroscopy 69 1997 p1-11 */ + double corr1, corr2, corr3, corr4; + + corr1 = 2*R*g_xy; corr1 = pow(corr1, 2); + corr2 = pow(g,2) - (2*wavenumber*g_z); corr2 = pow(corr2, 2); + corr3 = corr1 - corr2; + corr4 = 2*wavenumber; + corr = sqrt(corr3) / (2 * corr4); + + break; + + } + + case GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE : { + + /* Gjønnes (with divergence) */ + /* eqn (37) from Ultramicroscopy 69 1997 p1-11 */ + double corr1, corr2, corr3, corr3b, corr4, corr4a, corr4b; + + corr1 = pow(R, 3) / pow(wavenumber, 2); + corr2 = pow((g_xy / g), 2); + corr3b = (pow(g, 2) - 2*wavenumber*g_z) / (2*R*g_xy); + corr3 = 1 - pow(corr3b, 2); + corr4a = pow(((pow(g, 2) - (2*wavenumber*g_z))/wavenumber), 2); + corr4a = 1 - (corr4a / 2); + corr4b = (wavenumber*g_z)/pow(g, 2); + corr4b = 1 - (corr4b * 2); + corr4 = corr4a / corr4b; + corr = corr1 * corr2 * corr3 * corr4; + + //printf("corr1=%f, corr2=%f, corr3b=%f, corr3=%f, corr4a=%f, corr4b=%f, corr4=%f\n", + // corr1, corr2, corr3b, corr3, corr4a, corr4b, corr4); + + break; + + } + + } + + printf("%f %f\n", g, corr); + + if ( corr < 0 ) corr = 0; + + /* Square root here because we are working with amplitudes not intensities */ + reflections->refs[i].amplitude = sqrt(corr) * reflections->refs[i].amplitude; + assert( reflections->refs[i].amplitude >= 0 ); + + } + + displaywindow_switchview(); + +} + +static gint geometry_window_response(GtkWidget *geometry_window, gint response, GeometryCorrectionWindow *gcw) { + + if ( response == GTK_RESPONSE_OK ) { + + const char *str; + float wavenumber_f, circleradius_f; + double wavenumber, circleradius; + + str = gtk_entry_get_text(GTK_ENTRY(gcw->wn_entry)); + sscanf(str, "%f", &wavenumber_f); wavenumber = wavenumber_f; + + str = gtk_entry_get_text(GTK_ENTRY(gcw->cr_entry)); + sscanf(str, "%f", &circleradius_f); circleradius = circleradius_f; + + main_geometry_correct(gcw->correction_type, wavenumber, circleradius); + + } + + geometry_window_open = 0; + gtk_widget_destroy(geometry_window); + free(gcw); + + return 0; + +} + +static gint geometry_select_vm(GtkWidget *widget, GeometryCorrectionWindow *gcw) { + + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_VINCENT_MIDGLEY; + + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_entry), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_nm_label), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_nm_label), TRUE); + + return 0; + +} + +static gint geometry_select_gn(GtkWidget *widget, GeometryCorrectionWindow *gcw) { + + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_GJONNES; + + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_nm_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_nm_label), TRUE); + + return 0; + +} + +static gint geometry_select_gd(GtkWidget *widget, GeometryCorrectionWindow *gcw) { + + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE; + + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->wn_nm_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_entry), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_label), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(gcw->cr_nm_label), TRUE); + + return 0; + +} + +/* Wavelength of an electron (in m) given accelerating potential (in V) */ +static double lambda(double V) { + + double m = 9.110E-31; + double h = 6.625E-34; + double e = 1.60E-19; + double c = 2.998E8; + + return h / sqrt(2*m*e*V*(1+((e*V) / (2*m*c*c)))); + +} + +static void geometry_set_defaults(GeometryCorrectionWindow *gcw) { + + double wn; + char string[32]; + int i; + double r; + ReflectionList *reflections; + + wn = 1/(lambda(300000)*1e9); /* Answer in nm^-1 */ + snprintf(string, 31, "%f", wn); + gtk_entry_set_text(GTK_ENTRY(gcw->wn_entry), string); + + reflections = main_reflist(); + r = 0; + double a = data_a(); + double b = data_b(); + for ( i=1; in_reflections; i++ ) { + + double g; + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + + g = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b))); + + if ( g > r ) r = g; + + } + + snprintf(string, 31, "%f", r); + gtk_entry_set_text(GTK_ENTRY(gcw->cr_entry), string); + +} + +void geometry_dialog_open() { + + GtkWidget *geometry_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *geometry_type_vm; + GtkWidget *geometry_type_gn; + GtkWidget *geometry_type_gd; + GeometryCorrectionWindow *gcw; + + if ( geometry_window_open ) { + return; + } + geometry_window_open = 1; + + gcw = malloc(sizeof(GeometryCorrectionWindow)); + + geometry_window = gtk_dialog_new_with_buttons("Geometrical Correction", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(geometry_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(5, 3, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + geometry_type_vm = gtk_radio_button_new_with_label(NULL, "Vincent-Midgley"); + geometry_type_gn = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(geometry_type_vm), + "Gjønnes (without divergence)"); + geometry_type_gd = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(geometry_type_vm), + "Gjønnes (with divergence)"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(geometry_type_gd), TRUE); + gcw->correction_type = GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE; + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(geometry_type_vm), 1, 4, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(geometry_type_gn), 1, 4, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(geometry_type_gd), 1, 4, 3, 4); + + gcw->wn_label = gtk_label_new("Electron Wavenumber:"); + gtk_misc_set_alignment(GTK_MISC(gcw->wn_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->wn_label), 1, 2, 4, 5); + gcw->wn_nm_label = gtk_label_new("nm^-1"); + gtk_label_set_markup(GTK_LABEL(gcw->wn_nm_label), "nm-1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->wn_nm_label), 3, 4, 4, 5); + + gcw->wn_entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->wn_entry), 2, 3, 4, 5); + + gcw->cr_label = gtk_label_new("Laue Circle Radius:"); + gtk_misc_set_alignment(GTK_MISC(gcw->cr_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->cr_label), 1, 2, 5, 6); + gcw->cr_nm_label = gtk_label_new("nm^-1"); + gtk_label_set_markup(GTK_LABEL(gcw->cr_nm_label), "nm-1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->cr_nm_label), 3, 4, 5, 6); + + gcw->cr_entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(gcw->cr_entry), 2, 3, 5, 6); + + geometry_set_defaults(gcw); + + g_signal_connect(G_OBJECT(geometry_type_vm), "clicked", G_CALLBACK(geometry_select_vm), gcw); + g_signal_connect(G_OBJECT(geometry_type_gn), "clicked", G_CALLBACK(geometry_select_gn), gcw); + g_signal_connect(G_OBJECT(geometry_type_gd), "clicked", G_CALLBACK(geometry_select_gd), gcw); + + g_signal_connect(G_OBJECT(geometry_window), "response", G_CALLBACK(geometry_window_response), gcw); + + gtk_widget_show_all(geometry_window); + + gtk_widget_grab_focus(GTK_WIDGET(gcw->wn_entry)); + +} diff --git a/src/geometry.h b/src/geometry.h new file mode 100644 index 0000000..80f9c6d --- /dev/null +++ b/src/geometry.h @@ -0,0 +1,29 @@ +/* + * geometry.h + * + * Geometrical (Lorentz) Corrections + * + * (c) 2006 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef GEOMETRY_H +#define GEOMETRY_H + +#include "reflist.h" + +typedef enum { + GEOMETRY_CORRECTION_TYPE_VINCENT_MIDGLEY, + GEOMETRY_CORRECTION_TYPE_GJONNES, + GEOMETRY_CORRECTION_TYPE_GJONNES_DIVERGENCE +} GeometryCorrectionType; + +extern void geometry_correct(ReflectionList *reflections, GeometryCorrectionType correction_type, double wavenumber, double R); +extern void geometry_dialog_open(void); + +#endif /* GEOMETRY_H */ diff --git a/src/gsf.c b/src/gsf.c new file mode 100644 index 0000000..aa18da8 --- /dev/null +++ b/src/gsf.c @@ -0,0 +1,669 @@ +/* + * gsf.c + * + * Gerchberg-Saxton-Fienup iteration algorithms + * + * (c) 2006-2007 Thomas White + * (c) 2008 Alex Eggeman + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "data.h" +#include "displaywindow.h" +#include "symmetry.h" +#include "gtk-symmetry.h" +#include "reflist.h" + + +GtkWidget *gsfw_iterations = NULL; +static int gsf_window_open = 0; +static int gsf_running = 0; +static int gsf_n_iterations = 0; +static int gsf_centroid = 0; +static int gsf_atomicity = 0; +static int gsf_zerofzero = 1; +static int gsf_fracsup = 0; +static int gsf_threshsup = 1; +static double gsf_fracsup_phi = 0.15; +static double gsf_hio_beta = 0.15; +static double gsf_threshsup_delta = 0; /* i.e. all positive values are in the support */ +Symmetry gsf_symmetry = PLANEGROUP_P1; +fftw_complex *gsf_drive = NULL; +fftw_complex *gsf_drive_old = NULL; +unsigned int *gsf_support = NULL; +fftw_complex *gsf_in = NULL; +fftw_complex *gsf_out = NULL; +fftw_plan gsf_plan_i2d; +fftw_plan gsf_plan_d2i; +ReflectionList *gsf_reflections = NULL; +static int gsf_antistag = 0; + +static void gsf_pin_centroid(fftw_complex *out, fftw_complex *drive, unsigned int width, unsigned int height) { + + unsigned int x, y; + double xc = 0; + double yc = 0; + double total_int = 0; + signed int xs; + signed int ys; + fftw_complex *out_copy; + + for ( x=0; xn_reflections; i++ ) { + + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + in_new[k + height*h][0] = in[k + height*h][0]; + in_new[k + height*h][1] = in[k + height*h][1]; + + } + + memcpy(in, in_new, width*height*sizeof(fftw_complex)); + free(in_new); + +} + +/* Perform a single GSF iteration */ +void gsf_iteration(int gsf_n_iterations) { + + unsigned int x, y; + double mean; + double top; + unsigned int mean_n; + double fzero; + unsigned int i; + unsigned int width = data_width(); + unsigned int height = data_height(); + + assert(gsf_drive_old != NULL); + assert(gsf_drive != NULL); + assert(gsf_in != NULL); + assert(gsf_out != NULL); + + /* Copy this structure if required */ + memcpy(gsf_drive_old, gsf_drive, width*height*sizeof(fftw_complex)); + + /* Transform back to reciprocal space and scale. This time, the driving function is + being transformed. */ + fftw_execute(gsf_plan_d2i); + for ( x=0; xn_reflections; i++ ) { + + double re, im; + double am, ph; + signed int h, k; + + h = gsf_reflections->refs[i].h; + k = gsf_reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + re = gsf_in[k + height*h][0]; + im = gsf_in[k + height*h][1]; + ph = atan2(im, re); + am = gsf_reflections->refs[i].amplitude; + + gsf_in[k + height*h][0] = am*cos(ph); + gsf_in[k + height*h][1] = am*sin(ph); + + } + + fzero = gsf_in[0][0]; + gsf_mask(gsf_reflections, gsf_in, width, height); + if ( !gsf_zerofzero ) { + gsf_in[0][0] = fzero; + } + + /* Impose Symmetry */ + + symmetry_symmetrise_array(gsf_in, width, height, gsf_symmetry); + + /* Transform to real space */ + fftw_execute(gsf_plan_i2d); + + if ( gsf_centroid ) { + gsf_pin_centroid(gsf_drive, gsf_drive_old, width, height); + } + + memcpy(gsf_out, gsf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + + /* Determine the support */ + if ( gsf_threshsup && !gsf_fracsup ) { + + /* Threshold support */ + for ( y=0; y gsf_threshsup_delta ) { + gsf_support[y+height*x] = 1; + } else { + gsf_support[y+height*x] = 0; + } + + if ( gsf_antistag && ((x+y)>width) ) { + gsf_support[y+height*x] = 0; + } + + } + } + + } else if ( gsf_fracsup ) { + + /* 'Fractional' support */ + double max; + unsigned int max_x, max_y; + int n, target, i; + + + n = height*width; + target = n * gsf_fracsup_phi; + i = 0; + + while ( i < target ) { + max_x = 0; max_y = 0; max = -1e10; + for ( y=0; y 0) ) { + if ( ( am > max) && (!gsf_support[y+height*x]) ) { + max_x = x; max_y = y; max = am; + } + } + } + } + gsf_support[max_y+height*max_x] = 1; + i++; + } + + } else { + + /* No options selected for support - consider everything valid */ + for ( y=0; y 0 ) { + mean += am; + mean_n++; + if ( am > top ) top = am; + + } + } + } + + mean = mean / mean_n; + for ( y=0; yn_reflections; i++ ) { + + double am, ph; + signed int h, k; + + h = gsf_reflections->refs[i].h; + k = gsf_reflections->refs[i].k; + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + am = gsf_reflections->refs[i].amplitude; + ph = (((double)random())/RAND_MAX) * (2*M_PI); + + gsf_in[k + height*h][0] = am*cos(ph); + gsf_in[k + height*h][1] = am*sin(ph); + + } + fftw_execute(gsf_plan_i2d); + + memcpy(gsf_out, gsf_drive, width*height*sizeof(fftw_complex)); + displaywindow_switchview(); + +} + +void gsf_initialise(ReflectionList *reflections) { + + gsf_drive_old = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + gsf_drive = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + gsf_support = fftw_malloc(data_width()*data_height()*sizeof(unsigned int)); + gsf_in = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + memset(gsf_in, 0, data_width()*data_height()*sizeof(fftw_complex)); + if ( !gsf_out ) gsf_out = fftw_malloc(data_width()*data_height()*sizeof(fftw_complex)); + + gsf_plan_i2d = fftw_plan_dft_2d(data_width(), data_height(), gsf_in, gsf_drive, FFTW_BACKWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + gsf_plan_d2i = fftw_plan_dft_2d(data_width(), data_height(), gsf_drive, gsf_in, FFTW_FORWARD, FFTW_MEASURE | FFTW_PRESERVE_INPUT); + + gsf_reflections = malloc(sizeof(ReflectionList)); + memcpy(gsf_reflections, reflections, sizeof(ReflectionList)); + + displaywindow_set_realspace(gsf_out, DWR_GSF); + gsf_reset(reflections); + +} + +static gint gsf_window_go(GtkWidget *gsf_go) { + + if ( gsf_running ) { + return 0; + } + + gsf_running = 1; + while ( gsf_running ) { + gsf_n_iterations++; + gsf_update_iterations(); + gsf_iteration(gsf_n_iterations); + while ( gtk_events_pending() ) gtk_main_iteration(); + } + return 0; + +} + +static gint gsf_window_stop(GtkWidget *widget, gpointer data) { + gsf_running = 0; + return 0; +} + +static gint gsf_window_reset(GtkWidget *widget) { + gsf_running = 0; + main_gsf_reset(); + return 0; +} + +static gint gsf_symmetry_changed(GtkWidget *widget, gpointer data) { + gsf_symmetry = gtk_symmetry_get_symmetry(GTK_SYMMETRY(widget)); + return 0; +} + +void gsf_dialog_open() { + + GtkWidget *gsfw_window; + GtkWidget *gsfw_go; + GtkWidget *gsfw_stop; + GtkWidget *gsfw_reset; + GtkWidget *gsfw_gostop_hbox; + GtkWidget *gsfw_centroid; + GtkWidget *gsfw_atomicity; + GtkWidget *gsfw_zerofzero; + GtkWidget *gsfw_antistag; + GtkWidget *gsfw_fracsup; + GtkWidget *gsfw_fracsup_phi; + GtkWidget *gsfw_fracsup_hbox; + GtkWidget *gsfw_hio_beta; + GtkWidget *gsfw_hio_hbox; + GtkWidget *gsfw_threshsup; + GtkWidget *gsfw_threshsup_delta; + GtkWidget *gsfw_threshsup_hbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *sym_define; + GtkWidget *gsfw_support_label; + GtkWidget *gsfw_process_label; + char *text; + + if ( gsf_window_open ) { + return; + } + gsf_window_open = 1; + + gsfw_window = gtk_dialog_new_with_buttons("Projection Iteration", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gsfw_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + gsfw_iterations = gtk_label_new(""); + text = malloc(40); + snprintf(text, 39, "Iterations performed so far: %i", gsf_n_iterations); + gtk_label_set_text(GTK_LABEL(gsfw_iterations), text); + free(text); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_iterations), FALSE, FALSE, 5); + + sym_define = gtk_symmetry_new(2, 2, TRUE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(sym_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(sym_define), "changed", G_CALLBACK(gsf_symmetry_changed), NULL); + gsf_symmetry = PLANEGROUP_P1; + + gsfw_centroid = gtk_check_button_new_with_label("Pin centroid"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_centroid), FALSE, FALSE, 5); + if ( gsf_centroid ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_centroid), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_centroid), FALSE); + } + gsfw_atomicity = gtk_check_button_new_with_label("Atomicity"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_atomicity), FALSE, FALSE, 5); + if ( gsf_atomicity ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_atomicity), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_atomicity), FALSE); + } + gsfw_zerofzero = gtk_check_button_new_with_label("Zero F(00)"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_zerofzero), FALSE, FALSE, 5); + if ( gsf_zerofzero ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_zerofzero), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_zerofzero), FALSE); + } + gsfw_antistag = gtk_check_button_new_with_label("Anti-stagnation"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_antistag), FALSE, FALSE, 5); + if ( gsf_antistag ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_antistag), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_antistag), FALSE); + } + + gsfw_support_label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(gsfw_support_label), "Dynamic Support"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_support_label), FALSE, FALSE, 10); + + gsfw_fracsup = gtk_radio_button_new_with_label(NULL, "Fraction of most positive points"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_fracsup), FALSE, FALSE, 5); + if ( gsf_fracsup ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_fracsup), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_fracsup), FALSE); + } + gsfw_fracsup_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_fracsup_hbox), FALSE, FALSE, 5); + gsfw_fracsup_phi_label = gtk_label_new("Φ:"); + gtk_box_pack_start(GTK_BOX(gsfw_fracsup_hbox), GTK_WIDGET(gsfw_fracsup_phi_label), FALSE, FALSE, 5); + gsfw_fracsup_phi = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_value_pos(GTK_SCALE(gsfw_fracsup_phi), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(gsfw_fracsup_hbox), GTK_WIDGET(gsfw_fracsup_phi), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(gsfw_fracsup_phi), gsf_hio_beta); + if ( gsf_fracsup) { + gtk_widget_set_sensitive(gsfw_fracsup_phi, TRUE); + gtk_widget_set_sensitive(gsfw_fracsup_phi_label, TRUE); + } else { + gtk_widget_set_sensitive(gsfw_fracsup_phi, FALSE); + gtk_widget_set_sensitive(gsfw_fracsup_phi_label, FALSE); + } + + gsfw_threshsup = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(gsfw_fracsup), "Points above threshold"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_threshsup), FALSE, FALSE, 5); + if ( gsf_threshsup ) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_threshsup), TRUE); + } else { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gsfw_threshsup), FALSE); + } + gsfw_threshsup_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_threshsup_hbox), FALSE, FALSE, 5); + gsfw_threshsup_delta_label = gtk_label_new("δ:"); + gtk_box_pack_start(GTK_BOX(gsfw_threshsup_hbox), GTK_WIDGET(gsfw_threshsup_delta_label), FALSE, FALSE, 5); + gsfw_threshsup_delta = gtk_hscale_new_with_range(-100, 100, 1); + gtk_scale_set_value_pos(GTK_SCALE(gsfw_threshsup_delta), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(gsfw_threshsup_hbox), GTK_WIDGET(gsfw_threshsup_delta), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(gsfw_threshsup_delta), gsf_threshsup_delta); + if ( gsf_threshsup ) { + gtk_widget_set_sensitive(gsfw_threshsup_delta, TRUE); + gtk_widget_set_sensitive(gsfw_threshsup_delta_label, TRUE); + } else { + gtk_widget_set_sensitive(gsfw_threshsup_delta, FALSE); + gtk_widget_set_sensitive(gsfw_threshsup_delta_label, FALSE); + } + + gsfw_process_label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(gsfw_process_label), "Points Outside Support"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_process_label), FALSE, FALSE, 10); + + gsfw_hio_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_hio_hbox), FALSE, FALSE, 5); + gsfw_hio_beta_label = gtk_label_new("β:"); + gtk_box_pack_start(GTK_BOX(gsfw_hio_hbox), GTK_WIDGET(gsfw_hio_beta_label), FALSE, FALSE, 5); + gsfw_hio_beta = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_value_pos(GTK_SCALE(gsfw_hio_beta), GTK_POS_RIGHT); + gtk_box_pack_start(GTK_BOX(gsfw_hio_hbox), GTK_WIDGET(gsfw_hio_beta), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(gsfw_hio_beta), gsf_hio_beta); + + gsfw_gostop_hbox = gtk_hbox_new(FALSE, 0); + gsfw_go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(gsfw_go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + gsfw_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + gsfw_reset = gtk_button_new_with_label("Reset"); + gtk_button_set_image(GTK_BUTTON(gsfw_reset), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(gsfw_gostop_hbox), GTK_WIDGET(gsfw_reset), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(gsfw_gostop_hbox), GTK_WIDGET(gsfw_stop), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(gsfw_gostop_hbox), GTK_WIDGET(gsfw_go), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(gsfw_gostop_hbox), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(gsfw_go), "clicked", G_CALLBACK(gsf_window_go), NULL); + g_signal_connect(G_OBJECT(gsfw_stop), "clicked", G_CALLBACK(gsf_window_stop), NULL); + g_signal_connect(G_OBJECT(gsfw_reset), "clicked", G_CALLBACK(gsf_window_reset), NULL); + + g_signal_connect(G_OBJECT(gsfw_window), "response", G_CALLBACK(gsf_window_close), NULL); + g_signal_connect(G_OBJECT(gsfw_window), "delete-event", G_CALLBACK(gsf_window_stop), NULL); + g_signal_connect(G_OBJECT(gsfw_centroid), "toggled", G_CALLBACK(gsf_centroid_toggled), NULL); + g_signal_connect(G_OBJECT(gsfw_atomicity), "toggled", G_CALLBACK(gsf_atomicity_toggled), NULL); + g_signal_connect(G_OBJECT(gsfw_zerofzero), "toggled", G_CALLBACK(gsf_zerofzero_toggled), NULL); + g_signal_connect(G_OBJECT(gsfw_fracsup), "toggled", G_CALLBACK(gsf_fracsup_toggled), gsfw_fracsup_phi); + g_signal_connect(G_OBJECT(gsfw_fracsup_phi), "value-changed", G_CALLBACK(gsf_fracsup_phichanged), NULL); + g_signal_connect(G_OBJECT(gsfw_threshsup), "toggled", G_CALLBACK(gsf_threshsup_toggled), gsfw_threshsup_delta); + g_signal_connect(G_OBJECT(gsfw_threshsup_delta), "value-changed", G_CALLBACK(gsf_threshsup_deltachanged), NULL); + g_signal_connect(G_OBJECT(gsfw_hio_beta), "value-changed", G_CALLBACK(gsf_hio_betachanged), NULL); + g_signal_connect(G_OBJECT(gsfw_antistag), "toggled", G_CALLBACK(gsf_antistag_toggled), NULL); + g_signal_connect(G_OBJECT(displaywindow_gtkwindow()), "delete-event", G_CALLBACK(gsf_window_stop), NULL); + + main_gsf_initialise(); + displaywindow_forceview(DWV_REALSPACE); + + gtk_widget_show_all(gsfw_window); + +} diff --git a/src/gsf.h b/src/gsf.h new file mode 100644 index 0000000..a2bd5d8 --- /dev/null +++ b/src/gsf.h @@ -0,0 +1,30 @@ +/* + * gsf.h + * + * Gerchberg-Saxton-Fienup and other Projection iteration algorithms + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef GSF_H +#define GSF_H + +#include "reflist.h" +#include "symmetry.h" + +extern void gsf_dpsynth_main_update(ReflectionList *gsf_listout); +extern void gsf_dpsynth_main_open(ReflectionList *gsf_listout); + +extern void gsf_dialog_open(void); +extern void gsf_initialise(ReflectionList *reflections); +extern void gsf_reset(ReflectionList *reflections); +extern void gsf_mask(ReflectionList *reflections, fftw_complex *in, unsigned int width, unsigned int height); + +#endif /* GSF_H */ diff --git a/src/gtk-symmetry.c b/src/gtk-symmetry.c new file mode 100644 index 0000000..60b06b8 --- /dev/null +++ b/src/gtk-symmetry.c @@ -0,0 +1,243 @@ +/* + * gtk-symmetry.c + * + * A simple widget to select a symmetry group + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#include + +#include "gtk-symmetry.h" + +static GtkObjectClass *parent_class = NULL; + +static void gtk_symmetry_destroy(GtkObject *gtk_symmetry) { + + parent_class->destroy(gtk_symmetry); + +} + +static gint gtk_symmetry_changed(GtkWidget *selection, GtkSymmetry *gtk_symmetry) { + g_signal_emit_by_name(G_OBJECT(gtk_symmetry), "changed"); + return 0; +} + +static gint gtk_symmetry_do_packing(GtkWidget *widget, GtkObject *old_parent, GtkSymmetry *gtk_symmetry) { + + if ( gtk_symmetry->has_friedel ) { + if ( gtk_symmetry->friedel_pos == GTK_POS_RIGHT ) { + gtk_symmetry->table = gtk_table_new(1, 3, FALSE); + } else { + gtk_symmetry->table = gtk_table_new(2, 2, FALSE); + } + } else { + gtk_symmetry->table = gtk_table_new(1, 2, FALSE); + } + gtk_table_set_row_spacings(GTK_TABLE(gtk_symmetry->table), 5); + gtk_table_set_col_spacings(GTK_TABLE(gtk_symmetry->table), 5); + + gtk_symmetry->label = gtk_label_new("Planegroup:"); + gtk_misc_set_alignment(GTK_MISC(gtk_symmetry->label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), GTK_WIDGET(gtk_symmetry->label), 1, 2, 1, 2); + + gtk_symmetry->selection = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p1"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pm (m // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pm (m // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pg (g // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "pg (g // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "cm (m // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "cm (m // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2mm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2mg (m // x)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2mg (m // y)"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p2gg"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "c2mm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p4"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p4mm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p4gm"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p3"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p3m1"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p31m"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p6"); + gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_symmetry->selection), "p6mm"); + gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_symmetry->selection), 0); + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), GTK_WIDGET(gtk_symmetry->selection), 2, 3, 1, 2); + g_signal_connect(G_OBJECT(gtk_symmetry->selection), "changed", G_CALLBACK(gtk_symmetry_changed), gtk_symmetry); + + gtk_symmetry->friedel = gtk_check_button_new_with_label("Friedel"); + if ( gtk_symmetry->has_friedel ) { + if ( gtk_symmetry->friedel_pos == GTK_POS_RIGHT ) { + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), gtk_symmetry->friedel, 3, 4, 1, 2); + } else { + gtk_table_attach_defaults(GTK_TABLE(gtk_symmetry->table), gtk_symmetry->friedel, 2, 3, 2, 3); + } + } + g_signal_connect(G_OBJECT(gtk_symmetry->friedel), "toggled", G_CALLBACK(gtk_symmetry_changed), gtk_symmetry); + + gtk_box_pack_start(GTK_BOX(gtk_symmetry), GTK_WIDGET(gtk_symmetry->table), TRUE, TRUE, 5); + + return 0; + +} + +GtkWidget *gtk_symmetry_new(unsigned int dimensions, unsigned int trans_dimensions, gboolean has_friedel) { + + GtkSymmetry *gtk_symmetry; + + g_return_val_if_fail(dimensions >= trans_dimensions, NULL); + g_return_val_if_fail(dimensions < 4, NULL); + g_return_val_if_fail(dimensions < 4, NULL); + + gtk_symmetry = GTK_SYMMETRY(gtk_type_new(gtk_symmetry_get_type())); + + gtk_symmetry->dimensions = dimensions; + gtk_symmetry->trans_dimensions = trans_dimensions; + gtk_symmetry->has_friedel = has_friedel; + gtk_symmetry->friedel_pos = GTK_POS_BOTTOM; + + g_signal_connect(G_OBJECT(gtk_symmetry), "parent-set", G_CALLBACK(gtk_symmetry_do_packing), gtk_symmetry); + + return GTK_WIDGET(gtk_symmetry); + +} + +static GObject *gtk_symmetry_constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { + + GtkSymmetryClass *class; + GObjectClass *p_class; + GObject *obj; + + class = GTK_SYMMETRY_CLASS(g_type_class_peek(gtk_symmetry_get_type())); + p_class = G_OBJECT_CLASS(g_type_class_peek_parent(class)); + + obj = p_class->constructor(type, n_construct_properties, construct_properties); + + return obj; + +} + +static void gtk_symmetry_class_init(GtkSymmetryClass *class) { + + GtkObjectClass *object_class; + GObjectClass *g_object_class; + + object_class = (GtkObjectClass *) class; + g_object_class = G_OBJECT_CLASS(class); + + object_class->destroy = gtk_symmetry_destroy; + g_object_class->constructor = gtk_symmetry_constructor; + + parent_class = gtk_type_class(gtk_hbox_get_type()); + + g_signal_new("changed", gtk_symmetry_get_type(), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(GtkSymmetryClass, changed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + +} + +Symmetry gtk_symmetry_get_symmetry(GtkSymmetry *gtk_symmetry) { + + Symmetry symmetry = PLANEGROUP_P1; + + g_return_val_if_fail(GTK_IS_SYMMETRY(gtk_symmetry), symmetry); + + switch ( gtk_combo_box_get_active(GTK_COMBO_BOX(GTK_SYMMETRY(gtk_symmetry)->selection)) ) { + case 0 : symmetry = PLANEGROUP_P1; break; + case 1 : symmetry = PLANEGROUP_P2; break; + case 2 : symmetry = PLANEGROUP_PM_X; break; + case 3 : symmetry = PLANEGROUP_PM_Y; break; + case 4 : symmetry = PLANEGROUP_PG_X; break; + case 5 : symmetry = PLANEGROUP_PG_Y; break; + case 6 : symmetry = PLANEGROUP_CM_X; break; + case 7 : symmetry = PLANEGROUP_CM_Y; break; + case 8 : symmetry = PLANEGROUP_P2MM; break; + case 9 : symmetry = PLANEGROUP_P2MG_X; break; + case 10 : symmetry = PLANEGROUP_P2MG_Y; break; + case 11 : symmetry = PLANEGROUP_P2GG; break; + case 12 : symmetry = PLANEGROUP_C2MM; break; + case 13 : symmetry = PLANEGROUP_P4; break; + case 14 : symmetry = PLANEGROUP_P4MM; break; + case 15 : symmetry = PLANEGROUP_P4GM; break; + case 16 : symmetry = PLANEGROUP_P3; break; + case 17 : symmetry = PLANEGROUP_P3M1; break; + case 18 : symmetry = PLANEGROUP_P31M; break; + case 19 : symmetry = PLANEGROUP_P6; break; + case 20 : symmetry = PLANEGROUP_P6MM; break; + } + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(GTK_SYMMETRY(gtk_symmetry)->friedel)) ) { + symmetry = symmetry | SYMMETRY_FRIEDEL; + } + + return symmetry; + +} + +Symmetry gtk_symmetry_set_symmetry(GtkSymmetry *symmetry, Symmetry new_sym) { + + Symmetry old_sym; + gint idx = 0; + + old_sym = gtk_symmetry_get_symmetry(symmetry); + + if ( new_sym == PLANEGROUP_P1 ) idx = 0; + if ( new_sym == PLANEGROUP_P2 ) idx = 1; + if ( new_sym == PLANEGROUP_PM_X ) idx = 2; + if ( new_sym == PLANEGROUP_PM_Y ) idx = 3; + if ( new_sym == PLANEGROUP_PG_X ) idx = 4; + if ( new_sym == PLANEGROUP_PG_Y ) idx = 5; + if ( new_sym == PLANEGROUP_CM_X ) idx = 6; + if ( new_sym == PLANEGROUP_CM_Y ) idx = 7; + if ( new_sym == PLANEGROUP_P2MM ) idx = 8; + if ( new_sym == PLANEGROUP_P2MG_X ) idx = 9; + if ( new_sym == PLANEGROUP_P2MG_Y ) idx = 10; + if ( new_sym == PLANEGROUP_P2GG ) idx = 11; + if ( new_sym == PLANEGROUP_C2MM ) idx = 12; + if ( new_sym == PLANEGROUP_P4 ) idx = 13; + if ( new_sym == PLANEGROUP_P4MM ) idx = 14; + if ( new_sym == PLANEGROUP_P4GM ) idx = 15; + if ( new_sym == PLANEGROUP_P3 ) idx = 16; + if ( new_sym == PLANEGROUP_P3M1 ) idx = 17; + if ( new_sym == PLANEGROUP_P31M ) idx = 18; + if ( new_sym == PLANEGROUP_P6 ) idx = 19; + if ( new_sym ==PLANEGROUP_P6MM ) idx = 20; + + gtk_combo_box_set_active(GTK_COMBO_BOX(GTK_SYMMETRY(symmetry)->selection), idx); + + return old_sym; + +} + +static void gtk_symmetry_init(GtkSymmetry *gtk_symmetry) { + +} + +guint gtk_symmetry_get_type(void) { + + static guint gtk_symmetry_type = 0; + + if ( !gtk_symmetry_type ) { + + GtkTypeInfo gtk_symmetry_info = { + "GtkSymmetry", + sizeof(GtkSymmetry), + sizeof(GtkSymmetryClass), + (GtkClassInitFunc) gtk_symmetry_class_init, + (GtkObjectInitFunc) gtk_symmetry_init, + NULL, + NULL, + (GtkClassInitFunc) NULL, + }; + gtk_symmetry_type = gtk_type_unique(gtk_hbox_get_type(), >k_symmetry_info); + + } + + return gtk_symmetry_type; + +} + diff --git a/src/gtk-symmetry.h b/src/gtk-symmetry.h new file mode 100644 index 0000000..d50651e --- /dev/null +++ b/src/gtk-symmetry.h @@ -0,0 +1,50 @@ +/* + * gtk-symmetry.h + * + * A simple widget to select a symmetry group + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef GTKSYMMETRY_H +#define GTKSYMMETRY_H + +#include + +#include "symmetry.h" + +typedef struct { + + GtkHBox parent; /* Parent widget */ + + unsigned int dimensions; /* 1D, 2D, 3D, more...? */ + unsigned int trans_dimensions; /* Number of dimensions of translational symmetry */ + gboolean has_friedel; + GtkPositionType friedel_pos; + + GtkWidget *label; + GtkWidget *selection; + GtkWidget *table; + GtkWidget *friedel; + +} GtkSymmetry; + +typedef struct { + GtkHBoxClass parent_class; + void (* changed) (GtkSymmetry *gtksymmetry); +} GtkSymmetryClass; + +extern guint gtk_symmetry_get_type(void); +extern GtkWidget *gtk_symmetry_new(unsigned int dimensions, unsigned int trans_dimensions, gboolean has_friedel); +extern Symmetry gtk_symmetry_get_symmetry(GtkSymmetry *symmetry); +extern Symmetry gtk_symmetry_set_symmetry(GtkSymmetry *symmetry, Symmetry new_sym); + +#define GTK_SYMMETRY(obj) GTK_CHECK_CAST(obj, gtk_symmetry_get_type(), GtkSymmetry) +#define GTK_SYMMETRY_CLASS(class) GTK_CHECK_CLASS_CAST(class, gtk_symmetry_get_type(), GtkSymmetryClass) +#define GTK_IS_SYMMETRY(obj) GTK_CHECK_TYPE(obj, gtk_symmetry_get_type()) + +#endif /* GTKSYMMETRY_H */ + diff --git a/src/gtk-valuegraph.c b/src/gtk-valuegraph.c new file mode 100644 index 0000000..90a3469 --- /dev/null +++ b/src/gtk-valuegraph.c @@ -0,0 +1,234 @@ +/* + * gtk-valuegraph.c + * + * A widget to display a graph of a sequence of values + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#include +#include + +#include "gtk-valuegraph.h" + +static GtkObjectClass *parent_class = NULL; + +static void gtk_value_graph_destroy(GtkObject *gtk_value_graph) { + parent_class->destroy(gtk_value_graph); +} + +GtkWidget *gtk_value_graph_new() { + + GtkValueGraph *gtk_value_graph; + + gtk_value_graph = GTK_VALUE_GRAPH(gtk_type_new(gtk_value_graph_get_type())); + gtk_value_graph->data = NULL; + gtk_value_graph->n = 0; + + return GTK_WIDGET(gtk_value_graph); + +} + +static GObject *gtk_value_graph_constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { + + GtkValueGraphClass *class; + GObjectClass *p_class; + GObject *obj; + + class = GTK_VALUE_GRAPH_CLASS(g_type_class_peek(gtk_value_graph_get_type())); + p_class = G_OBJECT_CLASS(g_type_class_peek_parent(class)); + + obj = p_class->constructor(type, n_construct_properties, construct_properties); + + return obj; + +} + +static void gtk_value_graph_class_init(GtkValueGraphClass *class) { + + GtkObjectClass *object_class; + GObjectClass *g_object_class; + + object_class = (GtkObjectClass *) class; + g_object_class = G_OBJECT_CLASS(class); + + object_class->destroy = gtk_value_graph_destroy; + g_object_class->constructor = gtk_value_graph_constructor; + + parent_class = gtk_type_class(gtk_drawing_area_get_type()); + +} + + +static gint gtk_value_graph_draw(GtkWidget *graph, GdkEventExpose *event, gpointer data) { + + GtkValueGraph *vg; + unsigned int bw_left, bw_right, bw_top, bw_bottom; + PangoLayout *y0_layout; + PangoLayout *y1_layout; + PangoLayout *x0_layout; + PangoLayout *x1_layout; + PangoRectangle y0_extent, y1_extent, x0_extent, x1_extent; + unsigned int width, height; + char tmp[32]; + unsigned int i; + + vg = GTK_VALUE_GRAPH(graph); + + /* Blank white background */ + gdk_draw_rectangle(graph->window, graph->style->white_gc, TRUE, 0, 0, graph->allocation.width, graph->allocation.height); + + /* Create PangoLayouts for labels */ + y0_layout = gtk_widget_create_pango_layout(graph, "0"); + pango_layout_get_pixel_extents(y0_layout, NULL, &y0_extent); + + if ( fabs(log(vg->ymax)/log(10)) < 3 ) { + snprintf(tmp, 31, "%.4f", vg->ymax); + } else { + snprintf(tmp, 31, "%1.1e", vg->ymax); + } + y1_layout = gtk_widget_create_pango_layout(graph, tmp); + pango_layout_get_pixel_extents(y1_layout, NULL, &y1_extent); + + x0_layout = gtk_widget_create_pango_layout(graph, "0"); + pango_layout_get_pixel_extents(x0_layout, NULL, &x0_extent); + + if ( vg->xmax < 1000 ) { + snprintf(tmp, 31, "%i", vg->xmax); + } else { + snprintf(tmp, 31, "%1.1e", (double)vg->xmax); + } + x1_layout = gtk_widget_create_pango_layout(graph, tmp); + pango_layout_get_pixel_extents(x1_layout, NULL, &x1_extent); + + /* Determine border widths */ + bw_left = 1+((y1_extent.width > y0_extent.width) ? y1_extent.width : y0_extent.width); + bw_right = 1+x1_extent.width/2; + bw_top = 1+y1_extent.height/2; + bw_bottom = 1+((x1_extent.height > x0_extent.height) ? x1_extent.height : x0_extent.height); + width = graph->allocation.width; + height = graph->allocation.height; + + /* Draw axis lines */ + gdk_draw_line(graph->window, graph->style->black_gc, bw_left, height-1-bw_bottom, bw_left, bw_top); + gdk_draw_line(graph->window, graph->style->black_gc, bw_left, height-1-bw_bottom, width-1-bw_right, height-1-bw_bottom); + + /* Label axes */ + gdk_draw_layout(graph->window, graph->style->black_gc, 1+bw_left-x0_extent.width/2, height-1-bw_bottom, x0_layout); + gdk_draw_layout(graph->window, graph->style->black_gc, width-bw_right-x1_extent.width/2, height-1-bw_bottom, x1_layout); + gdk_draw_layout(graph->window, graph->style->black_gc, bw_left-y0_extent.width-1, height-1-bw_bottom-y0_extent.height/2, y0_layout); + gdk_draw_layout(graph->window, graph->style->black_gc, bw_left-y1_extent.width-1, 1, y1_layout); + + /* Plot data */ + for ( i=0; in; i++ ) { + + unsigned int x, y; + double xd, yd; + + xd = (((double)width-bw_left-bw_right)/(double)vg->xmax)*(double)(i+1); /* Graph axes go from 1 */ + x = bw_left + xd; + yd = (((double)height-bw_top-bw_bottom)/(double)vg->ymax)*(double)vg->data[i]; + y = height-bw_bottom - yd; + + gdk_draw_point(graph->window, graph->style->black_gc, x, y); + + } + + return 0; + +} + +static void gtk_value_graph_init(GtkValueGraph *gtk_value_graph) { + gtk_widget_set_size_request(GTK_WIDGET(gtk_value_graph), 100, 200); + g_signal_connect(G_OBJECT(gtk_value_graph), "expose_event", G_CALLBACK(gtk_value_graph_draw), NULL); +} + +guint gtk_value_graph_get_type(void) { + + static guint gtk_value_graph_type = 0; + + if ( !gtk_value_graph_type ) { + + GtkTypeInfo gtk_value_graph_info = { + "GtkValueGraph", + sizeof(GtkValueGraph), + sizeof(GtkValueGraphClass), + (GtkClassInitFunc) gtk_value_graph_class_init, + (GtkObjectInitFunc) gtk_value_graph_init, + NULL, + NULL, + (GtkClassInitFunc) NULL, + }; + gtk_value_graph_type = gtk_type_unique(gtk_drawing_area_get_type(), >k_value_graph_info); + + } + + return gtk_value_graph_type; + +} + +static double gtk_value_graph_peak(double *data, unsigned int n) { + + unsigned int i; + double max; + + if ( n == 0 ) return 1; + + max = 0; + for ( i=0; i max ) max = data[i]; + } + + return max; + +} + +/* Calculate the best range for a axis with maximum value n */ +static double gtk_value_graph_axis_max(double n) { + + double mantissa, exponent, test; +return n; + if ( n == 0 ) return 1; + + /* Convert to standard form */ + exponent = rint(log(n)/log(10)); + mantissa = n / pow(10, exponent); + + /* Check if the value can be exactly represented */ + test = mantissa * 10; + test = rint(test); + test /= 10; + if ( fabs(test - mantissa) > 0.001 ) { + + /* Round the mantissa upwards */ + mantissa += 0.1; + mantissa *= 10; + mantissa = rint(mantissa); + mantissa /= 10; + + } /* Else don't touch it */ + + return mantissa*pow(10, exponent); + +} + +void gtk_value_graph_set_data(GtkValueGraph *vg, double *data, unsigned int n) { + + double dmax; + + /* Recalculate axes */ + dmax = gtk_value_graph_peak(data, n); + vg->data = data; + vg->n = n; + vg->xmax = gtk_value_graph_axis_max(n); + vg->ymax = gtk_value_graph_axis_max(dmax); + + //printf("n=%i, dmax=%f => xmax=%i, ymax=%f\n", n, dmax, vg->xmax, vg->ymax); + + /* Schedule redraw */ + gtk_widget_queue_draw_area(GTK_WIDGET(vg), 0, 0, GTK_WIDGET(vg)->allocation.width, GTK_WIDGET(vg)->allocation.height); + +} diff --git a/src/gtk-valuegraph.h b/src/gtk-valuegraph.h new file mode 100644 index 0000000..e048739 --- /dev/null +++ b/src/gtk-valuegraph.h @@ -0,0 +1,41 @@ +/* + * gtk-valuegraph.c + * + * A widget to display a graph of a sequence of values + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifndef GTKVALUEGRAPH_H +#define GTKVALUEGRAPH_H + +#include + +typedef struct { + + GtkDrawingArea parent; /* Parent widget */ + + double *data; /* Data to be graphed */ + unsigned int n; /* Number of data points */ + unsigned int xmax; /* Maximum value on x (index) axis */ + double ymax; /* Maximum value on y (data) axis */ + +} GtkValueGraph; + +typedef struct { + GtkDrawingAreaClass parent_class; + void (* changed) (GtkValueGraph *gtkvaluegraph); +} GtkValueGraphClass; + +extern guint gtk_value_graph_get_type(void); +extern GtkWidget *gtk_value_graph_new(void); +extern void gtk_value_graph_set_data(GtkValueGraph *vg, double *data, unsigned int n); + +#define GTK_VALUE_GRAPH(obj) GTK_CHECK_CAST(obj, gtk_value_graph_get_type(), GtkValueGraph) +#define GTK_VALUE_GRAPH_CLASS(class) GTK_CHECK_CLASS_CAST(class, gtk_value_graph_get_type(), GtkValueGraphClass) +#define GTK_IS_VALUE_GRAPH(obj) GTK_CHECK_TYPE(obj, gtk_value_graph_get_type()) + +#endif /* GTKVALUEGRAPH_H */ diff --git a/src/luzzatti.c b/src/luzzatti.c new file mode 100644 index 0000000..4aca763 --- /dev/null +++ b/src/luzzatti.c @@ -0,0 +1,91 @@ +/* + * luzzatti.c + * + * Plot of R-factor against resolution + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "main.h" +#include "statistics.h" +#include "displaywindow.h" +#include "model.h" + +#define LUZZATTI_BINS 50 + +static void luzzatti_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void luzzatti_show() { + + FILE *fh; + float a = data_a(); + float b = data_b(); + float c = data_c(); + unsigned int i; + FILE *gnuplot; + double scale; + ReflectionList *reflections; + ReflectionList *model_reflections; + + reflections = main_reflist(); + model_reflections = model_calculate_f(reflections, NULL, 69); + + scale = stat_scale(reflections, model_reflections); + fh = fopen("synth2d-luzzatti.dat", "w"); + + for ( i=1; in_reflections; i++ ) { + + double s, residual; + signed int h, k, l; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + s = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + residual = 100*fabs((reflections->refs[i].amplitude - scale*model_reflections->refs[i].amplitude)/reflections->refs[i].amplitude); + printf("%3i %3i %3i: obs=%f calc=%f => residual=%f\n", h, k, l, reflections->refs[i].amplitude, + scale*model_reflections->refs[i].amplitude, residual); + fprintf(fh, "%f %f\n", s, residual); + } + + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + luzzatti_gpwrite(gnuplot, "set autoscale\n"); + luzzatti_gpwrite(gnuplot, "unset log\n"); + luzzatti_gpwrite(gnuplot, "unset label\n"); + luzzatti_gpwrite(gnuplot, "set xtic auto\n"); + luzzatti_gpwrite(gnuplot, "set ytic auto\n"); + luzzatti_gpwrite(gnuplot, "set grid\n"); + luzzatti_gpwrite(gnuplot, "set ylabel 'R (%)' font \"Helvetica,10\"\n"); + luzzatti_gpwrite(gnuplot, "set xlabel '1/d / nm^(-1)' font \"Helvetica,10\"\n"); + luzzatti_gpwrite(gnuplot, "set title 'Luzzatti Plot' font \"Helvetica,10\"\n"); + luzzatti_gpwrite(gnuplot, "plot 'synth2d-luzzatti.dat'\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} diff --git a/src/luzzatti.h b/src/luzzatti.h new file mode 100644 index 0000000..a334a8c --- /dev/null +++ b/src/luzzatti.h @@ -0,0 +1,21 @@ +/* + * luzzatti.h + * + * Plot of R-factor against resolution + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef LUZZATTI_H +#define LUZZATTI_H + +extern void luzzatti_show(void); + +#endif /* LUZZATTI_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a2fff85 --- /dev/null +++ b/src/main.c @@ -0,0 +1,471 @@ +/* + * main.c + * + * The Top Level Source File + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "data.h" +#include "main.h" +#include "reflist.h" +#include "normalise.h" +#include "dpsynth.h" +#include "argand.h" +#include "symmetry.h" +#include "gsf.h" +#include "cflip.h" +#include "cdm.h" +#include "clean.h" +#include "model.h" +#include "geometry.h" +#include "statistics.h" +#include "elements.h" +#include "refine.h" +#include "superlattice.h" +#include "options.h" + + +ReflectionList *main_reflections = NULL; + +/* A rather shameless glue layer to avoid sharing information */ +void main_show_patterson() { displaywindow_show_patterson(main_reflections); } +void main_show_pattersone() { displaywindow_show_pattersone(main_reflections); } +void main_show_knownphases() { displaywindow_show_knownphases(main_reflections); } +void main_show_calcphases() { displaywindow_show_calcphases(main_reflections); } +void main_show_difference() { + ReflectionList *reflections = reflist_copy(main_reflections); + model_calculate_difference_coefficients(reflections); + displaywindow_show_difference(reflections); + reflist_free(reflections); +} +void main_show_refsyn() { + ReflectionList *reflections = reflist_copy(main_reflections); + model_calculate_refinement_coefficients(reflections); + displaywindow_show_refsyn(reflections); + reflist_free(reflections); +} +void main_show_diffpatt() { + ReflectionList *reflections = reflist_copy(main_reflections); + model_calculate_difference_coefficients(reflections); + displaywindow_show_diffpatt(reflections); + reflist_free(reflections); +} +void main_wilsonplot() { normalise_wilsonplot(main_reflections); } +void main_falloffplot() { normalise_falloffplot(main_reflections); } +void main_dpsynth() { dpsynth_main_open(main_reflections); } +void main_dpsynth_update() { dpsynth_main_update(main_reflections); } +void main_argand() { argand_open(main_reflections); } +void main_argand_update() { argand_update(main_reflections); } +void main_dethermalise(double level) { normalise_dethermalise(main_reflections, level); } +void main_normalise_exponential(double a, double b, double c) { normalise_exponential(main_reflections, a, b, c); } +void main_normalise(double level) { normalise_execute(main_reflections, level); } +void main_gsf_initialise() { gsf_initialise(main_reflections); } +void main_gsf_reset() { gsf_reset(main_reflections); } +void main_aperture_open() { clean_aperture_open(main_reflections); } +unsigned int main_cdm_tangentexpansion(CDMContext *cdm) { + cdm->reflections = main_reflections; + return cdm_tangentexpansion(cdm); +} +void main_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol) { + cdm_display_phasing_solution(cdm, sol, main_reflections); +} +void main_symmetrise(Symmetry symmetry) { + + char string[256]; + double rsym; + SymFlags flags = 0; + + if ( displaywindow_mode() == DWV_KNOWNPHASE ) { + flags = flags | SYMFLAG_PHASES_KNOWN; + } else if ( displaywindow_mode() == DWV_CALCPHASE ) { + flags = flags | SYMFLAG_PHASES_CALC; + } /* else amplitudes only */ + + rsym = symmetry_symmetrise(main_reflections, symmetry, flags); + + main_displayr(); + main_dpsynth_update(); + + displaywindow_switchview(); + snprintf(string, 255, "Rsym=%.2f%%", rsym*100); + displaywindow_statusbar(string); + +} +void main_geometry_correct(GeometryCorrectionType correction_type, double wavenumber, double circleradius) { + geometry_correct(main_reflections, correction_type, wavenumber, circleradius); +} + +void main_superlattice_split(unsigned int xc, unsigned int yc) { + + char string[256]; + + main_substitutereflections(superlattice_split(main_reflections, xc, yc)); + displaywindow_switchview(); + snprintf(string, 255, "Split %ix%i superlattice", xc, yc); + displaywindow_statusbar(string); + +} + +/* Cut out reflections with |g|<2.5 nm^-1 */ +void main_hpfilter() { + + unsigned int i; + double a, b, c; + char string[256]; + unsigned int n_del, n_orig; + + a = data_a(); b = data_b(); c = data_c(); + n_del = 0; n_orig = main_reflections->n_reflections; + + for ( i=1; in_reflections; i++ ) { + + double g; + signed int h = main_reflections->refs[i].h; + signed int k = main_reflections->refs[i].k; + signed int l = main_reflections->refs[i].l; + + g = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + + if ( g < 2.5 ) { + reflist_delref(main_reflections, h, k, l); + n_del++; + } + } + + displaywindow_switchview(); + snprintf(string, 255, "%0.2f%% of reflections filtered out (|g|<2.5nm^-1)", ((double)n_del/n_orig)*100); + displaywindow_statusbar(string); + +} + +void main_savereflections(const char *filename) { + + FILE *fh; + unsigned int i; + int sim; + + fh = fopen(filename, "w"); + + fprintf(fh, "a %f\n", data_a()); + fprintf(fh, "b %f\n", data_b()); + fprintf(fh, "c %f\n", data_c()); + fprintf(fh, "angle %f\n", 180*data_gamma()/M_PI); + fprintf(fh, "scale %i\n", data_get_image_scale()); + + switch ( displaywindow_mode() ) { + case DWV_PATTERSON : sim = 0; break; + case DWV_PATTERSONE : sim = 0; break; + case DWV_KNOWNPHASE : sim = 0; break; + /* DWV_REALSPACE should never get here */ + case DWV_DIFFERENCE : sim = 1; break; + case DWV_DIFFPATT : sim = 1; break; + case DWV_REFSYN : sim = 1; break; + case DWV_MODEL : sim = 1; break; + case DWV_SIMPATT : sim = 1; break; + case DWV_SIMFOLZPATT : sim = 1; break; + case DWV_EXITWAVE : sim = 1; break; + default : sim = 0; break; + } + + if ( sim ) { + + ReflectionList *calc; + + switch ( displaywindow_mode() ) { + case DWV_DIFFERENCE : calc = reflist_copy(main_reflections); + model_calculate_difference_coefficients(calc); break; + case DWV_DIFFPATT : calc = reflist_copy(main_reflections); + model_calculate_difference_coefficients(calc); break; + case DWV_REFSYN : calc = reflist_copy(main_reflections); + model_calculate_refinement_coefficients(calc); break; + case DWV_MODEL : calc = model_calculate_f(NULL, NULL, 0); break; + case DWV_SIMPATT : calc = model_calculate_f(NULL, NULL, 0); break; + case DWV_SIMFOLZPATT : calc = model_calculate_f(NULL, NULL, 1); break; + case DWV_EXITWAVE : calc = model_calculate_f(NULL, NULL, 0); break; + default : calc = model_calculate_f(NULL, NULL, 0); break; + } + + for ( i=1; in_reflections; i++ ) { + signed int h = calc->refs[i].h; + signed int k = calc->refs[i].k; + signed int l = calc->refs[i].l; + double am = calc->refs[i].amplitude; + if ( (displaywindow_mode() != DWV_DIFFPATT) && (displaywindow_mode() != DWV_SIMPATT) && (displaywindow_mode() != DWV_SIMFOLZPATT) ) { + fprintf(fh, "%3i %3i %3i %8f %8f\n", h, k, l, am, calc->refs[i].phase_known); + } else { + fprintf(fh, "%3i %3i %3i %8f\n", h, k, l, am); + } + } + reflist_free(calc); + + } else { + + for ( i=1; in_reflections; i++ ) { + + signed int h = main_reflections->refs[i].h; + signed int k = main_reflections->refs[i].k; + signed int l = main_reflections->refs[i].l; + double am = main_reflections->refs[i].amplitude; + + switch ( displaywindow_mode() ) { + + case DWV_PATTERSON : fprintf(fh, "%3i %3i %3i %8f\n", h, k, l, am); break; + case DWV_PATTERSONE : fprintf(fh, "%3i %3i %3i %8f\n", h, k, l, am); break; + case DWV_KNOWNPHASE : if ( main_reflections->refs[i].phase_known_set ) + fprintf(fh, "%3i %3i %3i %8f %8f\n", h, k, l, am, main_reflections->refs[i].phase_known); + break; + case DWV_CALCPHASE : if ( main_reflections->refs[i].phase_calc_set ) + fprintf(fh, "%3i %3i %3i %8f %8f\n", h, k, l, am, main_reflections->refs[i].phase_calc); + break; + default : break; + + } + + } + + } + + fclose(fh); + +} + +static void main_reset() { + + unsigned int i; + unsigned int phases_known; + + if ( main_reflections ) { + free(main_reflections); + } + main_reflections = reflist_new(); + memcpy(main_reflections, data_getreflections(), sizeof(ReflectionList)); + data_free(); + + /* Decide whether to start with known phases or Patterson */ + phases_known = 0; + for ( i=0; in_reflections; i++ ) { + if ( main_reflections->refs[i].phase_known_set ) { + phases_known++;; + } + } + printf("MA: Have phase values for %i of %i reflections\n", phases_known, main_reflections->n_reflections); + if ( phases_known == 0 ) { + displaywindow_show_patterson(main_reflections); + printf("MA: No phase values known - displaying Patterson map instead.\n"); + displaywindow_statusbar("No phase values known - displaying Patterson map instead"); + } else { + displaywindow_show_knownphases(main_reflections); + displaywindow_statusbar("Displaying known phases from file"); + } + + displaywindow_brightness_auto(NULL, NULL); + +} + +int main(int argc, char *argv[]) { + + /* Sort out configuration */ + options_load(); + + g_thread_init(NULL); + gtk_init(&argc, &argv); + + if ( argc != 2 ) { + fprintf(stderr, "Syntax: %s \n", argv[0]); + return 1; + } + + gsl_set_error_handler_off(); + + /* Read data */ + if ( data_read(argv[1]) ) { + return 1; + } + + elements_initialise(); + model_default(); + + /* Open main window */ + displaywindow_open(argv[1]); + + /* Execute first transform */ + main_reset(); + + gtk_main(); + + options_save(); + + return 0; + +} + +void main_substitutereflections(ReflectionList *new) { + memcpy(main_reflections, new, sizeof(ReflectionList)); + displaywindow_switchview(); +} + +void main_displayr() { + + ReflectionList *model_reflections; + char r_string[256]; + + model_reflections = model_calculate_f(main_reflections, NULL, 69); + dpsynth_simdp_update(model_reflections); + if ( !model_current_is_blank() ) { + snprintf(r_string, 255, "R1=%.2f%%, R2=%.2f%%", 100*stat_r(main_reflections, model_reflections), + 100*stat_r2(main_reflections, model_reflections)); + displaywindow_statusbar(r_string); + } + reflist_free(model_reflections); + +} + +/* Caution! */ +ReflectionList *main_reflist() { return main_reflections; } + +unsigned int main_max_h() { + unsigned int max, i; + max = 0; + for ( i=1; in_reflections; i++ ) { + if ( abs(main_reflections->refs[i].h) > max ) max = abs(main_reflections->refs[i].h); + } + return max; +} + +unsigned int main_max_k() { + unsigned int max, i; + max = 0; + for ( i=1; in_reflections; i++ ) { + if ( abs(main_reflections->refs[i].k) > max ) max = abs(main_reflections->refs[i].k); + } + return max; +} + +void main_stripzero() { + + int i; + + i = reflist_inlist(main_reflections, 0, 0, 0); + assert(i == 0); + main_reflections->refs[i].amplitude = 0.0; + +} + +/* Anti-alias the pattern by restricting resolution to a circle */ +void main_antialias() { + + unsigned int i; + double max; + double ph, pk, mh, mk; + + /* Find the resolution in each direction */ + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].k != 0 ) continue; + if ( main_reflections->refs[i].h < 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + ph = max; + + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].k != 0 ) continue; + if ( main_reflections->refs[i].h > 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + mh = max; + + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].h != 0 ) continue; + if ( main_reflections->refs[i].k < 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + pk = max; + + max = 0.0; + for ( i=1; in_reflections; i++ ) { + double res; + /* Find reflections on centre line */ + if ( main_reflections->refs[i].h != 0 ) continue; + if ( main_reflections->refs[i].k > 0 ) continue; + res = resolution(main_reflections->refs[i].h, + main_reflections->refs[i].k, + main_reflections->refs[i].l, + data_a(), data_b(), data_c(), data_gamma()); + /* Find highest resolution in this direction */ + if ( res > max ) max = res; + } + mk = max; + + /* Find the smallest */ + double hm, km, m; + if ( mh < ph ) hm = mh; else hm = ph; + if ( mk < pk ) km = mk; else km = pk; + if ( hm < km ) m = hm; else m=km; + + printf("Data resolution is %f nm^-1\n", ph); + + i = 1; + while ( i < main_reflections->n_reflections ) { + + double res; + signed int h, k, l; + + h = main_reflections->refs[i].h; + k = main_reflections->refs[i].k; + l = main_reflections->refs[i].l; + res = resolution(h, k, l, data_a(), data_b(), data_c(), + data_gamma()); + //printf("Resolution of %3i %3i %3i is %f - ", h, k, l, res); + if ( res >= m ) { + reflist_delref(main_reflections, h, k, l); + // printf("eliminated\n"); + } else { + // main_reflections->refs[i].amplitude = 1000000000; + // printf("maximised\n"); + i++; + } + + } + +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..487e6cd --- /dev/null +++ b/src/main.h @@ -0,0 +1,69 @@ +/* + * main.h + * + * The Top Level Source File + * + * (c) 2006-2008 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef MAIN_H +#define MAIN_H + +#include + +#include "reflist.h" +#include "symmetry.h" +#include "geometry.h" +#include "refine.h" +#include "cdm.h" +#include "cflip.h" + + +extern void main_show_patterson(void); +extern void main_show_pattersone(void); +extern void main_show_knownphases(void); +extern void main_show_calcphases(void); +extern void main_show_difference(void); +extern void main_show_refsyn(void); +extern void main_show_diffpatt(void); + +extern void main_wilsonplot(void); +extern void main_falloffplot(void); +extern void main_dpsynth(void); +extern void main_dpsynth_update(void); +extern void main_argand(void); +extern void main_argand_update(void); +extern void main_dethermalise(double level); +extern void main_normalise_exponential(double a, double b, double c); +extern void main_normalise(double level); +extern void main_symmetrise(Symmetry symmetry); +extern void main_gsf_initialise(); +extern void main_gsf_reset(); +extern unsigned int main_cdm_tangentexpansion(CDMContext *cdm); +extern void main_aperture_open(void); +extern void main_display_phasing_solution(CDMContext *cdm, PhasingSolution *sol); +extern void main_savereflections(const char *filename); + +extern void main_substitutereflections(ReflectionList *new); +extern void main_geometry_correct(GeometryCorrectionType correction_type, double wavenumber, double circleradius); +extern void main_displayr(void); +extern void main_superlattice_split(unsigned int xc, unsigned int yc); +extern void main_hpfilter(void); + +extern ReflectionList *main_reflist(void); + +extern unsigned int main_max_h(void); +extern unsigned int main_max_k(void); + +extern void main_stripzero(void); +extern void main_antialias(void); + +#endif /* MAIN_H */ + diff --git a/src/model-display.c b/src/model-display.c new file mode 100644 index 0000000..4eeb83a --- /dev/null +++ b/src/model-display.c @@ -0,0 +1,411 @@ +/* + * model-display.c + * + * Display atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#if HAVE_CAIRO +#include +#include +#endif + +#include "model.h" +#include "elements.h" +#include "symmetry.h" + +#if HAVE_CAIRO +static double model_oblique_x(double x, double y, int height, double gamma, int ny) { + + double offs; + + if ( gamma <= M_PI_2 ) { + offs = 0; + } else { + offs = (height*ny)*(-cos(gamma)); + } + + return 20.0 + offs + x + (y*cos(gamma)); + +} + +static double model_oblique_y(double x, double y, double gamma) { + + return 20.0 + y * sin(gamma); + +} + +static void model_display_draw_atoms(cairo_t *dctx, AtomicModel *model, int width, int height, double gamma, int nx, int ny, double h, int names, int heights) { + + cairo_surface_t *surface; + size_t i; + cairo_font_extents_t ext; + + surface = cairo_get_target(dctx); + + for ( i=0; in_atoms; i++ ) { + + AtomicModel *equivalents; + size_t j; + double x, y; + + if ( !model->atoms[i].active ) continue; + + equivalents = symmetry_generate_equivalent_atoms(model, i, MODEL_TARGET_DISPLAY); + + for ( j=0; jn_atoms; j++ ) { + + double R; + int xp, yp; + + x = width*(double)equivalents->atoms[j].x; + y = height*(double)equivalents->atoms[j].y; + R = sqrt(elements[equivalents->atoms[j].ref].z); + + for ( xp=0; xpatoms[i].occ < 1.0 ) { + cairo_set_source_rgb(dctx, 0.4, 0.4, 0.4); + } else if ( model->atoms[i].occ > 1.0 ) { + cairo_set_source_rgb(dctx, 0.4, 0, 0); + } else { + cairo_set_source_rgb(dctx, 0, 0, 0); + } + + cairo_arc(dctx, model_oblique_x(x+(width*xp), y+(height*yp), height, gamma, ny), + h-model_oblique_y(x+(width*xp), y+(height*yp), gamma), + R, 0, 2*M_PI); + cairo_fill(dctx); + } + } + + } + + model_free(equivalents); + + cairo_set_source_rgb(dctx, 0, 0, 1); + cairo_select_font_face(dctx, "Sans", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_NORMAL); + cairo_font_extents(dctx, &ext); + + /* Only the unique atoms are labelled */ + x = 5 + width*(double)model->atoms[i].x + sqrt(elements[model->atoms[i].ref].z); + y = height*(double)model->atoms[i].y; + cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma)); + if ( names ) { + cairo_show_text(dctx, elements[model->atoms[i].ref].element_name); + y -= ext.height; + } + x = 5 + width*(double)model->atoms[i].x + sqrt(elements[model->atoms[i].ref].z); + if ( heights ) { + + char tmp[32]; + + cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma)); + cairo_show_text(dctx, "z="); + snprintf(tmp, 31, "%.3f", model->atoms[i].z); + cairo_show_text(dctx, tmp); + y -= ext.height; + + } + if ( model->atoms[i].occ != 1.0 ) { + + char tmp[32]; + + cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma)); + cairo_show_text(dctx, "occ: "); + snprintf(tmp, 31, "%.2f", model->atoms[i].occ); + cairo_show_text(dctx, tmp); + y -= ext.height; + + } + + } + +} + +static void model_display_draw_diad(cairo_t *dctx, double x, double y) { + + cairo_matrix_t matrix; + + cairo_get_matrix(dctx, &matrix); + cairo_new_path(dctx); + cairo_set_source_rgb(dctx, 1, 0, 0); + cairo_translate(dctx, x, y); + cairo_scale(dctx, 1.0, 2.0); + cairo_arc(dctx, 0.0, 0.0, 3, 0, 2*M_PI); + cairo_fill(dctx); + cairo_set_matrix(dctx, &matrix); + +} + +static void model_display_draw_glide(cairo_t *dctx, double x1, double y1, double x2, double y2, int height, double gamma, int ny, double h) { + + double dash; + + cairo_new_path(dctx); + cairo_set_source_rgb(dctx, 1, 0, 0); + cairo_set_line_width(dctx, 1.0); + dash = 3.0; cairo_set_dash(dctx, &dash, 1, 0); + cairo_move_to(dctx, model_oblique_x(x1, y1, height, gamma, ny), h-model_oblique_y(x1, y1, gamma)); + cairo_line_to(dctx, model_oblique_x(x2, y2, height, gamma, ny), h-model_oblique_y(x2, y2, gamma)); + cairo_stroke(dctx); + dash = 0.0; cairo_set_dash(dctx, &dash, 0, 0); + +} + +static void model_display_draw_mirror(cairo_t *dctx, double x1, double y1, double x2, double y2, int height, double gamma, int ny, double h) { + + cairo_new_path(dctx); + cairo_set_source_rgb(dctx, 1, 0, 0); + cairo_set_line_width(dctx, 1.0); + cairo_move_to(dctx, model_oblique_x(x1, y1, height, gamma, ny), h-model_oblique_y(x1, y1, gamma)); + cairo_line_to(dctx, model_oblique_x(x2, y2, height, gamma, ny), h-model_oblique_y(x2, y2, gamma)); + cairo_stroke(dctx); + +} + +static void model_display_draw_symmetry(cairo_t *dctx, Symmetry sym, int width, int height, double gamma, int nx, int ny, double h) { + + cairo_surface_t *surface; + + surface = cairo_get_target(dctx); + + if ( sym & SYMMETRY_CENTRE ) { + int x, y; + for ( x=0; xsym, width, height, gamma, nx, ny, h); + + return dctx; + +} + +static void model_display_free_data(guchar *image_data, cairo_t *dctx) { + cairo_surface_finish(cairo_get_target(dctx)); + cairo_destroy(dctx); +} +#endif + +GdkPixbuf *model_display_render_pixbuf(AtomicModel *model, int width, int height, double gamma, int names, int heights, int nx, int ny) { + +#if HAVE_CAIRO + cairo_surface_t *surface; + cairo_t *dctx; + GdkPixbuf *pixbuf; + unsigned char *data; + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, model_display_surface_width(nx*width, ny*height, gamma), + model_display_surface_height(nx*width, ny*height, gamma)); + + if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, "Couldn't create Cairo surface\n"); + cairo_surface_destroy(surface); + return NULL; + } + + dctx = cairo_create(surface); + model_display_do_cairo(dctx, model, width, height, gamma, nx, ny, cairo_image_surface_get_height(surface), names, heights); + + cairo_surface_flush(surface); + + data = cairo_image_surface_get_data(surface); + model_display_swizzle_data(data, cairo_image_surface_get_width(surface) * cairo_image_surface_get_height(surface)); + pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface), + cairo_image_surface_get_stride(surface), + (GdkPixbufDestroyNotify)model_display_free_data, dctx); + + return pixbuf; +#else + return NULL; +#endif + +} + +int model_display_render_pdf(AtomicModel *model, int width, int height, double gamma, const char *filename, int names, int heights, int nx, int ny) { + +#if HAVE_CAIRO + cairo_surface_t *surface; + cairo_t *dctx; + + surface = cairo_pdf_surface_create(filename, model_display_surface_width(nx*width, ny*height, gamma), + model_display_surface_height(nx*width, ny*height, gamma)); + + if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) { + fprintf(stderr, "Couldn't create Cairo surface\n"); + cairo_surface_destroy(surface); + return -1; + } + + dctx = cairo_create(surface); + + model_display_do_cairo(dctx, model, width, height, gamma, nx, ny, model_display_surface_height(nx*width, ny*height, gamma), names, heights); + + cairo_surface_finish(surface); + cairo_destroy(dctx); +#endif + + return 0; + +} + diff --git a/src/model-display.h b/src/model-display.h new file mode 100644 index 0000000..3142c70 --- /dev/null +++ b/src/model-display.h @@ -0,0 +1,27 @@ +/* + * model-display.h + * + * Display atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef MODEL_DISPLAY_H +#define MODEL_DISPLAY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "model.h" + +extern GdkPixbuf *model_display_render_pixbuf(AtomicModel *model, int width, int height, double gamma, int names, int heights, int nx, int ny); +extern int model_display_render_pdf(AtomicModel *model, int width, int height, double gamma, const char *filename, int names, int heights, int nx, int ny); + +#endif /* MODEL_DISPLAY_H */ + diff --git a/src/model-editor.c b/src/model-editor.c new file mode 100644 index 0000000..3146b5d --- /dev/null +++ b/src/model-editor.c @@ -0,0 +1,831 @@ +/* + * model-editor.c + * + * GUI for editing atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "data.h" +#include "elements.h" +#include "main.h" +#include "statistics.h" +#include "refine.h" +#include "model.h" +#include "gtk-symmetry.h" + +enum { + MODEL_ATOMS_COLUMN_ACTIVE, + MODEL_ATOMS_COLUMN_REFINE, + MODEL_ATOMS_COLUMN_ELEMENT, + MODEL_ATOMS_COLUMN_X, + MODEL_ATOMS_COLUMN_Y, + MODEL_ATOMS_COLUMN_Z, /* Atomic coordinates */ + MODEL_ATOMS_COLUMN_B, /* Debye-Waller */ + MODEL_ATOMS_COLUMN_OCC, /* Occupancy */ + MODEL_ATOMS_COLUMNS +}; + +static gint model_save_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + model_save(filename, model_get_current()); + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +gint model_save_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Save Atomic Model", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + #if HAVE_GTK_TEN + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + #endif /* HAVE_GTK_TEN */ + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "synth2d-model.xyz"); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(model_save_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +static gint model_load_response(GtkWidget *dialog, gint response, gpointer data) { + + if ( response == GTK_RESPONSE_ACCEPT ) { + + char *filename; + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + model_load_as_current(filename); + displaywindow_forceview(DWV_MODEL); + g_free(filename); + + } + + gtk_widget_destroy(dialog); + + return 0; + +} + +gint model_load_open(GtkWidget *widget, gpointer data) { + + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new("Load Atomic Model", GTK_WINDOW(displaywindow_gtkwindow()), GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); + + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(model_load_response), NULL); + + gtk_widget_show_all(dialog); + + return 0; + +} + +/* Not all model updates should trigger an update of the display and R-factor + * (e.g. changing a refinement flag) */ +static void model_editor_put_model_noupdate(ModelEditor *editor) { + + GtkTreeIter iter; + gboolean ival; + + editor->model->n_atoms = 0; + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(editor->list_store), &iter); + while ( ival ) { + + double x, y, z, B, occ; + gboolean refine, active; + gchar *tmp; + + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, &x, MODEL_ATOMS_COLUMN_Y, &y, MODEL_ATOMS_COLUMN_Z, &z, + MODEL_ATOMS_COLUMN_ELEMENT, &tmp, + MODEL_ATOMS_COLUMN_B, &B, MODEL_ATOMS_COLUMN_OCC, &occ, + MODEL_ATOMS_COLUMN_ACTIVE, &active, MODEL_ATOMS_COLUMN_REFINE, &refine, -1); + + editor->model->atoms[editor->model->n_atoms].x = x; + editor->model->atoms[editor->model->n_atoms].y = y; + editor->model->atoms[editor->model->n_atoms].z = z; + editor->model->atoms[editor->model->n_atoms].B = B; + editor->model->atoms[editor->model->n_atoms].occ = occ; + editor->model->atoms[editor->model->n_atoms].active = active; + editor->model->atoms[editor->model->n_atoms].refine = refine; + editor->model->atoms[editor->model->n_atoms].ref = elements_lookup(tmp); + editor->model->n_atoms++; + + ival = gtk_tree_model_iter_next(GTK_TREE_MODEL(editor->list_store), &iter); + + } + +} + +/* Transfer model from an editor to its corresponding model */ +static void model_editor_put_model(ModelEditor *editor) { + model_editor_put_model_noupdate(editor); + model_notify_update_editor(editor->model); +} + +/* Transfer model->editor */ +void model_editor_get_model(ModelEditor *editor) { + + size_t i; + char tmp[32]; + + gtk_list_store_clear(GTK_LIST_STORE(editor->list_store)); + for ( i=0; imodel->n_atoms; i++ ) { + + double x, y, z, B, occ; + gboolean refine, active; + GtkTreeIter iter; + + x = editor->model->atoms[i].x; + y = editor->model->atoms[i].y; + z = editor->model->atoms[i].z; + B = editor->model->atoms[i].B; + occ = editor->model->atoms[i].occ; + active = editor->model->atoms[i].active; + refine = editor->model->atoms[i].refine; + + gtk_list_store_append(GTK_LIST_STORE(editor->list_store), &iter); + gtk_list_store_set(editor->list_store, &iter, MODEL_ATOMS_COLUMN_X, x, MODEL_ATOMS_COLUMN_Y, y, MODEL_ATOMS_COLUMN_Z, z, + MODEL_ATOMS_COLUMN_ELEMENT, elements[editor->model->atoms[i].ref].element_name, + MODEL_ATOMS_COLUMN_B, B, MODEL_ATOMS_COLUMN_OCC, occ, + MODEL_ATOMS_COLUMN_ACTIVE, active, MODEL_ATOMS_COLUMN_REFINE, refine, -1); + + }; + + snprintf(tmp, 31, "%.2f", editor->model->thickness); + gtk_entry_set_text(GTK_ENTRY(editor->thickness), tmp); + gtk_symmetry_set_symmetry(GTK_SYMMETRY(editor->symmetry), editor->model->sym); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(editor->point_atoms), editor->model->point_atoms); + + +} + +static gint model_editor_symmetry_changed(GtkWidget *thickness, ModelEditor *editor) { + + editor->model->sym = gtk_symmetry_get_symmetry(GTK_SYMMETRY(editor->symmetry)); + model_notify_update_editor(editor->model); + + return 0; + +} + +static gint model_editor_thickness_edited(GtkWidget *thickness, ModelEditor *editor) { + + const char *str; + float tf; + double t; + char tmp[32]; + + str = gtk_entry_get_text(GTK_ENTRY(thickness)); + sscanf(str, "%f", &tf); t = tf; + editor->model->thickness = t; + + snprintf(tmp, 31, "%.2f", editor->model->thickness); + gtk_entry_set_text(GTK_ENTRY(thickness), tmp); + + model_notify_update_editor(editor->model); + + return 0; + +} + +void model_editor_unlock(ModelEditor *editor) { + + gtk_widget_set_sensitive(editor->add_button, TRUE); + gtk_widget_set_sensitive(editor->model_atoms_tree, TRUE); + +} + +void model_editor_lock(ModelEditor *editor) { + + gtk_widget_set_sensitive(editor->add_button, FALSE); + gtk_widget_set_sensitive(editor->model_atoms_tree, FALSE); + gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(editor->model_atoms_tree))); + +} + +static gint model_atoms_delete_atom(GtkWidget *widget, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_remove(GTK_LIST_STORE(editor->list_store), &iter); + model_editor_put_model(editor); + + return 0; + +} + +static gint model_atoms_add_atom(GtkWidget *widget, ModelEditor *editor) { + + GtkTreeIter iter; + unsigned int number; + + number = gtk_combo_box_get_active(GTK_COMBO_BOX(editor->atom_define)); + gtk_list_store_append(GTK_LIST_STORE(editor->list_store), &iter); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, + MODEL_ATOMS_COLUMN_ELEMENT, elements[number].element_name, + MODEL_ATOMS_COLUMN_ACTIVE, TRUE, MODEL_ATOMS_COLUMN_REFINE, TRUE, + MODEL_ATOMS_COLUMN_B, 0.0005, + MODEL_ATOMS_COLUMN_OCC, 1.0, -1); + model_editor_put_model(editor); + + return 0; + +} + +static gint model_atoms_selection_changed(GtkTreeSelection *selection, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double x, y, z, B, occ; + char tmp[32]; + + gtk_widget_set_sensitive(editor->delete_button, gtk_tree_selection_get_selected(selection, NULL, NULL)); + gtk_widget_set_sensitive(editor->position_table, gtk_tree_selection_get_selected(selection, NULL, NULL)); + + if ( !gtk_tree_selection_get_selected(selection, NULL, NULL) ) return 0; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, &x, + MODEL_ATOMS_COLUMN_Y, &y, MODEL_ATOMS_COLUMN_Z, &z, + MODEL_ATOMS_COLUMN_B, &B, MODEL_ATOMS_COLUMN_OCC, &occ, -1); + + /* Set the scales, but don't trigger a callback */ + editor->scale_lock++; + gtk_range_set_value(GTK_RANGE(editor->x_scale), x); + gtk_range_set_value(GTK_RANGE(editor->y_scale), y); + gtk_range_set_value(GTK_RANGE(editor->z_scale), z); + gtk_range_set_value(GTK_RANGE(editor->b_scale), B); + gtk_range_set_value(GTK_RANGE(editor->occ_scale), occ); + editor->scale_lock--; + + snprintf(tmp, 31, "%f", x); gtk_entry_set_text(GTK_ENTRY(editor->x_edit), tmp); + snprintf(tmp, 31, "%f", y); gtk_entry_set_text(GTK_ENTRY(editor->y_edit), tmp); + snprintf(tmp, 31, "%f", z); gtk_entry_set_text(GTK_ENTRY(editor->z_edit), tmp); + snprintf(tmp, 31, "%f", B); gtk_entry_set_text(GTK_ENTRY(editor->b_edit), tmp); + snprintf(tmp, 31, "%f", occ); gtk_entry_set_text(GTK_ENTRY(editor->occ_edit), tmp); + + return 0; + +} + +static gint model_atoms_active_toggled(GtkCellRendererToggle *cell, gchar *path_string, ModelEditor *editor) { + + GtkTreeIter iter; + gboolean active; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(editor->list_store), &iter, path_string); + + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_ACTIVE, &active, -1); + active = !active; + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_ACTIVE, active, -1); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_atoms_refine_toggled(GtkCellRendererToggle *cell, gchar *path_string, ModelEditor *editor) { + + GtkTreeIter iter; + gboolean refine; + + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(editor->list_store), &iter, path_string); + + gtk_tree_model_get(GTK_TREE_MODEL(editor->list_store), &iter, MODEL_ATOMS_COLUMN_REFINE, &refine, -1); + refine = !refine; + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_REFINE, refine, -1); + + model_editor_put_model_noupdate(editor); + + return 0; + +} + +static gint model_editor_x_edit(GtkWidget *x_edit, ModelEditor *editor) { + + const char *str; + float xf; + double x; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(x_edit)); + sscanf(str, "%f", &xf); x = xf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, x, -1); + + /* This triggers an x_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->x_scale), x); + + return 0; + +} + +static gint model_editor_y_edit(GtkWidget *y_edit, ModelEditor *editor) { + + const char *str; + float yf; + double y; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(y_edit)); + sscanf(str, "%f", &yf); y = yf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Y, y, -1); + + /* This triggers a y_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->y_scale), y); + + return 0; + +} + +static gint model_editor_z_edit(GtkWidget *z_edit, ModelEditor *editor) { + + const char *str; + float zf; + double z; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(z_edit)); + sscanf(str, "%f", &zf); z = zf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Z, z, -1); + + /* This triggers a z_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->z_scale), z); + + return 0; + +} + +static gint model_editor_b_edit(GtkWidget *b_edit, ModelEditor *editor) { + + const char *str; + float bf; + double b; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(b_edit)); + sscanf(str, "%f", &bf); b = bf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_B, b, -1); + + /* This triggers a b_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->b_scale), b); + + return 0; + +} + +static gint model_editor_occ_edit(GtkWidget *occ_edit, ModelEditor *editor) { + + const char *str; + float occf; + double occ; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + str = gtk_entry_get_text(GTK_ENTRY(occ_edit)); + sscanf(str, "%f", &occf); occ = occf; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_OCC, occ, -1); + + /* This triggers an occ_scale callback which updates the model */ + gtk_range_set_value(GTK_RANGE(editor->occ_scale), occ); + + return 0; + +} + +static gint model_editor_x_scale(GtkWidget *x_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double x; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + x = gtk_range_get_value(GTK_RANGE(x_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_X, x, -1); + + snprintf(tmp, 31, "%f", x); gtk_entry_set_text(GTK_ENTRY(editor->x_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_y_scale(GtkWidget *y_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double y; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + y = gtk_range_get_value(GTK_RANGE(y_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Y, y, -1); + + snprintf(tmp, 31, "%f", y); gtk_entry_set_text(GTK_ENTRY(editor->y_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_z_scale(GtkWidget *z_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double z; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + z = gtk_range_get_value(GTK_RANGE(z_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_Z, z, -1); + + snprintf(tmp, 31, "%f", z); gtk_entry_set_text(GTK_ENTRY(editor->z_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_b_scale(GtkWidget *b_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double b; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + b = gtk_range_get_value(GTK_RANGE(b_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_B, b, -1); + + snprintf(tmp, 31, "%f", b); gtk_entry_set_text(GTK_ENTRY(editor->b_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_occ_scale(GtkWidget *occ_scale, ModelEditor *editor) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + double occ; + char tmp[32]; + + if ( editor->scale_lock > 0 ) return 0; + + occ = gtk_range_get_value(GTK_RANGE(occ_scale)); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(editor->model_atoms_tree), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(editor->list_store), &iter, path); + gtk_list_store_set(GTK_LIST_STORE(editor->list_store), &iter, MODEL_ATOMS_COLUMN_OCC, occ, -1); + + snprintf(tmp, 31, "%f", occ); gtk_entry_set_text(GTK_ENTRY(editor->occ_edit), tmp); + + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_pointatoms_toggled(GtkWidget *point_atoms, ModelEditor *editor) { + + editor->model->point_atoms = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(point_atoms)); + model_editor_put_model(editor); + + return 0; + +} + +static gint model_editor_close(GtkWidget *widget, gint response, ModelEditor *editor) { + + /* Bye bye */ + editor->model->editor = NULL; + gtk_widget_destroy(editor->window); + free(editor); + + return 0; + +} + +static void model_editor_do_list(GtkWidget *vbox, ModelEditor *editor) { + + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkCellRenderer *togglerenderer_active; + GtkCellRenderer *togglerenderer_refine; + + GtkWidget *scrolledwindow; + GtkWidget *add_button; + GtkWidget *button_box; + + unsigned int i = 0; + + editor->list_store = gtk_list_store_new(MODEL_ATOMS_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); + model_editor_get_model(editor); + editor->model_atoms_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(editor->list_store)); + + renderer = gtk_cell_renderer_text_new(); + togglerenderer_active = gtk_cell_renderer_toggle_new(); + togglerenderer_refine = gtk_cell_renderer_toggle_new(); + + column = gtk_tree_view_column_new_with_attributes("On", togglerenderer_active, "active", MODEL_ATOMS_COLUMN_ACTIVE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + g_signal_connect(G_OBJECT(togglerenderer_active), "toggled", G_CALLBACK(model_atoms_active_toggled), editor); + + column = gtk_tree_view_column_new_with_attributes("R?", togglerenderer_refine, "active", MODEL_ATOMS_COLUMN_REFINE, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + g_signal_connect(G_OBJECT(togglerenderer_refine), "toggled", G_CALLBACK(model_atoms_refine_toggled), editor); + + column = gtk_tree_view_column_new_with_attributes("Element", renderer, "text", MODEL_ATOMS_COLUMN_ELEMENT, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 100); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("x", renderer, "text", MODEL_ATOMS_COLUMN_X, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("y", renderer, "text", MODEL_ATOMS_COLUMN_Y, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("z", renderer, "text", MODEL_ATOMS_COLUMN_Z, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("B", renderer, "text", MODEL_ATOMS_COLUMN_B, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + column = gtk_tree_view_column_new_with_attributes("Occupancy", renderer, "text", MODEL_ATOMS_COLUMN_OCC, NULL); + gtk_tree_view_column_set_min_width(GTK_TREE_VIEW_COLUMN(column), 70); + gtk_tree_view_append_column(GTK_TREE_VIEW(editor->model_atoms_tree), column); + + scrolledwindow = gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(editor->model_atoms_tree)), + gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(editor->model_atoms_tree))); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolledwindow), GTK_WIDGET(editor->model_atoms_tree)); + + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolledwindow), TRUE, TRUE, 5); + + button_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), button_box, FALSE, FALSE, 5); + + add_button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_box_pack_end(GTK_BOX(button_box), add_button, FALSE, FALSE, 5); + + editor->atom_define = gtk_combo_box_new_text(); + while ( elements[i].z > 0 ) { + gtk_combo_box_append_text(GTK_COMBO_BOX(editor->atom_define), elements[i].element_name); + i++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(editor->atom_define), 0); + gtk_box_pack_end(GTK_BOX(button_box), GTK_WIDGET(editor->atom_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(add_button), "clicked", G_CALLBACK(model_atoms_add_atom), editor); + + editor->delete_button = gtk_button_new_from_stock(GTK_STOCK_DELETE); + gtk_box_pack_end(GTK_BOX(button_box), editor->delete_button, FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(editor->delete_button), "clicked", G_CALLBACK(model_atoms_delete_atom), editor); + + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(editor->model_atoms_tree))), "changed", + G_CALLBACK(model_atoms_selection_changed), editor); + gtk_widget_set_sensitive(editor->delete_button, + gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(editor->model_atoms_tree)), NULL, NULL)); + + editor->add_button = add_button; + +} + +/* Open the model editor for a given model */ +ModelEditor *model_editor_open(AtomicModel *model) { + + ModelEditor *editor; + + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *x_scale_label; + GtkWidget *y_scale_label; + GtkWidget *z_scale_label; + GtkWidget *b_scale_label; + GtkWidget *occ_scale_label; + GtkWidget *top_hbox; + GtkWidget *label; + + /* Don't proceed if an editor is already open for this model */ + if ( model->editor ) return model->editor; + editor = malloc(sizeof(ModelEditor)); + editor->model = model; + editor->scale_lock = 0; + + editor->window = gtk_dialog_new_with_buttons("Edit Atomic Model", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(editor->window), 550, 600); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(editor->window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + top_hbox = gtk_hbox_new(FALSE, 0); + label = gtk_label_new("Thickness: "); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_box_pack_start(GTK_BOX(top_hbox), GTK_WIDGET(label), TRUE, TRUE, 5); + editor->thickness = gtk_entry_new(); + gtk_entry_set_alignment(GTK_ENTRY(editor->thickness), 1); + gtk_entry_set_max_length(GTK_ENTRY(editor->thickness), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->thickness), 8); + gtk_box_pack_start(GTK_BOX(top_hbox), GTK_WIDGET(editor->thickness), TRUE, TRUE, 5); + label = gtk_label_new(" nm"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_box_pack_start(GTK_BOX(top_hbox), GTK_WIDGET(label), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(editor->thickness), "activate", G_CALLBACK(model_editor_thickness_edited), editor); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(top_hbox), FALSE, FALSE, 2); + + editor->point_atoms = gtk_check_button_new_with_label("Point atoms"); + g_signal_connect(G_OBJECT(editor->point_atoms), "toggled", G_CALLBACK(model_editor_pointatoms_toggled), editor); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(editor->point_atoms), FALSE, TRUE, 2); + + editor->symmetry = gtk_symmetry_new(2, 2, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(editor->symmetry), FALSE, FALSE, 2); + g_signal_connect(G_OBJECT(editor->symmetry), "changed", G_CALLBACK(model_editor_symmetry_changed), editor); + + /* Add the list of atoms and Add/Remove widgets */ + model_editor_do_list(vbox, editor); + + /* Add the atomic coordinate editor */ + editor->position_table = gtk_table_new(5, 3, FALSE); + gtk_table_set_homogeneous(GTK_TABLE(editor->position_table), FALSE); + gtk_table_set_row_spacings(GTK_TABLE(editor->position_table), 2); + gtk_table_set_col_spacings(GTK_TABLE(editor->position_table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(editor->position_table), FALSE, FALSE, 5); + + editor->x_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->x_scale), FALSE); + //gtk_range_set_update_policy(GTK_RANGE(editor->x_scale), GTK_UPDATE_DELAYED); + g_signal_connect(G_OBJECT(editor->x_scale), "value-changed", G_CALLBACK(model_editor_x_scale), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->x_scale), 2, 3, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + x_scale_label = gtk_label_new("x"); + gtk_misc_set_alignment(GTK_MISC(x_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(x_scale_label), 1, 2, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->x_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->x_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->x_edit), 8); + g_signal_connect(G_OBJECT(editor->x_edit), "activate", G_CALLBACK(model_editor_x_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->x_edit), 3, 4, 1, 2, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->y_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->y_scale), FALSE); + g_signal_connect(G_OBJECT(editor->y_scale), "value-changed", G_CALLBACK(model_editor_y_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->y_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->y_scale), 2, 3, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + y_scale_label = gtk_label_new("y"); + gtk_misc_set_alignment(GTK_MISC(y_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(y_scale_label), 1, 2, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->y_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->y_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->y_edit), 8); + g_signal_connect(G_OBJECT(editor->y_edit), "activate", G_CALLBACK(model_editor_y_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->y_edit), 3, 4, 2, 3, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->z_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->z_scale), FALSE); + g_signal_connect(G_OBJECT(editor->z_scale), "value-changed", G_CALLBACK(model_editor_z_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->z_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->z_scale), 2, 3, 3, 4, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + z_scale_label = gtk_label_new("z"); + gtk_misc_set_alignment(GTK_MISC(z_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(z_scale_label), 1, 2, 3, 4, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->z_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->z_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->z_edit), 8); + g_signal_connect(G_OBJECT(editor->z_edit), "activate", G_CALLBACK(model_editor_z_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->z_edit), 3, 4, 3, 4, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->b_scale = gtk_hscale_new_with_range(0, 0.001, 0.00001); + gtk_scale_set_draw_value(GTK_SCALE(editor->b_scale), FALSE); + g_signal_connect(G_OBJECT(editor->b_scale), "value-changed", G_CALLBACK(model_editor_b_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->b_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->b_scale), 2, 3, 4, 5, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + b_scale_label = gtk_label_new("B"); + gtk_misc_set_alignment(GTK_MISC(b_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(b_scale_label), 1, 2, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->b_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->b_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->b_edit), 8); + g_signal_connect(G_OBJECT(editor->b_edit), "activate", G_CALLBACK(model_editor_b_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->b_edit), 3, 4, 4, 5, GTK_SHRINK, GTK_SHRINK, 2, 2); + + editor->occ_scale = gtk_hscale_new_with_range(0, 1, 0.05); + gtk_scale_set_draw_value(GTK_SCALE(editor->occ_scale), FALSE); + g_signal_connect(G_OBJECT(editor->occ_scale), "value-changed", G_CALLBACK(model_editor_occ_scale), editor); + //gtk_range_set_update_policy(GTK_RANGE(editor->occ_scale), GTK_UPDATE_DELAYED); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->occ_scale), 2, 3, 5, 6, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 2); + occ_scale_label = gtk_label_new("Occ"); + gtk_misc_set_alignment(GTK_MISC(occ_scale_label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(occ_scale_label), 1, 2, 5, 6, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 2, 2); + editor->occ_edit = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(editor->occ_edit), 10); + gtk_entry_set_width_chars(GTK_ENTRY(editor->occ_edit), 8); + g_signal_connect(G_OBJECT(editor->occ_edit), "activate", G_CALLBACK(model_editor_occ_edit), editor); + gtk_table_attach(GTK_TABLE(editor->position_table), GTK_WIDGET(editor->occ_edit), 3, 4, 5, 6, GTK_SHRINK, GTK_SHRINK, 2, 2); + + g_signal_connect(G_OBJECT(editor->window), "response", G_CALLBACK(model_editor_close), editor); + + gtk_widget_set_sensitive(editor->delete_button, FALSE); + gtk_widget_set_sensitive(editor->position_table, FALSE); + /* If the model is locked, disable things appropriately from the start */ + if ( editor->model->lock_count > 0 ) model_editor_lock(editor); + + gtk_widget_show_all(editor->window); + + return editor; + +} + diff --git a/src/model-editor.h b/src/model-editor.h new file mode 100644 index 0000000..6dc55da --- /dev/null +++ b/src/model-editor.h @@ -0,0 +1,69 @@ +/* + * model-editor.h + * + * GUI for editing atomic models + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef MODEL_EDITOR_H +#define MODEL_EDITOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +typedef struct { + + struct struct_atomicmodel *model; + + GtkWidget *window; + GtkListStore *list_store; + + GtkWidget *thickness; /* Text entry for thickness */ + GtkWidget *symmetry; /* GtkSymmetry for model */ + GtkWidget *point_atoms; + + GtkWidget *model_atoms_tree; + + GtkWidget *add_button; + GtkWidget *atom_define; + GtkWidget *delete_button; + + GtkWidget *position_table; + + GtkWidget *x_scale; + GtkWidget *x_edit; + + GtkWidget *y_scale; + GtkWidget *y_edit; + + GtkWidget *z_scale; + GtkWidget *z_edit; + + GtkWidget *b_scale; + GtkWidget *b_edit; + + GtkWidget *occ_scale; + GtkWidget *occ_edit; + + int scale_lock; /* Don't respond to scale value-changed if this >1 */ + +} ModelEditor; + +extern ModelEditor *model_editor_open(struct struct_atomicmodel *model); +extern void model_editor_get_model(ModelEditor *editor); + +extern gint model_load_open(GtkWidget *widget, gpointer data); +extern gint model_save_open(GtkWidget *widget, gpointer data); +extern void model_editor_update(struct struct_atomicmodel *model); +extern void model_editor_lock(ModelEditor *editor); +extern void model_editor_unlock(ModelEditor *editor); + +#endif /* MODEL_EDITOR_H */ + diff --git a/src/model.c b/src/model.c new file mode 100644 index 0000000..08c1bb2 --- /dev/null +++ b/src/model.c @@ -0,0 +1,523 @@ +/* + * model.c + * + * Atomic Model Structures + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "data.h" +#include "elements.h" +#include "main.h" +#include "statistics.h" +#include "symmetry.h" +#include "model.h" +#include "displaywindow.h" +#include "model-editor.h" +#include "multislice.h" +#include "dpsynth.h" +#include "normalise.h" + +/* This is the SPOT for the "current atomic model", but don't assume it's the only model around... */ +AtomicModel *model_current = NULL; + +/* --------------------------------------------------- Loading and saving of models --------------------------------------------------- */ + +/* NB This doesn't exactly replicate Perl's chomp()... */ +static void chomp(char *line) { + + size_t i; + + for ( i=0; iatoms[model->n_atoms].x = x; + model->atoms[model->n_atoms].y = y; + model->atoms[model->n_atoms].z = z; + model->atoms[model->n_atoms].B = B; + model->atoms[model->n_atoms].occ = occ; + model->atoms[model->n_atoms].ref = elements_lookup(tmp); + model->atoms[model->n_atoms].active = (active == 'A')?1:0; /* Anything other than "A" means "inactive" */ + model->atoms[model->n_atoms].refine = (refine == 'R')?1:0; /* Anything other than "R" means "don't refine" */ + model->n_atoms++; + } else if ( strncmp(line, "symmetry ", 9) == 0 ) { + model->sym = symmetry_encode(line+9); + } else if ( sscanf(line, "thickness %f nm", &thickness) == 1 ) { + model->thickness = thickness; + } else { + fprintf(stderr, "Unrecognised line in model file (line %i)\n", i); + } + + i++; + + } + fclose(fh); + + return model; + +} + +void model_load_as_current(const char *filename) { + + AtomicModel *new; + double new_thickness; + Symmetry new_sym; + + new = model_load(filename); + new_thickness = new->thickness; + new_sym = new->sym; + + model_move(model_current, new); /* Preserves lock_count, editor, refinementwindow, thickness and sym */ + model_free(new); + + /* Restore thickness and symmetry */ + model_current->thickness = new_thickness; + model_current->sym = new_sym; + + model_notify_update(model_current); + +} + +void model_save(const char *filename, AtomicModel *model) { + + FILE *fh; + size_t i; + + fh = fopen(filename, "w"); + fprintf(fh, "symmetry %s\n", symmetry_decode(model->sym)); + fprintf(fh, "thickness %.2f nm\n", model->thickness); + for ( i=0; in_atoms; i++ ) { + + fprintf(fh, "%f %f %f %s %f %f %c %c\n", model->atoms[i].x, model->atoms[i].y, model->atoms[i].z, + elements[model->atoms[i].ref].element_name, model->atoms[i].B, + model->atoms[i].occ, + model->atoms[i].active?'A':'a', model->atoms[i].refine?'R':'r'); + + } + + fclose(fh); + +} + +/* --------------------------------------------------- Basic model handling functions ------------------------------------------------- */ + +AtomicModel *model_new() { + + AtomicModel *model; + + model = malloc(sizeof(AtomicModel)); + + model->n_atoms = 0; + model->sym = PLANEGROUP_P1; + model->thickness = 0.0; + model->point_atoms = 0; + + model->editor = NULL; + model->refine_window = NULL; + model->lock_count = 0; + + return model; + +} + +AtomicModel *model_copy(const AtomicModel *a) { + AtomicModel *model; + model = malloc(sizeof(AtomicModel)); + memcpy(model, a, sizeof(AtomicModel)); + return model; +} + +void model_move(AtomicModel *to, AtomicModel *from) { + + ModelEditor *old_editor; + RefinementWindow *old_refine; + int old_lock_count; + Symmetry old_sym; + double old_thickness; + + old_editor = to->editor; + old_refine = to->refine_window; + old_lock_count = to->lock_count; + old_sym = to->sym; + old_thickness = to->thickness; + + memcpy(to, from, sizeof(AtomicModel)); + + to->editor = old_editor; + to->refine_window = old_refine; + to->lock_count = old_lock_count; + to->sym = old_sym; + to->thickness = old_thickness; + +} + +void model_free(AtomicModel *model) { + free(model); +} + +/* --------------------------------------------------- Structure-Factor Calculations -------------------------------------------------- */ + +/* Generate a template for writing structure factors when there's nothing better to use */ +extern void model_generate_template(ReflectionList *reflections, signed int layer) { + + signed int h; + signed int k; + for ( h=-40; h<=40; h++ ) { + for ( k=-40; k<=40; k++ ) { + reflist_addref(reflections, h, k, layer); + } + } + +} + +/* Return f(j, hkl) */ +static double model_sfac(AtomicModel *model, unsigned int j, signed int h, signed int k, signed int l) { + + unsigned int ref; + double x, y, z; + double sfac; + double s; + double a = data_a() * 10; + double b = data_b() * 10; + double c = data_c() * 10; /* Change to Angstroms */ + double gamma = data_gamma(); + double B; + + if ( model->point_atoms ) return 1; + + x = model->atoms[j].x; y = model->atoms[j].y; z = model->atoms[j].z; + ref = model->atoms[j].ref; B = model->atoms[j].B; + + s = resolution(h, k, l, a, b, c, gamma); + + /* Calculate f(j,hkl) */ + sfac = elements[ref].sfac_c; + sfac += elements[ref].sfac_a1 * exp(-elements[ref].sfac_b1 * s * s); + sfac += elements[ref].sfac_a2 * exp(-elements[ref].sfac_b2 * s * s); + sfac += elements[ref].sfac_a3 * exp(-elements[ref].sfac_b3 * s * s); + sfac += elements[ref].sfac_a4 * exp(-elements[ref].sfac_b4 * s * s); + + /* Thermal factor */ + sfac = sfac * exp(- B * s * s); + + return sfac; + +} + +/* Real part of a particular atom's (and it's equivalents') contribution to F */ +static double model_f_contribution_re(AtomicModel *model, unsigned int j, signed int h, signed int k, signed int l) { + + double sfac, cont; + AtomicModel *equivalents; + size_t i; + + sfac = model_sfac(model, j, h, k, l); + equivalents = symmetry_generate_equivalent_atoms(model, j, MODEL_TARGET_CALCULATION); + + cont = 0; + for ( i=0; in_atoms; i++ ) { + double dot, x, y, z; + x = equivalents->atoms[i].x; y = equivalents->atoms[i].y; z = equivalents->atoms[i].z; + dot = h * (1-x) + k * (1-y) + l * z; + cont += model->atoms[j].occ * sfac * cos(-2 * M_PI * dot); + } + + model_free(equivalents); + + return cont; + +} + +/* Imaginary part of a particular atom's (and it's equivalents') contribution to F */ +static double model_f_contribution_im(AtomicModel *model, unsigned int j, signed int h, signed int k, signed int l) { + + double sfac, cont; + AtomicModel *equivalents; + size_t i; + + sfac = model_sfac(model, j, h, k, l); + equivalents = symmetry_generate_equivalent_atoms(model, j, MODEL_TARGET_CALCULATION); + + cont = 0; + for ( i=0; in_atoms; i++ ) { + double dot, x, y, z; + x = equivalents->atoms[i].x; y = equivalents->atoms[i].y; z = equivalents->atoms[i].z; + dot = h * (1-x) + k * (1-y) + l * z; + cont += model->atoms[j].occ * sfac * sin(-2 * M_PI * dot); + } + + model_free(equivalents); + + return cont; + +} + +double model_mod_f(AtomicModel *model, signed int h, signed int k, signed int l) { + + double sf_re, sf_im; + unsigned int j; + + sf_re = 0; sf_im = 0; + /* Work out the total contribution */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].active ) { + sf_re += model_f_contribution_re(model, j, h, k, l); + sf_im += model_f_contribution_im(model, j, h, k, l); + } + } + + return sqrt((sf_re*sf_re) + (sf_im*sf_im)); + +} + +/* Calculate kinematical structure factor amplitudes. + * "layer" is meaningless and is ignored if "given_template" is not NULL */ +ReflectionList *model_calculate_f(ReflectionList *given_template, AtomicModel *given_model, signed int layer) { + + ReflectionList *model_reflections; + unsigned int i; + AtomicModel *model; + ReflectionList *template; + + model_reflections = reflist_new(); + + if ( !given_template ) { + template = reflist_new(); + model_generate_template(template, layer); + } else { + template = given_template; + } + + if ( !given_model ) { + model = model_get_current(); + } else { + model = given_model; + } + + if ( model->thickness > 0.0001 ) { + + /* Dynamical diffraction amplitudes */ + model_reflections = multislice_calculate_f_dyn(model, template); + + } else { + + /* Kinematical structure factors */ + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + signed int h = template->refs[i].h; + signed int k = template->refs[i].k; + signed int l = template->refs[i].l; + double sf_re = 0; + double sf_im = 0; + unsigned int j; + + /* Work out the total contribution */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].active ) { + sf_re += model_f_contribution_re(model, j, h, k, l); + sf_im += model_f_contribution_im(model, j, h, k, l); + } + } + + //printf("%3i %3i %3i am=%f ph=%f re=%f im=%f\n", h, k, l, sqrt((sf_re*sf_re) + (sf_im*sf_im)), atan2(sf_im, sf_re), sf_re, sf_im); + reflist_addref_am_ph(model_reflections, h, k, l, sqrt((sf_re*sf_re) + (sf_im*sf_im)), atan2(sf_im, sf_re)); + reflist_set_components(model_reflections, h, k, l, sf_re, sf_im); + + } + + } + + if ( given_template == NULL ) free(template); + + return model_reflections; + +} + +#if 0 +/* Apply a smoothing filter to the amplitudes */ +static void model_smooth(ReflectionList *reflections) { + + unsigned int i; + float a = data_a(); + float b = data_b(); + float c = data_c(); + double sigma = 5.19; + + for ( i=1; in_reflections; i++ ) { + + double s; + signed int h, k, l; + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + s = sqrt(((h*h)/(a*a)) + ((k*k)/(b*b)) + ((l*l)/(c*c))); + + reflections->refs[i].amplitude *= exp(-(s*s)/(2*sigma*sigma)); + + } + + +} +#endif + +/* For the difference synthesis */ +void model_calculate_difference_coefficients(ReflectionList *reflections) { + + unsigned int i; + double scale; + ReflectionList *model_reflections; + + if ( reflections->n_reflections == 0 ) return; /* No reflections */ + model_reflections = model_calculate_f(reflections, NULL, 69); + if ( !model_reflections ) return; + + scale = stat_scale(reflections, model_reflections); + //printf("Scale for difference coefficients = %f\n", scale); + + for ( i=0; in_reflections; i++ ) { /* 'hkl' loop */ + + if ( model_reflections->refs[i].amplitude > 0.0000001 ) { + reflections->refs[i].amplitude = reflections->refs[i].amplitude - (scale * model_reflections->refs[i].amplitude); + reflections->refs[i].phase_known = model_reflections->refs[i].phase_known; + reflections->refs[i].phase_known_set = 1; + } else { + reflections->refs[i].phase_known_set = 0; + } + + } + + //model_smooth(reflections); + + free(model_reflections); + +} + +/* For the Fourier refinement synthesis */ +void model_calculate_refinement_coefficients(ReflectionList *reflections) { + + unsigned int i; + ReflectionList *model_reflections; + + if ( reflections->n_reflections == 0 ) return; /* No reflections */ + model_reflections = model_calculate_f(reflections, NULL, 69); + if ( !model_reflections ) return; + + for ( i=0; in_reflections; i++ ) { /* 'hkl' loop */ + + reflections->refs[i].amplitude = reflections->refs[i].amplitude; + reflections->refs[i].phase_known = model_reflections->refs[i].phase_known; + + } + + //model_smooth(reflections); + + free(model_reflections); + +} + +/* ---------------------------------------------------------------- Misc -------------------------------------------------------------- */ + +void model_open_editor() { + model_current->editor = model_editor_open(model_current); +} + +AtomicModel *model_get_current() { + return model_current; +} + +/* Notify that a model has been changed. + * Special version for when a ModelEditor initiates the update + * (don't try and update the editor in this case!) */ +void model_notify_update_editor(AtomicModel *model) { + + if ( model == model_current ) { + displaywindow_switchview(); + } + +} + +/* Notify that a model has been changed */ +void model_notify_update(AtomicModel *model) { + + model_notify_update_editor(model); + + if ( model->editor ) { + model_editor_get_model(model->editor); + } + +} + +/* Initial setting of the current model. Many functions assume that + * a model structure exists. */ +void model_default() { + model_current = model_new(); +} + +void model_lock(AtomicModel *model) { + model->lock_count++; + if ( model->editor ) { + model_editor_lock(model->editor); + } +} + +void model_unlock(AtomicModel *model) { + model->lock_count--; + if ( model->lock_count == 0 ) { + if ( model->editor ) { + model_editor_unlock(model->editor); + } + } +} + +int model_current_is_blank() { + if ( model_get_current()->n_atoms) { + return 0; + } + return 1; +} diff --git a/src/model.h b/src/model.h new file mode 100644 index 0000000..9c78683 --- /dev/null +++ b/src/model.h @@ -0,0 +1,79 @@ +/* + * model.h + * + * Atomic Model Structures + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifndef MODEL_H +#define MODEL_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "symmetry.h" +#include "reflist.h" +#include "model-editor.h" +#include "refine.h" + +#define MAX_ATOMS 255 + +typedef struct { + + unsigned int ref; /* Atom type reference */ + double x; + double y; + double z; + double B; /* Debye-Waller thermal parameter */ + double occ; /* Site occupancy */ + + unsigned int refine; /* Refine this atom? */ + unsigned int active; /* Include this atom in calculations? */ + +} ModelAtom; + +typedef struct struct_atomicmodel { + + Symmetry sym; + double thickness; + int point_atoms; + + ModelEditor *editor; /* Editor for this model, if open */ + RefinementWindow *refine_window; /* Refinement window for this model, if open */ + int lock_count; + + unsigned int n_atoms; + ModelAtom atoms[MAX_ATOMS]; + +} AtomicModel; + +extern AtomicModel *model_new(void); +extern AtomicModel *model_copy(const AtomicModel *a); +extern void model_move(AtomicModel *a, AtomicModel *b); +extern void model_free(AtomicModel *model); + +extern AtomicModel *model_load(const char *filename); +extern void model_load_as_current(const char *filename); +extern void model_save(const char *filename, AtomicModel *model); + +extern ReflectionList *model_calculate_f(ReflectionList *template, AtomicModel *given_model, signed int layer); +extern void model_calculate_difference_coefficients(ReflectionList *reflections); +extern void model_calculate_refinement_coefficients(ReflectionList *reflections); +extern double model_mod_f(AtomicModel *model, signed int h, signed int k, signed int l); + +extern AtomicModel *model_get_current(); +extern void model_open_editor(void); +extern void model_notify_update(AtomicModel *model); +extern void model_notify_update_editor(AtomicModel *model); +extern void model_default(void); +extern void model_lock(AtomicModel *model); +extern void model_unlock(AtomicModel *model); +extern int model_current_is_blank(void); + +#endif /* MODEL_H */ + diff --git a/src/multislice.c b/src/multislice.c new file mode 100644 index 0000000..b314098 --- /dev/null +++ b/src/multislice.c @@ -0,0 +1,126 @@ +/* + * multislice.c + * + * Multislice Dynamical Simulations + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "reflist.h" +#include "model.h" +#include "data.h" + +/* Relativistic Electron Interaction Constant. Voltage in volts. */ +static double multislice_interaction(double voltage) { + + double a, b; + + a = (1 + voltage*1.9569341e-6) * 0.25615739; + b = sqrt(voltage) * sqrt(1 + voltage*0.97846707e-6); + + return a / b; + +} + +/* Unit cell volume */ +static double multislice_volume(double a, double b, double c, double alpha, double beta, double gamma) { + + + +} + +static fftw_complex *multislice_phasegrating(AtomicModel *model_orig, size_t width, size_t height) { + + AtomicModel *model; + fftw_complex *phase_grating_real; + fftw_complex *phase_grating_reciprocal; + fftw_plan plan; + ReflectionList *V; + ReflectionList *template; + + template = reflist_new(); + + model = model_copy(model_orig); + model->thickness = 0.0; + V = model_calculate_f(template, model, 69); + model_free(model); + reflist_free(template); + + plan = fftw_plan_dft_2d(width, height, phase_grating_real, phase_grating_reciprocal, FFTW_FORWARD, FFTW_MEASURE); + fftw_execute(plan); + fftw_destroy_plan(plan); + + return phase_grating_reciprocal; + +} + +static fftw_complex *multislice_propogator(AtomicModel *model_orig, size_t width, size_t height, double slice) { + + + + return NULL; + +} + +static void multislice_multiply(fftw_complex *a, fftw_complex *b, size_t size) { + size_t i; + for ( i=0; ithickness; t+=c ) { + multislice_multiply(wavefunction, propogator, width*height); + multislice_convolve(wavefunction, phasegrating, width*height); + printf("MS: Current thickness: %f\n", t); fflush(stdout); + } + + return NULL; + +} + diff --git a/src/multislice.h b/src/multislice.h new file mode 100644 index 0000000..4aa6014 --- /dev/null +++ b/src/multislice.h @@ -0,0 +1,25 @@ +/* + * multislice.h + * + * Multislice Dynamical Simulations + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef MULTISLICE_H +#define MULTISLICE_H + +#include "model.h" +#include "reflist.h" + +extern ReflectionList *multislice_calculate_f_dyn(AtomicModel *model, + ReflectionList *template); + +#endif /* MULTISLICE_H */ diff --git a/src/normalise.c b/src/normalise.c new file mode 100644 index 0000000..a08ff97 --- /dev/null +++ b/src/normalise.c @@ -0,0 +1,721 @@ +/* + * normalise.c + * + * Normalisation stuff + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "main.h" +#include "data.h" +#include "elements.h" + +static int normalise_window_open = 0; +GtkWidget *normalise_tree; +GtkListStore *normalise_list_store; +enum { + NORMALISE_ATOMS_COLUMN_NUMBER, + NORMALISE_ATOMS_COLUMN_Z, + NORMALISE_ATOMS_COLUMN_ELEMENT, + NORMALISE_ATOMS_COLUMN_REF, + NORMALISE_ATOMS_COLUMNS, +}; + +ReflectionList *normalise_undo_copy = NULL; + +static void normalise_undo() { + printf("NM: Undoing normalisation.\n"); + if ( normalise_undo_copy ) { + main_substitutereflections(normalise_undo_copy); + free(normalise_undo_copy); + normalise_undo_copy = NULL; + } else { + error_report("Nothing to undo..."); + return; + } +} + +/* Returns sin(theta)/lambda = 1/(2d) */ +double resolution(signed int h, signed int k, signed int l, + double a, double b, double c, double gamma) +{ + static int complained = 0; + double one_over_dsq; + + if ( (l != 0) && !complained ) { + printf("Warning: you asked for the resolution of a HOLZ " + "reflection. The value will be wrong unless alpha " + "and beta are 90 degrees.\n"); + complained = 1; + } + + /* This is just the formula for a monoclinic structure with + * b and c swapped round. */ + one_over_dsq = (h*h)/(a*a*sin(gamma)*sin(gamma)); + one_over_dsq += (k*k)/(b*b*sin(gamma)*sin(gamma)); + one_over_dsq += (l*l)/(c*c); + one_over_dsq -= (2*h*k*cos(gamma))/(a*b*sin(gamma)*sin(gamma)); + return 0.5 * sqrt(one_over_dsq); +} + +void normalise_execute(ReflectionList *reflections, double level) { + + GtkTreeIter iter; + unsigned int ref; + gboolean ival; + unsigned int i; + + printf("NM: Normalising (%f)\n", level); + + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(normalise_list_store), &iter); + if ( !ival ) { + error_report("You must define some atoms for the unit cell!"); + return; + } + + if ( normalise_undo_copy ) { + free(normalise_undo_copy); + } + normalise_undo_copy = malloc(sizeof(ReflectionList)); + memcpy(normalise_undo_copy, reflections, sizeof(ReflectionList)); + + for ( i=0; in_reflections; i++ ) { + + double am = reflections->refs[i].amplitude; + + /* Only attempt to normalise reflections which actually exist */ + if ( am > 0 ) { + + double sfac = 0; + double s; + double a = data_a() * 10; + double b = data_b() * 10; + double c = data_c() * 10; /* Change to Angstroms */ + double gamma = data_gamma(); + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + + /* Determine the value of 's' = sin(theta)/lambda = 1/2d */ + s = resolution(h, k, l, a, b, c, gamma); + + /* Step through the list of elements, adding up the contribution to this particular reflection from each */ + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(normalise_list_store), &iter); + while ( ival ) { + gtk_tree_model_get(GTK_TREE_MODEL(normalise_list_store), &iter, NORMALISE_ATOMS_COLUMN_REF, &ref, -1); + ival = gtk_tree_model_iter_next(GTK_TREE_MODEL(normalise_list_store), &iter); + sfac = elements[ref].sfac_c; + sfac += elements[ref].sfac_a1 * exp(-elements[ref].sfac_b1 * s * s); + sfac += elements[ref].sfac_a2 * exp(-elements[ref].sfac_b2 * s * s); + sfac += elements[ref].sfac_a3 * exp(-elements[ref].sfac_b3 * s * s); + sfac += elements[ref].sfac_a4 * exp(-elements[ref].sfac_b4 * s * s); + } + sfac = sfac*sfac; + + am = pow((am/sfac), level) * pow(am, 1-level); + + reflections->refs[i].amplitude = am; + + } + + } + + displaywindow_switchview(); + displaywindow_statusbar("Amplitudes normalised"); + +} + +static gint normalise_window_close(GtkWidget *widget, gint response, GtkWidget *normalise_level) { + + if ( response == GTK_RESPONSE_APPLY ) { + main_normalise(gtk_range_get_value(GTK_RANGE(normalise_level))); + } + + if ( response == GTK_RESPONSE_REJECT ) { + normalise_undo(); + } + + if ( (response == GTK_RESPONSE_CLOSE) || (response == GTK_RESPONSE_NONE) || (response == GTK_RESPONSE_DELETE_EVENT) ) { + normalise_window_open = 0; + gtk_widget_destroy(widget); + } + + return 0; +} + +static void normalise_atoms_number(GtkListStore *normalise_list_store) { + + GtkTreeIter iter; + gboolean ival; + unsigned int i = 1; + + ival = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(normalise_list_store), &iter); + + while ( ival ) { + gtk_list_store_set(GTK_LIST_STORE(normalise_list_store), &iter, NORMALISE_ATOMS_COLUMN_NUMBER, i, -1); + ival = gtk_tree_model_iter_next(GTK_TREE_MODEL(normalise_list_store), &iter); + i++; + } + +} + +static void normalise_delete_atom(GtkWidget *widget, GtkWidget *tree_view) { + + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(tree_view), &path, &column); + gtk_tree_model_get_iter(GTK_TREE_MODEL(normalise_list_store), &iter, path); + gtk_list_store_remove(GTK_LIST_STORE(normalise_list_store), &iter); + normalise_atoms_number(normalise_list_store); + +} + +static void normalise_add_atom(GtkWidget *widget, GtkWidget *atom_define) { + + GtkTreeIter iter; + unsigned int number; + + number = gtk_combo_box_get_active(GTK_COMBO_BOX(atom_define)); + gtk_list_store_append(normalise_list_store, &iter); + printf("Adding element #%i, Z=%i, name=%s\n", number, elements[number].z, elements[number].element_name); + gtk_list_store_set(normalise_list_store, &iter, NORMALISE_ATOMS_COLUMN_Z, elements[number].z, + NORMALISE_ATOMS_COLUMN_ELEMENT, elements[number].element_name, + NORMALISE_ATOMS_COLUMN_REF, number, -1); + normalise_atoms_number(normalise_list_store); + +} + +static void normalise_selection_changed(GtkTreeSelection *selection, GtkWidget *delete_button) { + + gtk_widget_set_sensitive(delete_button, gtk_tree_selection_get_selected(selection, NULL, NULL)); + +} + +static void normalise_dialog_do_list(GtkWidget *vbox) { + + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + GtkWidget *scrolledwindow; + GtkWidget *add_button; + GtkWidget *delete_button; + GtkWidget *button_box; + GtkWidget *atom_define; + + unsigned int i = 0; + + normalise_list_store = gtk_list_store_new(NORMALISE_ATOMS_COLUMNS, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT); + + normalise_tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(normalise_list_store)); + g_object_unref(G_OBJECT(normalise_list_store)); + + renderer = gtk_cell_renderer_text_new(); + + column = gtk_tree_view_column_new_with_attributes("#", renderer, "text", NORMALISE_ATOMS_COLUMN_NUMBER, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(normalise_tree), column); + + column = gtk_tree_view_column_new_with_attributes("Z", renderer, "text", NORMALISE_ATOMS_COLUMN_Z, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(normalise_tree), column); + + column = gtk_tree_view_column_new_with_attributes("Element", renderer, "text", NORMALISE_ATOMS_COLUMN_ELEMENT, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(normalise_tree), column); + + scrolledwindow = gtk_scrolled_window_new(gtk_tree_view_get_hadjustment(GTK_TREE_VIEW(normalise_tree)), + gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(normalise_tree))); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolledwindow), GTK_WIDGET(normalise_tree)); + + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scrolledwindow), TRUE, TRUE, 5); + + button_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), button_box, FALSE, FALSE, 5); + + add_button = gtk_button_new_from_stock(GTK_STOCK_ADD); + gtk_box_pack_end(GTK_BOX(button_box), add_button, FALSE, FALSE, 5); + + atom_define = gtk_combo_box_new_text(); + elements_initialise(); + while ( elements[i].z > 0 ) { + gtk_combo_box_append_text(GTK_COMBO_BOX(atom_define), elements[i].element_name); + i++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(atom_define), 0); + gtk_box_pack_end(GTK_BOX(button_box), GTK_WIDGET(atom_define), FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(add_button), "clicked", G_CALLBACK(normalise_add_atom), atom_define); + + delete_button = gtk_button_new_from_stock(GTK_STOCK_DELETE); + gtk_box_pack_end(GTK_BOX(button_box), delete_button, FALSE, FALSE, 5); + g_signal_connect(G_OBJECT(delete_button), "clicked", G_CALLBACK(normalise_delete_atom), normalise_tree); + + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(normalise_tree))), "changed", + G_CALLBACK(normalise_selection_changed), delete_button); + gtk_widget_set_sensitive(delete_button, + gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(normalise_tree)), NULL, NULL)); + +} + +void normalise_dialog_open() { + + GtkWidget *normalise_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *normalise_level; + GtkWidget *normalise_level_hbox; + + if ( normalise_window_open ) { + return; + } + normalise_window_open = 1; + + normalise_window = gtk_dialog_new_with_buttons("Sharpen / Normalise", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_UNDO, GTK_RESPONSE_REJECT, + GTK_STOCK_EXECUTE, GTK_RESPONSE_APPLY, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_default_size(GTK_WINDOW(normalise_window), -1, 300); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(normalise_window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + normalise_dialog_do_list(vbox); + + normalise_level_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(normalise_level_hbox), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(normalise_level_hbox), GTK_WIDGET(gtk_label_new("No sharpening")), FALSE, FALSE, 5); + normalise_level = gtk_hscale_new_with_range(0, 1, 0.1); + gtk_box_pack_start(GTK_BOX(normalise_level_hbox), GTK_WIDGET(normalise_level), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(normalise_level), 1); + gtk_scale_set_draw_value(GTK_SCALE(normalise_level), TRUE); + gtk_box_pack_start(GTK_BOX(normalise_level_hbox), GTK_WIDGET(gtk_label_new("Normalisation")), FALSE, FALSE, 5); + + g_signal_connect(G_OBJECT(normalise_window), "response", G_CALLBACK(normalise_window_close), normalise_level); + + gtk_widget_show_all(normalise_window); + +} + +static void normalise_gpwrite(FILE *gnuplot, const char *string) { + fwrite(string, strlen(string), 1, gnuplot); +} + +void normalise_wilsonplot(ReflectionList *reflections) { + + FILE *fh; + double a = data_a(); + double b = data_b(); + double c = data_c(); + double gamma = data_gamma(); + unsigned int i; + FILE *gnuplot; + double sigma = 0; + unsigned int n = 0; + + for ( i=1; in_reflections; i++ ) { + if ( reflections->refs[i].amplitude > 0 ) { + sigma += reflections->refs[i].amplitude * reflections->refs[i].amplitude; + n++; + } + } + sigma = sigma / n; + + fh = fopen("synth2d-dethermalise.dat", "w"); + for ( i=1; in_reflections; i++ ) { + + double am_sq = reflections->refs[i].amplitude * reflections->refs[i].amplitude; + + /* Only include reflections which actually exist */ + if ( am_sq > 0 ) { + + double s2; + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + s2 = pow(resolution(h, k, l, a, b, c, gamma), 2); + fprintf(fh, "%f %f\n", s2, log(am_sq)); /* "log" is actually "ln" */ + + } + } + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + normalise_gpwrite(gnuplot, "set autoscale\n"); + normalise_gpwrite(gnuplot, "unset log\n"); + normalise_gpwrite(gnuplot, "unset label\n"); + normalise_gpwrite(gnuplot, "set xtic auto\n"); + normalise_gpwrite(gnuplot, "set ytic auto\n"); + normalise_gpwrite(gnuplot, "set grid\n"); + normalise_gpwrite(gnuplot, "set grid\n"); + normalise_gpwrite(gnuplot, "set bmargin 7cm\n"); + normalise_gpwrite(gnuplot, "set ylabel 'ln(I)' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set xlabel 's^2 / nm^(-2)' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set title 'Wilson Plot' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "plot 'synth2d-dethermalise.dat'\n"); + normalise_gpwrite(gnuplot, "K=7; B=0.05\n"); + normalise_gpwrite(gnuplot, "fit log(K)-2*B*x 'synth2d-dethermalise.dat' via K,B\n"); + normalise_gpwrite(gnuplot, "set label 'ln(K) = %3.5g',log(K),', B = %3.5g nm^2',B at screen 0.1, screen 0.1 font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "replot log(K)-2*B*x lw 2 lc 3\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} + +GtkWidget *normalise_k_box; +GtkWidget *normalise_b_box; +ReflectionList *normalise_dt_undo_copy = NULL; + +static void normalise_dt_undo() { + printf("NM: Undoing dethermalisationn.\n"); + if ( normalise_dt_undo_copy ) { + main_substitutereflections(normalise_dt_undo_copy); + free(normalise_dt_undo_copy); + normalise_dt_undo_copy = NULL; + } else { + error_report("Nothing to undo..."); + return; + } +} + +void normalise_dethermalise(ReflectionList *reflections, double level) +{ + unsigned int i; + double kf; + double bf; + const char *ks; + const char *bs; + double a = data_a(); + double b = data_b(); + double c = data_c(); + double gamma = data_gamma(); + + printf("NM: Wilson Normalising...\n"); + + if ( normalise_dt_undo_copy ) { + free(normalise_dt_undo_copy); + } + normalise_dt_undo_copy = malloc(sizeof(ReflectionList)); + memcpy(normalise_dt_undo_copy, reflections, sizeof(ReflectionList)); + + ks = gtk_entry_get_text(GTK_ENTRY(normalise_k_box)); + bs = gtk_entry_get_text(GTK_ENTRY(normalise_b_box)); + if ( sscanf(ks, "%lf", &kf) != 1 ) { + error_report("Invalid value for 'ln(K)'"); + return; + } + if ( sscanf(bs, "%lf", &bf) != 1 ) { + error_report("Invalid value for 'B'"); + return; + } + + printf("NM: ln(K)=%f, B=%f => K=%f\n", kf, bf, exp(kf)); + kf = exp(kf); + + for ( i=0; in_reflections; i++ ) { + + double am_sq = pow(reflections->refs[i].amplitude, 2.0); + + /* Only include reflections which actually exist */ + if ( am_sq > 0.0 ) { + + double am_e; + double am = sqrt(am_sq); + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + double s2 = pow(resolution(h, k, l, a, b, c, gamma), 2.0); + + am_e = sqrt(am_sq/(kf*exp(-2.0*bf*s2))); + + am = pow((am_e), level) * pow(am, 1.0-level); + reflections->refs[i].amplitude = am; + + } + + } + + displaywindow_switchview(); + displaywindow_statusbar("Amplitudes normalised"); +} + +static unsigned int normalise_dtw_open = 0; + +static gint normalise_dt_window_close(GtkWidget *widget, gint response, + GtkWidget *normalise_level) +{ + + if ( response == GTK_RESPONSE_APPLY ) { + main_dethermalise(gtk_range_get_value(GTK_RANGE(normalise_level))); + } + + if ( response == GTK_RESPONSE_REJECT ) { + normalise_dt_undo(); + } + + if ( (response == GTK_RESPONSE_CLOSE) || (response == GTK_RESPONSE_NONE) + || (response == GTK_RESPONSE_DELETE_EVENT) ) { + normalise_dtw_open = 0; + gtk_widget_destroy(widget); + } + + return 0; +} + +void normalise_dethermalise_open() { + + GtkWidget *normalise_dt_window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *k_label; + GtkWidget *b_label; + GtkWidget *b_units; + GtkWidget *normalise_dt_level; + GtkWidget *normalise_dt_level_hbox; + + if ( normalise_dtw_open ) { + return; + } + normalise_dtw_open = 1; + + normalise_dt_window = gtk_dialog_new_with_buttons("Normalise", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_UNDO, GTK_RESPONSE_REJECT, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, GTK_STOCK_EXECUTE, GTK_RESPONSE_APPLY, NULL); + gtk_window_set_default_size(GTK_WINDOW(normalise_dt_window), -1, 100); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(normalise_dt_window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + table = gtk_table_new(3, 2, FALSE); + k_label = gtk_label_new("ln(K) = "); + gtk_table_attach_defaults(GTK_TABLE(table), k_label, 1, 2, 1, 2); + gtk_misc_set_alignment(GTK_MISC(k_label), 1, 0.5); + b_label = gtk_label_new("B = "); + gtk_table_attach_defaults(GTK_TABLE(table), b_label, 1, 2, 2, 3); + gtk_misc_set_alignment(GTK_MISC(b_label), 1, 0.5); + normalise_k_box = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), normalise_k_box, 2, 3, 1, 2); + normalise_b_box = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), normalise_b_box, 2, 3, 2, 3); + b_units = gtk_label_new(" nm^-2"); + gtk_table_attach_defaults(GTK_TABLE(table), b_units, 3, 4, 2, 3); + gtk_misc_set_alignment(GTK_MISC(b_units), 0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 5); + + normalise_dt_level_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(normalise_dt_level_hbox), FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(normalise_dt_level_hbox), GTK_WIDGET(gtk_label_new("No sharpening")), FALSE, FALSE, 5); + normalise_dt_level = gtk_hscale_new_with_range(0, 1, 0.1); + gtk_box_pack_start(GTK_BOX(normalise_dt_level_hbox), GTK_WIDGET(normalise_dt_level), TRUE, TRUE, 5); + gtk_range_set_value(GTK_RANGE(normalise_dt_level), 1); + gtk_scale_set_draw_value(GTK_SCALE(normalise_dt_level), TRUE); + gtk_box_pack_start(GTK_BOX(normalise_dt_level_hbox), GTK_WIDGET(gtk_label_new("Normalisation")), FALSE, FALSE, 5); + + g_signal_connect(G_OBJECT(normalise_dt_window), "response", G_CALLBACK(normalise_dt_window_close), normalise_dt_level); + + gtk_widget_show_all(normalise_dt_window); + +} + +void normalise_falloffplot(ReflectionList *reflections) { + + FILE *fh; + double a = data_a(); + double b = data_b(); + double c = data_c(); + double gamma = data_gamma(); + unsigned int i; + FILE *gnuplot; + + fh = fopen("synth2d-falloff.dat", "w"); + for ( i=1; in_reflections; i++ ) { + + double am = reflections->refs[i].amplitude; + + /* Only include reflections which actually exist */ + if ( am > 0 ) { + + signed int h = reflections->refs[i].h; + signed int k = reflections->refs[i].k; + signed int l = reflections->refs[i].l; + double s = resolution(h, k, l, a, b, c, gamma); + + fprintf(fh, "%f %f\n", s, log(am)); /* "log" is actually "ln" */ + + } + } + fclose(fh); + + gnuplot = popen("gnuplot -persist -", "w"); + if ( !gnuplot ) { + error_report("Couldn't invoke gnuplot. Please check your PATH."); + return; + } + + normalise_gpwrite(gnuplot, "set autoscale\n"); + normalise_gpwrite(gnuplot, "unset log\n"); + normalise_gpwrite(gnuplot, "unset label\n"); + normalise_gpwrite(gnuplot, "set xtic auto\n"); + normalise_gpwrite(gnuplot, "set ytic auto\n"); + normalise_gpwrite(gnuplot, "unset grid\n"); + normalise_gpwrite(gnuplot, "set ylabel 'ln F' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set xlabel 's / nm^(-1)' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "set title 'Resolution Falloff Plot' font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "plot 'synth2d-falloff.dat'\n"); + normalise_gpwrite(gnuplot, "a=-14; b=10; c=0.01\n"); + normalise_gpwrite(gnuplot, "fit a+b*exp(-c*x) 'synth2d-falloff.dat' via a,b,c\n"); + normalise_gpwrite(gnuplot, "set label 'a = %3.5g',a,', b = %3.5g',b, ', c = %3.5g',c at graph 0.4, graph 0.7 font \"Helvetica,14\"\n"); + normalise_gpwrite(gnuplot, "replot a+b*exp(-c*x) lc 3 lw 2\n"); + + if ( pclose(gnuplot) == -1 ) { + error_report("gnuplot returned an error code."); + return; + } + +} + +typedef struct { + GtkWidget *a; + GtkWidget *b; + GtkWidget *c; +} NormaliseExpDialog; + +void normalise_exponential(ReflectionList *reflections, double a, double b, double c) { + + size_t i; + double al = data_a(); + double bl = data_b(); + double cl = data_c(); + double gamma = data_gamma(); + + printf("NM: Exponential normalisation, a=%f, b=%f, c=%f\n", a, b, c); + + for ( i=1; in_reflections; i++ ) { + + double am; + + am = reflections->refs[i].amplitude; + + if ( am > 0.0 ) { + + const signed int h = reflections->refs[i].h; + const signed int k = reflections->refs[i].k; + const signed int l = reflections->refs[i].l; + const double s = resolution(h, k, l, al, bl, cl, gamma); + const double n = a + b*exp(-c*s); + + reflections->refs[i].amplitude = am / exp(n); + + } + + } + + displaywindow_switchview(); + displaywindow_statusbar("Amplitudes normalised"); + + +} + +static gint normalise_exponential_response(GtkWidget *window, gint response, NormaliseExpDialog *dialog) { + + if ( response == GTK_RESPONSE_OK ) { + + const char *as; + const char *bs; + const char *cs; + double a, b, c; + + as = gtk_entry_get_text(GTK_ENTRY(dialog->a)); + bs = gtk_entry_get_text(GTK_ENTRY(dialog->b)); + cs = gtk_entry_get_text(GTK_ENTRY(dialog->c)); + + if ( sscanf(as, "%lf", &a) != 1 ) { + error_report("Invalid value for 'a'"); + return 0; + } + if ( sscanf(bs, "%lf", &b) != 1 ) { + error_report("Invalid value for 'b'"); + return 0; + } + if ( sscanf(cs, "%lf", &c) != 1 ) { + error_report("Invalid value for 'c'"); + return 0; + } + + main_normalise_exponential(a, b, c); + + } + + gtk_widget_destroy(window); + + return 0; +} + +void normalise_exponential_open() { + + NormaliseExpDialog *dialog; + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *label; + + dialog = malloc(sizeof(NormaliseExpDialog)); + + window = gtk_dialog_new_with_buttons("Exponential Normalisation", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), GTK_WIDGET(hbox), TRUE, TRUE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), TRUE, TRUE, 5); + + table = gtk_table_new(2, 3, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), TRUE, TRUE, 5); + + label = gtk_label_new("a = "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new("b = "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + label = gtk_label_new("c = "); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + + dialog->a = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), dialog->a, 2, 3, 1, 2); + dialog->b = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), dialog->b, 2, 3, 2, 3); + dialog->c = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), dialog->c, 2, 3, 3, 4); + + g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(normalise_exponential_response), dialog); + + gtk_widget_show_all(window); + +} diff --git a/src/normalise.h b/src/normalise.h new file mode 100644 index 0000000..5cf9839 --- /dev/null +++ b/src/normalise.h @@ -0,0 +1,32 @@ +/* + * normalise.h + * + * Normalisation stuff + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef NORMALISE_H +#define NORMALISE_H + +#include "reflist.h" + +extern void normalise_dialog_open(void); +extern void normalise_wilsonplot(ReflectionList *reflections); +extern void normalise_falloffplot(ReflectionList *reflections); +extern void normalise_dethermalise_open(void); +extern void normalise_exponential_open(void); +extern void normalise_exponential(ReflectionList *reflections, double a, double b, double c); +extern void normalise_dethermalise(ReflectionList *reflections, double level); +extern void normalise_execute(ReflectionList *reflections, double level); +extern double resolution(signed int h, signed int k, signed int l, + double a, double b, double c, double gamma); + +#endif /* NORMALISE_H */ diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..ccaa115 --- /dev/null +++ b/src/options.c @@ -0,0 +1,191 @@ +/* + * options.c + * + * Handle run-time options and stuff + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +static void options_createhomedir(char *filename) { + + /* Create directory. */ + if ( mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR) != 0 ) { + fprintf(stderr, "OP: Couldn't create ~/.synth2d directory.\n"); + exit(1); + } + +} + +static char *options_checkdir() { + + int glob_retval; + glob_t glob_result; + struct stat stat_buffer; + struct stat *statbuf; + char *dir_filename; + + glob_retval = glob("~", GLOB_TILDE, NULL, &glob_result); + statbuf = &stat_buffer; + if ( glob_retval != 0 ) { + + fprintf(stderr, "OP: glob() for ~/.synth2d failed: "); + switch ( glob_retval ) { + case GLOB_NOSPACE : fprintf(stderr, "GLOB_NOSPACE\n"); break; + case GLOB_ABORTED : fprintf(stderr, "GLOB_ABORTED\n"); break; + case GLOB_NOMATCH : fprintf(stderr, "GLOB_NOMATCH\n"); break; + default : fprintf(stderr, "Unknown!\n"); break; + } + return NULL; + + } + + dir_filename = malloc(strlen(glob_result.gl_pathv[0]) + 12); + strcpy(dir_filename, glob_result.gl_pathv[0]); + globfree(&glob_result); + strcat(dir_filename, "/.synth2d"); + + if ( stat(dir_filename, statbuf) != -1 ) { + + if ( S_ISDIR(stat_buffer.st_mode) ) { + + //printf("OP: Found '%s'.\n", dir_filename); + + } else { + + fprintf(stderr, "OP: Found '%s', but it isn't a directory!\n", dir_filename); + return NULL; + + } + + } else { + + printf("OP: ~/.synth2d directory not found: creating it.\n"); + options_createhomedir(dir_filename); + + } + + return dir_filename; + +} + + +void options_load() { + + char *dir_filename; + char *rc_filename; + char *wisdom_filename; + struct stat stat_buffer; + struct stat *statbuf; + + dir_filename = options_checkdir(); + + rc_filename = malloc(strlen(dir_filename) + 8); + strcpy(rc_filename, dir_filename); + strcat(rc_filename, "/config"); + statbuf = &stat_buffer; + if ( stat(rc_filename, statbuf) != -1 ) { + + if ( (S_ISREG(stat_buffer.st_mode)) || (S_ISLNK(stat_buffer.st_mode)) ) { + + + } else { + + fprintf(stderr, "OP: Config file isn't a regular file or a link. Giving up!\n"); + exit(1); + + } + + } else { + printf("OP: Config file doesn't exist. This is OK.\n"); + } + free(rc_filename); + + wisdom_filename = malloc(strlen(dir_filename) + 13); + strcpy(wisdom_filename, dir_filename); + strcat(wisdom_filename, "/fftw-wisdom"); + if ( stat(wisdom_filename, statbuf) != -1 ) { + + if ( (S_ISREG(stat_buffer.st_mode)) || (S_ISLNK(stat_buffer.st_mode)) ) { + + FILE *fh; + + fh = fopen(wisdom_filename, "r"); + if ( fh == NULL ) { + fprintf(stderr, "OP: Error opening FFTW wisdom file.\n"); + } else { + if ( fftw_import_wisdom_from_file(fh) == 1 ) { + //printf("OP: Successfully imported FFTW wisdom\n"); + } else { + fprintf(stderr, "OP: Failed to import FFTW wisdom\n"); + } + } + + fclose(fh); + + } else { + + fprintf(stderr, "OP: FFTW wisdom file isn't a regular file or a link. Giving up!\n"); + exit(1); + + } + + } else { + printf("OP: FFTW wisdom file doesn't exist. This is OK.\n"); + } + free(wisdom_filename); + + free(dir_filename); + +} + +void options_save() { + + char *dir_filename; + char *rc_filename; + char *wisdom_filename; + FILE *fh; + + dir_filename = options_checkdir(); + + rc_filename = malloc(strlen(dir_filename) + 8); + strcpy(rc_filename, dir_filename); + strcat(rc_filename, "/config"); + fh = fopen(rc_filename, "w"); + if ( fh == NULL ) { + fprintf(stderr, "OP: Error opening options file.\n"); + } else { + fprintf(fh, "# synth2d options file\n"); + } + free(rc_filename); + + wisdom_filename = malloc(strlen(dir_filename) + 13); + strcpy(wisdom_filename, dir_filename); + strcat(wisdom_filename, "/fftw-wisdom"); + fh = fopen(wisdom_filename, "w"); + if ( fh == NULL ) { + fprintf(stderr, "OP: Error opening options file.\n"); + } else { + fftw_export_wisdom_to_file(fh); + } + fclose(fh); + free(wisdom_filename); + + free(dir_filename); + +} + diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..27e3d7f --- /dev/null +++ b/src/options.h @@ -0,0 +1,23 @@ +/* + * options.h + * + * Handle run-time options and stuff + * + * (c) 2006-2007 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef OPTIONS_H +#define OPTIONS_H + +extern void options_load(void); +extern void options_save(void); + +#endif /* OPTIONS_H */ + diff --git a/src/png-file.c b/src/png-file.c new file mode 100644 index 0000000..09925a7 --- /dev/null +++ b/src/png-file.c @@ -0,0 +1,125 @@ +/* + * png-file.c + * + * PNG output + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "data.h" +#include "colwheel.h" +#include "renderer.h" + +int png_write_real(const char *filename, fftw_complex *out, double brightness, double gamma_angle, int width, int height, int nx, int ny) { + + FILE *fh; + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointers; + int xn, yn; + int width_n, height_n; + ComplexArray cxar; + + fh = fopen(filename, "wb"); + if (!fh) { + fprintf(stderr, "Couldn't open output file.\n"); + return 1; + } + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if ( !png_ptr ) { + fprintf(stderr, "Couldn't create PNG write structure.\n"); + fclose(fh); + return 1; + } + info_ptr = png_create_info_struct(png_ptr); + if ( !info_ptr ) { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + fprintf(stderr, "Couldn't create PNG info structure.\n"); + fclose(fh); + return 1; + } + if ( setjmp(png_jmpbuf(png_ptr)) ) { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fh); + fprintf(stderr, "PNG write failed.\n"); + return 1; + } + png_init_io(png_ptr, fh); + + width_n = (int)renderer_width(width, height, gamma_angle, nx, ny); + height_n = (int)renderer_height(width, height, gamma_angle, nx, ny); + cxar = renderer_draw(out, width, height, gamma_angle, nx, ny); + + png_set_IHDR(png_ptr, info_ptr, width_n, height_n, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + /* Write the image data */ + row_pointers = malloc(height_n*sizeof(png_bytep *)); + + for ( yn=0; yn 1 ) am = 1; + + row_pointers[yn][3*xn] = (png_byte)255*colwheel_red(am, ph); + row_pointers[yn][3*xn+1] = (png_byte)255*colwheel_green(am, ph); + row_pointers[yn][3*xn+2] = (png_byte)255*colwheel_blue(am, ph); + + } + } + + for ( yn=0; yn + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef PNGFILE_H +#define PNGFILE_H + +#include + +extern int png_write(const char *filename, fftw_complex *out, double norm, int nx, int ny); + +#endif /* PNGFILE_H */ diff --git a/src/refine-brent.c b/src/refine-brent.c new file mode 100644 index 0000000..06a6f95 --- /dev/null +++ b/src/refine-brent.c @@ -0,0 +1,138 @@ +/* + * refine-brent.c + * + * Refinement by Sequential Brent Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +/* For the purposes of GSL */ +static double refine_calc_r(double loc, void *params) { + + double r; + ReflectionList *model_reflections; + RefinementPair *pair = params; + AtomicModel *model; + + model = model_copy(pair->model); + + switch ( pair->cur_mod_coor ) { + case COORDINATE_X : model->atoms[pair->cur_mod_atom].x = loc; break; + case COORDINATE_Y : model->atoms[pair->cur_mod_atom].y = loc; break; + case COORDINATE_Z : model->atoms[pair->cur_mod_atom].z = loc; break; + } + + model_reflections = model_calculate_f(pair->reflections, model, 69); + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r = stat_r2(pair->reflections, model_reflections); /* stat_r(Fobs, Fcalc) */ + } else { + r = stat_r(pair->reflections, model_reflections); /* stat_r(Fobs, Fcalc) */ + } + free(model_reflections); + + model_free(model); + + return r; + +} + +void refine_brent(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_function F; + gsl_min_fminimizer *s; + int status; + RefinementPair pair; + + pair.reflections = reflections; + pair.model = model; + pair.cur_mod_coor = COORDINATE_X; + pair.cur_mod_atom = 1; + + F.function = &refine_calc_r; + F.params = &pair; + + s = gsl_min_fminimizer_alloc(gsl_min_fminimizer_brent); + + while ( model->refine_window->run_semaphore ) { + + double loc_cur; + unsigned int iter = 0; + + /* Select the next thing to refine */ + pair.cur_mod_coor++; + if ( pair.cur_mod_coor > COORDINATE_Z ) { + pair.cur_mod_coor = COORDINATE_X; + pair.cur_mod_atom++; + if ( pair.cur_mod_atom >= pair.model->n_atoms ) pair.cur_mod_atom = 1; + } + // if ( random() < (0.3333*RAND_MAX) ) pair.cur_mod_coor = COORDINATE_Y; + // if ( random() > (0.6666*RAND_MAX) ) pair.cur_mod_coor = COORDINATE_Z; + // atom = 1 + (random()*(double)(pair.model->n_atoms-1))/RAND_MAX; + // pair.cur_mod_atom = atom; + + loc_cur = 0; + switch ( pair.cur_mod_coor ) { + case COORDINATE_X : loc_cur = pair.model->atoms[pair.cur_mod_atom].x; break; + case COORDINATE_Y : loc_cur = pair.model->atoms[pair.cur_mod_atom].y; break; + case COORDINATE_Z : loc_cur = pair.model->atoms[pair.cur_mod_atom].z; break; + } + + gsl_min_fminimizer_set(s, &F, loc_cur, loc_cur-0.1, loc_cur+0.1); + + do { + + double lo, up; + + /* Iterate */ + gsl_min_fminimizer_iterate(s); + iter++; + + /* Get the current estimate */ + loc_cur = gsl_min_fminimizer_x_minimum(s); + lo = gsl_min_fminimizer_x_lower(s); + up = gsl_min_fminimizer_x_upper(s); + + /* Check for convergence */ + status = gsl_min_test_interval(lo, up, 0.001, 0.0); + + } while ( status == GSL_CONTINUE ); + + if (status == GSL_SUCCESS) { + + if ( loc_cur < 0 ) loc_cur = 1+loc_cur; + if ( loc_cur >= 1 ) loc_cur = loc_cur-1; + + switch ( pair.cur_mod_coor ) { + case COORDINATE_X : pair.model->atoms[pair.cur_mod_atom].x = loc_cur; break; + case COORDINATE_Y : pair.model->atoms[pair.cur_mod_atom].y = loc_cur; break; + case COORDINATE_Z : pair.model->atoms[pair.cur_mod_atom].z = loc_cur; break; + } + + refine_schedule_update(model); + + } + + } + + gsl_min_fminimizer_free(s); + +} + diff --git a/src/refine-brent.h b/src/refine-brent.h new file mode 100644 index 0000000..019acd1 --- /dev/null +++ b/src/refine-brent.h @@ -0,0 +1,26 @@ +/* + * refine-brent.h + * + * Refinement by Sequential Brent Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_BRENT_H +#define REFINE_BRENT_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_brent(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_BRENT_H */ + diff --git a/src/refine-cgrad.c b/src/refine-cgrad.c new file mode 100644 index 0000000..5df9ac9 --- /dev/null +++ b/src/refine-cgrad.c @@ -0,0 +1,339 @@ +/* + * refine-cgrad.c + * + * Refinement by Conjugate Gradient Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static double refine_cgrad_f(const gsl_vector *coordinates, void *params) { + + double r; + RefinementPair *pair = params; + AtomicModel *model; + unsigned int idx; + unsigned int j; + ReflectionList *obs; + ReflectionList *calc; + + obs = pair->reflections; + model = model_copy(pair->model); + + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + free(calc); + model_free(model); + + return r; + +} + +static void refine_cgrad_df(const gsl_vector *coordinates, void *params, gsl_vector *df) { + + RefinementPair *pair; + AtomicModel *model; + ReflectionList *calc; + unsigned int j; + AtomicModel *model_original; + unsigned int idx; + ReflectionList *obs; + double r_old; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_old = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r_old = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + reflist_free(calc); + + idx = 0; + model_original = model_copy(model); + + /* Determine gradients */ + for ( j=0; jn_atoms; j++ ) { + + ReflectionList *calc_new; + + if ( !(model->atoms[j].refine) ) continue; + + if ( pair->spec & REFINE_SPEC_X ) { + + double r_new, derivative; + + model->atoms[j].x += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_Y ) { + + double r_new, derivative; + + model->atoms[j].y += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_Z ) { + + double r_new, derivative; + + model->atoms[j].z += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc_new); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_B ) { + + double r_new, derivative; + + model->atoms[j].B += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + if ( pair->spec & REFINE_SPEC_OCC ) { + + double r_new, derivative; + + model->atoms[j].occ += LSQ_MSLS_SHIFT; + + calc_new = model_calculate_f(obs, model, 69); + + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + r_new = stat_r2(obs, calc); /* stat_r(Fobs, Fcalc) */ + } else { + r_new = stat_r(obs, calc); /* stat_r(Fobs, Fcalc) */ + } + derivative = (r_new - r_old)/LSQ_MSLS_SHIFT; + gsl_vector_set(df, idx, derivative); + + idx++; + reflist_free(calc_new); + model_move(model, model_original); + + } + + } + + model_free(model_original); + model_free(model); + +} + +static void refine_cgrad_fdf(const gsl_vector *coordinates, void *params, double *f, gsl_vector *df) { + + *f = refine_cgrad_f(coordinates, params); + refine_cgrad_df(coordinates, params, df); + +} + +void refine_cgrad(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_multimin_fdfminimizer *s; + gsl_multimin_function_fdf f; + unsigned int iter = 0; + gsl_vector *coordinates; + unsigned int j; + RefinementPair pair; + int iter_status, conv_status; + unsigned int n_params, n_atoms, idx; + + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Prevent origin from floating */ + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) { + model->atoms[0].refine = 0; + } + + n_atoms = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + f.n = n_params * n_atoms; + s = gsl_multimin_fdfminimizer_alloc(gsl_multimin_fdfminimizer_conjugate_fr, f.n); + f.f = &refine_cgrad_f; f.df = &refine_cgrad_df; f.fdf = &refine_cgrad_fdf; + coordinates = gsl_vector_alloc(f.n); + + /* Set initial estimates */ + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(coordinates, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(coordinates, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(coordinates, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(coordinates, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(coordinates, idx++, model->atoms[j].occ); + } + } + + pair.model = model; pair.reflections = reflections; + pair.spec = spec; + f.params = &pair; + + gsl_multimin_fdfminimizer_set(s, &f, coordinates, 1e-4, 1e-6); + + do { + + /* Iterate */ + iter_status = gsl_multimin_fdfminimizer_iterate(s); + printf ("status = %s\n", gsl_strerror(iter_status)); + iter++; + + /* Check for error */ + if ( iter_status ) { + printf("status after iteration = %s\n", gsl_strerror(iter_status)); + break; + } + + /* Test for convergence */ + conv_status = gsl_multimin_test_gradient(s->gradient, 1e-6); + printf("status after convergence test = %s\n", gsl_strerror(conv_status)); + + idx = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + refine_schedule_update(model); + + } while ( (conv_status == GSL_CONTINUE) && (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_multimin_fdfminimizer_free(s); + gsl_vector_free(coordinates); + +} + diff --git a/src/refine-cgrad.h b/src/refine-cgrad.h new file mode 100644 index 0000000..1f28a39 --- /dev/null +++ b/src/refine-cgrad.h @@ -0,0 +1,26 @@ +/* + * refine-cgrad.h + * + * Refinement by Conjugate Gradient Minimisation + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_CGRAD_H +#define REFINE_CGRAD_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_cgrad(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_CGRAD_H */ + diff --git a/src/refine-lmder.c b/src/refine-lmder.c new file mode 100644 index 0000000..ba99eaf --- /dev/null +++ b/src/refine-lmder.c @@ -0,0 +1,439 @@ +/* + * refine-lmder.c + * + * Refinement by GSL's Levenberg-Marquardt LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static int refine_lmder_f(const gsl_vector *coordinates, void *params, gsl_vector *f) { + + RefinementPair *pair; + AtomicModel *model; + ReflectionList *calc; + unsigned int i, j, idx; + double scale; + ReflectionList *obs; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + scale = gsl_vector_get(coordinates, idx++); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + /* Calculate residuals */ + for ( i=1; in_reflections; i++ ) { + double am_calc, residual; + am_calc = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + /* ( k|Icalc| - |Iobs| ) / error(|Iobs|) */ + residual = (scale*am_calc*am_calc - obs->refs[i].amplitude*obs->refs[i].amplitude)/(sqrt(2)*obs->refs[i].am_error); + } else { + residual = (scale*am_calc - obs->refs[i].amplitude)/obs->refs[i].am_error; /* ( k|Fcalc| - |Fobs| ) / error(|Fobs|) */ + } + gsl_vector_set(f, i-1, residual); + // printf("Residual for %3i %3i %3i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, residual); + } + + model_free(model); + free(calc); + + return GSL_SUCCESS; + +} + +static int refine_lmder_df(const gsl_vector *coordinates, void *params, gsl_matrix *J) { + + RefinementPair *pair; + AtomicModel *model_original; + AtomicModel *model; + ReflectionList *obs; + ReflectionList *calc; + unsigned int i, j, idx; + double scale; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + scale = gsl_vector_get(coordinates, idx++); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + idx = 0; + + model_original = model_copy(model); + + /* Gradient of the scale factor */ + for ( i=1; in_reflections; i++ ) { + double am; + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + gsl_matrix_set(J, i-1, idx, am*am); + } else { + gsl_matrix_set(J, i-1, idx, am); + } + } + idx++; + + /* Determine gradients of atomic coordinates and B-factors */ + for ( j=0; jn_atoms; j++ ) { + + if ( !(model->atoms[j].refine) ) continue; + double x, y, z; + ReflectionList *reflections_new; + + x = model->atoms[j].x; + y = model->atoms[j].y; + z = model->atoms[j].z; + + if ( pair->spec & REFINE_SPEC_X ) { + model->atoms[j].x += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt x%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_Y ) { + model->atoms[j].y += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt y%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_Z ) { + model->atoms[j].z += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt z%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_B ) { + model->atoms[j].B += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt B%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_OCC ) { + model->atoms[j].occ += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; in_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt B%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + } + + model_free(model_original); + model_free(model); + free(calc); + + return GSL_SUCCESS; + +} + +static int refine_lmder_fdf(const gsl_vector *coordinates, void *params, gsl_vector *f, gsl_matrix *J) { + + refine_lmder_f(coordinates, params, f); + refine_lmder_df(coordinates, params, J); + + return GSL_SUCCESS; + +} + +void refine_lmder(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_multifit_fdfsolver *s; + gsl_multifit_function_fdf f; + unsigned int iter = 0; + gsl_vector *coordinates; + unsigned int j; + RefinementPair pair; + int iter_status, conv_status; + gsl_matrix *covar; + unsigned int n_params, n_atoms, idx; + double scale; + ReflectionList *calc; + + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Prevent origin from floating */ + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) { + model->atoms[0].refine = 0; + } + + n_atoms = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + f.n = reflections->n_reflections-1; f.p = 1 + n_params * n_atoms; + s = gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder, f.n, f.p); + f.f = &refine_lmder_f; f.df = &refine_lmder_df; f.fdf = &refine_lmder_fdf; + printf("Refining %i parameters against %i reflections\n", f.p, f.n); + coordinates = gsl_vector_alloc(f.p); + + /* Set initial estimates */ + idx = 0; + calc = model_calculate_f(reflections, NULL, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + scale = stat_scale_intensity(reflections, calc); + } else { + scale = stat_scale(reflections, calc); + } + reflist_free(calc); + gsl_vector_set(coordinates, idx++, scale); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(coordinates, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(coordinates, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(coordinates, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(coordinates, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(coordinates, idx++, model->atoms[j].occ); + } + } + + pair.model = model; pair.reflections = reflections; pair.spec = spec; + f.params = &pair; + + covar = gsl_matrix_alloc(f.p, f.p); + gsl_multifit_fdfsolver_set(s, &f, coordinates); + + printf("initial: scale=%f, |f(x)|=%g\n", gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f)); + + do { + + ReflectionList *model_reflections; + + /* Iterate */ + printf("before iteration %i: scale=%f, |f(x)|=%g\n", iter, gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f)); + iter_status = gsl_multifit_fdfsolver_iterate(s); + printf("after iteration %i: scale=%f, |f(x)|=%g, status=%s, ", iter, gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f), gsl_strerror(iter_status)); + iter++; + + /* Check for error */ + if ( iter_status ) { + printf("\nError in LSQ refinement"); + break; + } + + /* Test for convergence */ + conv_status = gsl_multifit_test_delta(s->dx, s->x, 1e-6, 1e-6); + //printf("status after convergence test = %s\n", gsl_strerror(conv_status)); + + idx = 0; + scale = gsl_vector_get(s->x, idx++); + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + printf("R2=%.2f%%\n\n", stat_r2(reflections, model_reflections)*100); /* stat_r(Fobs, Fcalc) */ + } else { + printf("R1=%.2f%%\n\n", stat_r(reflections, model_reflections)*100); /* stat_r(Fobs, Fcalc) */ + } + free(model_reflections); + + refine_schedule_update(model); + + } while ( (conv_status == GSL_CONTINUE) && (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_multifit_covar(s->J, 0.0, covar); + gsl_matrix_free(covar); + + gsl_multifit_fdfsolver_free(s); + gsl_vector_free(coordinates); + +} + diff --git a/src/refine-lmder.h b/src/refine-lmder.h new file mode 100644 index 0000000..1e2730d --- /dev/null +++ b/src/refine-lmder.h @@ -0,0 +1,26 @@ +/* + * refine-lmder.h + * + * Refinement by GSL's Levenberg-Marquardt LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_LMDER_H +#define REFINE_LMDER_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_lmder(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_LMDER_H */ + diff --git a/src/refine-lsq.c b/src/refine-lsq.c new file mode 100644 index 0000000..1845212 --- /dev/null +++ b/src/refine-lsq.c @@ -0,0 +1,320 @@ +/* + * refine-lsq.c + * + * Refinement by Simple LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static AtomicModel *refine_model_from_parameters(const gsl_vector *parameters, RefinementSpec spec, AtomicModel *template) { + + unsigned int j, idx; + AtomicModel *model; + + model = model_copy(template); + + idx = 1; /* Ignore scale factor */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(parameters, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(parameters, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(parameters, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(parameters, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(parameters, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + return model; + +} + +static double refine_lsq_gradient(const gsl_vector *parameters, const ReflectionList *obs, RefinementSpec spec, + size_t param_idx, size_t obs_idx, ReflectionList *f_calc, AtomicModel *model) { + + unsigned int j, idx; + double f_calc_new, gradient; + AtomicModel *model_new; + double scale; + + if ( spec & REFINE_SPEC_INTENSITIES ) { + if ( param_idx == 0 ) return f_calc->refs[obs_idx].amplitude*f_calc->refs[obs_idx].amplitude; /* Scale factor */ + } else { + if ( param_idx == 0 ) return f_calc->refs[obs_idx].amplitude; /* Scale factor */ + } + + scale = gsl_vector_get(parameters, 0); + idx = 0; + model_new = model_copy(model); + for ( j=0; jn_atoms; j++ ) { + + if ( !(model->atoms[j].refine) ) continue; + + if ( spec & REFINE_SPEC_X ) idx++; + if ( idx == param_idx ) { + model->atoms[j].x += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_Y ) idx++; + if ( idx == param_idx ) { + model->atoms[j].y += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_Z ) idx++; + if ( idx == param_idx ) { + model->atoms[j].z += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_B ) idx++; + if ( idx == param_idx ) { + model->atoms[j].B += LSQ_MSLS_SHIFT; + break; + } + + if ( spec & REFINE_SPEC_OCC ) idx++; + if ( idx == param_idx ) { + model->atoms[j].occ += LSQ_MSLS_SHIFT; + break; + } + + } + + f_calc_new = model_mod_f(model, f_calc->refs[obs_idx].h, f_calc->refs[obs_idx].k, f_calc->refs[obs_idx].l); + + if ( spec & REFINE_SPEC_INTENSITIES ) { + gradient = ((f_calc->refs[obs_idx].amplitude*f_calc->refs[obs_idx].amplitude) - (f_calc_new*f_calc_new))/LSQ_MSLS_SHIFT; + } else { + gradient = (f_calc->refs[obs_idx].amplitude - f_calc_new)/LSQ_MSLS_SHIFT; + } + model_free(model_new); + + return scale*gradient; + +} + +static gsl_matrix *refine_lsq_B(const gsl_vector *parameters, const ReflectionList *obs, RefinementSpec spec, AtomicModel *model, ReflectionList *f_calc) { + + gsl_matrix *B; + size_t j; + int n_params, n_obs; + + n_params = parameters->size; + n_obs = obs->n_reflections - 1; + + B = gsl_matrix_alloc(n_params, n_params); + for ( j=0; jsize; + n_obs = obs->n_reflections - 1; + scale = gsl_vector_get(parameters, 0); + + D = gsl_matrix_alloc(n_params, 1); + for ( j=0; jrefs[i].amplitude*obs->refs[i].amplitude) - scale*(f_calc->refs[i].amplitude*f_calc->refs[i].amplitude); + } else { + res = obs->refs[i].amplitude - scale*f_calc->refs[i].amplitude; + } + + total += grad*res; + + } + gsl_matrix_set(D, j, 0, total); + + } + + return D; + +} + +/* Determine and apply shift */ +static void refine_lsq_iterate(gsl_vector *parameters, ReflectionList *obs, RefinementSpec spec, AtomicModel *model_template) { + + gsl_matrix *B; + gsl_matrix *D; + gsl_matrix *Binv; + gsl_matrix *shift; + gsl_permutation *perm; + int s, n_params; + size_t i; + AtomicModel *model; + ReflectionList *f_calc; + + n_params = parameters->size; + model = refine_model_from_parameters(parameters, spec, model_template); + f_calc = model_calculate_f(obs, model, 69); + + /* Form 'B' */ + //printf("LSQ: Forming B\n"); + B = refine_lsq_B(parameters, obs, spec, model, f_calc); + + /* Form 'D' */ + //printf("LSQ: Forming D\n"); + D = refine_lsq_D(parameters, obs, spec, model, f_calc); + + /* Invert 'B' */ + //printf("LSQ: Inverting B\n"); + perm = gsl_permutation_alloc(B->size1); + Binv = gsl_matrix_alloc(B->size1, B->size2); + gsl_linalg_LU_decomp(B, perm, &s); + gsl_linalg_LU_invert(B, perm, Binv); + gsl_permutation_free(perm); + gsl_matrix_free(B); + + /* shift = B^-1 D */ + //printf("LSQ: Calculating shift\n"); + shift = gsl_matrix_alloc(n_params, 1); + gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, Binv, D, 0.0, shift); + gsl_matrix_free(Binv); + gsl_matrix_free(D); + + /* Apply shifts */ + //printf("LSQ: Applying shifts\n"); + for ( i=0; i %f\n", i, old, new); + } + + gsl_matrix_free(shift); + model_free(model); + //printf("LSQ: Done\n"); + + +} + +void refine_lsq(AtomicModel *model_orig, ReflectionList *reflections, RefinementSpec spec) { + + gsl_vector *parameters; + unsigned int n_obs, n_params, n_atoms, idx, j, iter; + double scale; + ReflectionList *calc; + AtomicModel *model; + + model = model_copy(model_orig); + + /* Count the number of parameters to refine */ + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Count the number of atoms to refine (having first fixed the origin) */ + n_atoms = 0; + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) model->atoms[0].refine = 0; + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + n_obs = reflections->n_reflections-1; /* Number of observations */ + n_params = 1 + n_params * n_atoms; /* Number of parameters */ + printf("LSQ: Refining %i parameters against %i observations\n", n_params, n_obs); + + /* Set initial estimates of the parameters */ + parameters = gsl_vector_alloc(n_params); + idx = 0; + + /* Initial scale factor */ + calc = model_calculate_f(reflections, NULL, 69); + scale = stat_scale(reflections, calc); + reflist_free(calc); + gsl_vector_set(parameters, idx++, scale); + + /* Initial atomic coordinates and (iso-)B-factors */ + for ( j=0; jn_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(parameters, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(parameters, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(parameters, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(parameters, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(parameters, idx++, model->atoms[j].occ); + } + } + + iter = 0; + do { + + /* Iterate */ + refine_lsq_iterate(parameters, reflections, spec, model); + printf("LSQ: after iteration %i:\tscale=%f\n", iter, gsl_vector_get(parameters, 0)); + iter++; + + /* Put the parameter vector back into the atomic model */ + model_free(model); + model = refine_model_from_parameters(parameters, spec, model); + model_move(model_orig, model); + + refine_schedule_update(model); + + } while ( (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_vector_free(parameters); + +} + diff --git a/src/refine-lsq.h b/src/refine-lsq.h new file mode 100644 index 0000000..ffb03b9 --- /dev/null +++ b/src/refine-lsq.h @@ -0,0 +1,26 @@ +/* + * refine-lsq.h + * + * Refinement by Simple LSQ + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_LSQ_H +#define REFINE_LSQ_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_lsq(AtomicModel *model_orig, ReflectionList *reflections, RefinementSpec spec); + +#endif /* REFINE_LSQ_H */ + diff --git a/src/refine-rns.c b/src/refine-rns.c new file mode 100644 index 0000000..3bfcd21 --- /dev/null +++ b/src/refine-rns.c @@ -0,0 +1,122 @@ +/* + * refine-rns.c + * + * Refinement by Random Neighbour Search + * + * (c) 2006-2009 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +void refine_neighboursearch(AtomicModel *model, ReflectionList *reflections, + RefinementSpec spec, double shift) +{ + unsigned int iterations_without_change; + double min_r; + ReflectionList *model_reflections; + + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + /* stat_r2(Fobs, Fcalc) */ + min_r = stat_r2(reflections, model_reflections); + } else { + /* stat_r(Fobs, Fcalc) */ + min_r = stat_r(reflections, model_reflections); + } + + iterations_without_change = 0; + while ( (model->refine_window->run_semaphore) + && (iterations_without_change < MAX_REFINEMENT_ITERATIONS) ) { + + unsigned int i; + double x, y, z; + double new_r; + AtomicModel *copy; + + /* Copy the structure */ + copy = model_copy(model); + + /* 'Jiggle' the atoms */ + for ( i=1; in_atoms; i++ ) { + + if ( model->atoms[i].refine ) { + + x = model->atoms[i].x; + y = model->atoms[i].y; + z = model->atoms[i].z; + + if ( spec & REFINE_SPEC_X ) { + if ( random() > (0.9*RAND_MAX) ) + x += shift; + if ( random() > (0.9*RAND_MAX) ) + x -= shift; + if ( x<0 ) x = 1+x; + if ( x>1 ) x = x-1; + model->atoms[i].x = x; + } + + if ( spec & REFINE_SPEC_Y ) { + if ( random() > (0.9*RAND_MAX) ) + y += shift; + if ( random() > (0.9*RAND_MAX) ) + y -= shift; + if ( y<0 ) y = 1+y; + if ( y>1 ) y = y-1; + model->atoms[i].y = y; + } + + if ( spec & REFINE_SPEC_Z ) { + if ( random() > (0.9*RAND_MAX) ) + z += shift; + if ( random() > (0.9*RAND_MAX) ) + z -= shift; + if ( z<0 ) z = 1+z; + if ( z>1 ) z = z-1; + model->atoms[i].z = z; + } + + } + + } + + /* Recalculate structure factors and check for improvement */ + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + /* stat_r(Fobs, Fcalc) */ + new_r = stat_r2(reflections, model_reflections); + } else { + /* stat_r(Fobs, Fcalc) */ + new_r = stat_r(reflections, model_reflections); + } + if ( new_r < min_r ) { + + min_r = new_r; + refine_schedule_update(model); + iterations_without_change = 0; + + model_free(copy); + + } else { + + model_move(model, copy); + model_free(copy); + iterations_without_change++; + + } + free(model_reflections); + + } +} diff --git a/src/refine-rns.h b/src/refine-rns.h new file mode 100644 index 0000000..fc06c76 --- /dev/null +++ b/src/refine-rns.h @@ -0,0 +1,26 @@ +/* + * refine-rns.h + * + * Refinement by Random Neighbour Search + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_RNS_H +#define REFINE_RNS_H + +#include "model.h" +#include "reflist.h" +#include "refine.h" + +extern void refine_neighboursearch(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec, double shift); + +#endif /* REFINE_RNS_H */ + diff --git a/src/refine.c b/src/refine.c new file mode 100644 index 0000000..314cdc1 --- /dev/null +++ b/src/refine.c @@ -0,0 +1,359 @@ +/* + * refine.c + * + * Model Refintement + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" +#include "displaywindow.h" +#include "main.h" +#include "refine-rns.h" +#include "refine-lmder.h" +#include "refine-lsq.h" +#include "refine-cgrad.h" +#include "refine-brent.h" + +static gboolean refine_update_model(gpointer data) { + + AtomicModel *model = data; + + model_notify_update(model); + + model->refine_window->display_callback = 0; + g_static_mutex_unlock(&model->refine_window->display_mutex); + + return FALSE; + +} + +void refine_schedule_update(AtomicModel *model) { + g_static_mutex_lock(&model->refine_window->display_mutex); + model->refine_window->display_callback = g_idle_add(refine_update_model, model); +} + +static void *refine_work(void *data) { + + RefinementWindow *refinementwindow; + AtomicModel *model; + ReflectionList *reflections; + RefinementSpec spec; + + refinementwindow = data; + refinementwindow->running = 1; + + model = refinementwindow->model; + reflections = main_reflist(); + spec = refinementwindow->spec; + + switch ( refinementwindow->type ) { + case REFINE_TYPE_NONE : break; + case REFINE_TYPE_NEIGHBOURSEARCH : { + const char *str; + float shiftf; + str = gtk_entry_get_text(GTK_ENTRY(refinementwindow->nbsearch_shift)); + sscanf(str, "%f", &shiftf); + refine_neighboursearch(model, reflections, spec, shiftf); + break; + } + case REFINE_TYPE_BRENT : refine_brent(model, reflections, spec); break; + case REFINE_TYPE_LMDER : refine_lmder(model, reflections, spec); break; + case REFINE_TYPE_LSQ : refine_lsq(model, reflections, spec); break; + case REFINE_TYPE_CGRAD : refine_cgrad(model, reflections, spec); break; + } + + refinementwindow->running = 0; + + return NULL; + +} + +/* Refine the model described by model_atoms_list_store against reflections */ +static void refine_go(RefinementWindow *refinementwindow) { + + AtomicModel *model; + + model = model_get_current(); + + assert(refinementwindow->run_semaphore == 0); + model_lock(model); + refinementwindow->run_semaphore = 1; + g_static_mutex_init(&refinementwindow->display_mutex); + assert(refinementwindow->work_thread == NULL); + refinementwindow->display_callback = 0; + refinementwindow->work_thread = g_thread_create(refine_work, refinementwindow, TRUE, NULL); + +} + +static void refine_stop(RefinementWindow *refinementwindow) { + + assert(refinementwindow->run_semaphore == 1); + assert(refinementwindow->work_thread != NULL); + refinementwindow->run_semaphore = 0; + if ( refinementwindow->display_callback ) { + g_idle_remove_by_data(refinementwindow); + g_static_mutex_unlock(&refinementwindow->display_mutex); + } + g_thread_join(refinementwindow->work_thread); + refinementwindow->work_thread = NULL; + model_unlock(refinementwindow->model); + +} + +static gint refine_button_stop(GtkWidget *widget, RefinementWindow *refinementwindow) { + + gtk_widget_set_sensitive(refinementwindow->go, TRUE); + gtk_widget_set_sensitive(refinementwindow->stop, FALSE); + + refine_stop(refinementwindow); + + return 0; + +} + +static gint refine_button_go(GtkWidget *widget, RefinementWindow *refinementwindow) { + + RefinementSpec spec; + RefinementType type; + + spec = REFINE_SPEC_NONE; + type = REFINE_TYPE_NONE; + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_x)) ) spec = spec | REFINE_SPEC_X; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_y)) ) spec = spec | REFINE_SPEC_Y; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_z)) ) spec = spec | REFINE_SPEC_Z; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_b)) ) spec = spec | REFINE_SPEC_B; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_occ)) ) spec = spec | REFINE_SPEC_OCC; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->spec_thickness)) ) spec = spec | REFINE_SPEC_THICKNESS; + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_neighboursearch)) ) type = REFINE_TYPE_NEIGHBOURSEARCH; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_brent)) ) type = REFINE_TYPE_BRENT; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_lmder)) ) type = REFINE_TYPE_LMDER; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_lsq)) ) type = REFINE_TYPE_LSQ; + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->type_cgrad)) ) type = REFINE_TYPE_CGRAD; + + if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(refinementwindow->target_intensities)) ) spec = spec | REFINE_SPEC_INTENSITIES; + + refinementwindow->spec = spec; + refinementwindow->type = type; + + gtk_widget_set_sensitive(refinementwindow->go, FALSE); + gtk_widget_set_sensitive(refinementwindow->stop, TRUE); + + refine_go(refinementwindow); + + return 0; + +} + +static gint refine_response(GtkWidget *refine_window, gint response, RefinementWindow *refinementwindow) { + + if ( refinementwindow->running ) refine_stop(refinementwindow); + + /* Avoid double-freeing if the refinement window already got closed */ + if ( refinementwindow->model->refine_window ) { + gtk_widget_destroy(refine_window); + refinementwindow->model->refine_window = NULL; + free(refinementwindow); + } + + return 0; + +} + +gint refine_type_neighboursearch(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_brent(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, FALSE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_cgrad(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_lmder(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +gint refine_type_lsq(GtkWidget *widget, RefinementWindow *refinementwindow) { + gtk_widget_set_sensitive(refinementwindow->spec_x, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_y, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_z, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_b, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_occ, TRUE); + gtk_widget_set_sensitive(refinementwindow->spec_thickness, FALSE); + return 0; +} + +void refine_open(AtomicModel *model) { + + GtkWidget *refine_window; + GtkWidget *table; + GtkWidget *label; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *buttons; + + if ( model->refine_window ) return; + model->refine_window = malloc(sizeof(RefinementWindow)); + model->refine_window->model = model; + model->refine_window->run_semaphore = 0; + model->refine_window->running = 0; + model->refine_window->work_thread = NULL; + model->refine_window->display_callback = 0; + + refine_window = gtk_dialog_new_with_buttons("Refine Model", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); + g_signal_connect(G_OBJECT(refine_window), "response", G_CALLBACK(refine_response), model->refine_window); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(refine_window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label), "Refinement Algorithm"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 5); + + table = gtk_table_new(5, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0); + + model->refine_window->type_neighboursearch = gtk_radio_button_new_with_label(NULL, "Random Neighbour Search"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_neighboursearch), 1, 3, 2, 3); + g_signal_connect(G_OBJECT(model->refine_window->type_neighboursearch), "toggled", G_CALLBACK(refine_type_neighboursearch), model->refine_window); + + label = gtk_label_new("Step Length:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label), 1, 2, 3, 4); + + model->refine_window->nbsearch_shift = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(model->refine_window->nbsearch_shift), 10); + gtk_entry_set_width_chars(GTK_ENTRY(model->refine_window->nbsearch_shift), 8); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->nbsearch_shift), 2, 3, 3, 4); + gtk_entry_set_text(GTK_ENTRY(model->refine_window->nbsearch_shift), "0.001"); + + model->refine_window->type_brent = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "Sequential Brent Minimisation"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_brent), 1, 3, 4, 5); + g_signal_connect(G_OBJECT(model->refine_window->type_brent), "toggled", G_CALLBACK(refine_type_brent), model->refine_window); + + model->refine_window->type_lmder = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "GSL Levenberg-Marquardt"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_lmder), 1, 3, 5, 6); + g_signal_connect(G_OBJECT(model->refine_window->type_lmder), "toggled", G_CALLBACK(refine_type_lmder), model->refine_window); + + model->refine_window->type_lsq = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "Least Squares"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_lsq), 1, 3, 6, 7); + g_signal_connect(G_OBJECT(model->refine_window->type_lsq), "toggled", G_CALLBACK(refine_type_lsq), model->refine_window); + + model->refine_window->type_cgrad = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->type_neighboursearch), + "Conjugate Gradient"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(model->refine_window->type_cgrad), 1, 3, 7, 8); + g_signal_connect(G_OBJECT(model->refine_window->type_cgrad), "toggled", G_CALLBACK(refine_type_cgrad), model->refine_window); + + label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label), "Parameters to Refine"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 10); + + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + model->refine_window->spec_x = gtk_check_button_new_with_label("x"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_x), FALSE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->spec_x), TRUE); + model->refine_window->spec_y = gtk_check_button_new_with_label("y"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_y), FALSE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->spec_y), TRUE); + model->refine_window->spec_z = gtk_check_button_new_with_label("z"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_z), FALSE, TRUE, 0); + + model->refine_window->spec_b = gtk_check_button_new_with_label("Debye-Waller parameters"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(model->refine_window->spec_b), FALSE, FALSE, 5); + + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + model->refine_window->spec_occ = gtk_check_button_new_with_label("Occupancies"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_occ), FALSE, FALSE, 0); + model->refine_window->spec_thickness = gtk_check_button_new_with_label("Thickness"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->spec_thickness), FALSE, FALSE, 0); + + label = gtk_label_new(""); + gtk_label_set_markup(GTK_LABEL(label), "Refine Against"); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(label), FALSE, FALSE, 10); + + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 5); + + model->refine_window->target_amplitudes = gtk_radio_button_new_with_label(NULL, "Amplitudes"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->target_amplitudes), FALSE, TRUE, 3); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->target_amplitudes), TRUE); + model->refine_window->target_intensities = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(model->refine_window->target_amplitudes), + "Intensities"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(model->refine_window->target_intensities), FALSE, TRUE, 3); + + /* Control buttons */ + buttons = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(buttons), FALSE, FALSE, 5); + /* Stop */ + model->refine_window->stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(model->refine_window->stop), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(model->refine_window->stop), "clicked", G_CALLBACK(refine_button_stop), model->refine_window); + /* Go */ + model->refine_window->go = gtk_button_new_with_label("Run"); + gtk_button_set_image(GTK_BUTTON(model->refine_window->go), gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON)); + gtk_box_pack_start(GTK_BOX(buttons), GTK_WIDGET(model->refine_window->go), TRUE, TRUE, 5); + g_signal_connect(G_OBJECT(model->refine_window->go), "clicked", G_CALLBACK(refine_button_go), model->refine_window); + + gtk_widget_set_sensitive(model->refine_window->go, TRUE); + gtk_widget_set_sensitive(model->refine_window->stop, FALSE); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(model->refine_window->type_lmder), TRUE); + gtk_widget_show_all(refine_window); + +} + diff --git a/src/refine.h b/src/refine.h new file mode 100644 index 0000000..2646b23 --- /dev/null +++ b/src/refine.h @@ -0,0 +1,102 @@ +/* + * refine.h + * + * Model Refintement + * + * (c) 2006-2007 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef REFINE_H +#define REFINE_H + +#include + +typedef enum { + COORDINATE_X, + COORDINATE_Y, + COORDINATE_Z +} CoordinateSelector; + +typedef enum { + REFINE_SPEC_NONE = 0, + REFINE_SPEC_X = 1<<0, + REFINE_SPEC_Y = 1<<1, + REFINE_SPEC_Z = 1<<2, + REFINE_SPEC_B = 1<<3, + REFINE_SPEC_OCC = 1<<4, + REFINE_SPEC_THICKNESS = 1<<16, + REFINE_SPEC_INTENSITIES = 1<<18 +} RefinementSpec; + +typedef enum { + REFINE_TYPE_NONE = 0, + REFINE_TYPE_NEIGHBOURSEARCH, + REFINE_TYPE_BRENT, + REFINE_TYPE_LMDER, + REFINE_TYPE_LSQ, + REFINE_TYPE_CGRAD +} RefinementType; + +typedef struct { + + /* Model this refinement refers to */ + struct struct_atomicmodel *model; + + /* Refinement specifications */ + RefinementSpec spec; + RefinementType type; + + /* Dialog box bits */ + GtkWidget *spec_x; + GtkWidget *spec_y; + GtkWidget *spec_z; + GtkWidget *spec_b; + GtkWidget *spec_thickness; + GtkWidget *spec_occ; + GtkWidget *type_neighboursearch; + GtkWidget *type_brent; + GtkWidget *type_lmder; + GtkWidget *type_lsq; + GtkWidget *type_cgrad; + GtkWidget *nbsearch_shift; + GtkWidget *target_amplitudes; + GtkWidget *target_intensities; + GtkWidget *stop; + GtkWidget *go; + + /* Thread control */ + GThread *work_thread; + unsigned int running; + unsigned int run_semaphore; + GStaticMutex display_mutex; + guint display_callback; + +} RefinementWindow; + +#include "reflist.h" + +typedef struct { + struct struct_atomicmodel *model; + ReflectionList *reflections; /* Observations */ + unsigned int cur_mod_atom; + CoordinateSelector cur_mod_coor; + double scale; + RefinementSpec spec; +} RefinementPair; + +extern void refine_open(struct struct_atomicmodel *model); + +#define LSQ_MSLS_SHIFT 0.000000001 +#define MAX_REFINEMENT_ITERATIONS 400 + +extern void refine_schedule_update(struct struct_atomicmodel *model); + +#endif /* REFINE_H */ + diff --git a/src/reflist.c b/src/reflist.c new file mode 100644 index 0000000..e0224e9 --- /dev/null +++ b/src/reflist.c @@ -0,0 +1,287 @@ +/* + * reflist.c + * + * Reflection-handling code + * + * (c) 2006-2008 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#include +#include +#include +#include +#include + +#include "reflist.h" + +ReflectionList *reflist_new() { + + ReflectionList *new = malloc(sizeof(ReflectionList)); + assert(new != NULL); + + new->n_reflections = 0; + + /* DC component makes reflist_inlist() work */ + reflist_addref(new, 0, 0, 0); + new->n_reflections = 1; + + return new; + +} + +ReflectionList *reflist_new_parent(ReflectionList *parent) { + + ReflectionList *new = reflist_new(); + + new->parent = parent; + + return new; + +} + +ReflectionList *reflist_copy(ReflectionList *list) { + ReflectionList *new = malloc(sizeof(ReflectionList)); + memcpy(new, list, sizeof(ReflectionList)); + return new; +} + +void reflist_free(ReflectionList *list) { + free(list); +} + +/* Add a reflection to a list, including all associated data */ +static int reflist_addref_all(ReflectionList *list, signed int h, signed int k, signed int l, + double am, double phase, double phase_set, double alpha, double delta_theta, signed int multiplier, + unsigned int parent_index) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (addref)\n", + list->n_reflections); + return -1; + } + + //if ( (i = reflist_inlist(list, h, k, l)) ) { + //printf("RL: Duplicated reflection %i %i %i\n", h, k, l); + //} else { + i = list->n_reflections; + //} + if ( (h==0) && (k==0) && (l==0) ) i = 0; + + list->refs[i].h = h; + list->refs[i].k = k; + list->refs[i].l = l; + list->refs[i].amplitude = am; + list->refs[i].am_error = 1; + list->refs[i].weight = 1; + list->refs[i].phase_known = phase; + list->refs[i].phase_known_set = phase_set; + list->refs[i].alpha = alpha; + list->refs[i].delta_theta = delta_theta; + list->refs[i].multiplier = multiplier; + list->refs[i].parent_index = parent_index; + + if ( !((h==0) && (k==0) && (l==0)) ) list->n_reflections++; + + return i; + +} + +/* Add a reflection to a list */ +int reflist_addref(ReflectionList *list, signed int h, signed int k, signed int l) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, 0, 0, 1, 0); +} + +/* Add a reflection to a list */ +int reflist_addref_am(ReflectionList *list, signed int h, signed int k, signed int l, double am) { + return reflist_addref_all(list, h, k, l, am, 0, 0, 0, 0, 1, 0); +} + +int reflist_addref_am_ph(ReflectionList *list, signed int h, signed int k, signed int l, double am, double ph) { + return reflist_addref_all(list, h, k, l, am, ph, 1, 0, 0, 1, 0); +} + +/* Add a reflection to a list, including alpha value */ +int reflist_addref_alpha_parent(ReflectionList *list, signed int h, signed int k, signed int l, double alpha, unsigned int parent_index) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, alpha, 0, 1, parent_index); +} + +/* Add a reflection to a list, including phase value */ +int reflist_addref_phase(ReflectionList *list, signed int h, signed int k, signed int l, double phase) { + return reflist_addref_all(list, h, k, l, 0, phase, 1, 0, 0, 1, 0); +} + +/* Add a reflection to a list, including "delta-phase" value */ +int reflist_addref_deltatheta(ReflectionList *list, signed int h, signed int k, signed int l, double delta_theta, signed int multiplier) { + if ( !reflist_inlist(list, h, k, l) ) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, 0, delta_theta, multiplier, 0); + } else { + //printf("RL: Not re-adding reflection %3i %3i %3i\n", h, k, l); + return -1; + } +} + +int reflist_addref_parent(ReflectionList *list, signed int h, signed int k, signed int l, unsigned int parent_index) { + return reflist_addref_all(list, h, k, l, 0, 0, 0, 0, 0, 1, parent_index); +} + +int reflist_addref_am_parent(ReflectionList *list, signed int h, signed int k, signed int l, double am, unsigned int parent_index) { + return reflist_addref_all(list, h, k, l, am, 0, 0, 0, 0, 1, parent_index); +} + +/* Delete a reflection from a list */ +void reflist_delref(ReflectionList *list, signed int h, signed int k, signed int l) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (delref)\n", + list->n_reflections); + return; + } + + if ( list->n_reflections == 0 ) { return; } + + for ( i=1; in_reflections; i++ ) { + if ( (list->refs[i].h == h) && (list->refs[i].k == k) ) { + + if ( i < list->n_reflections ) { + unsigned int j; + /* Shove everything up one place */ + for ( j=i+1; jn_reflections; j++ ) { + list->refs[j-1].h = list->refs[j].h; + list->refs[j-1].k = list->refs[j].k; + list->refs[j-1].l = list->refs[j].l; + list->refs[j-1].amplitude = list->refs[j].amplitude; + list->refs[j-1].phase_known = list->refs[j].phase_known; + list->refs[j-1].phase_known_set = list->refs[j].phase_known_set; + list->refs[j-1].phase_calc = list->refs[j].phase_calc; + list->refs[j-1].phase_calc_set = list->refs[j].phase_calc_set; + list->refs[j-1].alpha = list->refs[j].alpha; + list->refs[j-1].delta_theta = list->refs[j].delta_theta; + list->refs[j-1].multiplier = list->refs[j].multiplier; + list->refs[j-1].parent_index = list->refs[j].parent_index; + } + } /* else nothing to do */ + + list->n_reflections--; + + } + } + +} + +/* See if a reflection is in a list */ +int reflist_inlist(const ReflectionList *list, signed int h, signed int k, signed int l) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (refinlist)\n", + list->n_reflections); + return 0; + } + + if ( list->n_reflections == 0 ) { return 0; } + + for ( i=0; in_reflections; i++ ) { + if ( (list->refs[i].h == h) && (list->refs[i].k == k) && (list->refs[i].l == l) ) return i; + } + + return 0; + +} + +/* As above, but ignoring "l" */ +int reflist_inlist_2d(const ReflectionList *list, signed int h, signed int k) { + + unsigned int i; + + if ( list->n_reflections >= MAX_REFLECTIONS ) { + fprintf(stderr, "Too many reflections (%i): increase MAX_REFLECTIONS in reflist.h (refinlist)\n", + list->n_reflections); + return 0; + } + + if ( list->n_reflections == 0 ) { return 0; } + + for ( i=0; in_reflections; i++ ) { + if ( (list->refs[i].h == h) && (list->refs[i].k == k) ) return i; + } + + return 0; + +} + +void reflist_set_components(ReflectionList *list, signed int h, signed int k, signed int l, double re, double im) { + + unsigned int idx = reflist_inlist(list, h, k, l); + if ( !idx ) return; + list->refs[idx].re = re; + list->refs[idx].im = im; + +} + +ReflectionList *reflist_new_from_array(fftw_complex *array, int width, int height) { + + ReflectionList *reflections; + signed int h, k; + + reflections = reflist_new(); + + for ( h=-(width-1)/2; h<=(width-1)/2; h++ ) { + for ( k=-(height-1)/2; k<=(height-1)/2; k++ ) { + + double re, im, am, ph; + signed int hc, kc; + + hc = h; kc = k; + if ( h < 0 ) { hc = width+hc; } + if ( k < 0 ) { kc = height+kc; } + re = array[kc+height*hc][0]; im = array[kc+height*hc][1]; + am = sqrt(re*re + im*im); ph = atan2(im, re); + + if ( am > 0 ) { + reflist_addref_am_ph(reflections, h, k, 0, am, ph); + //printf("Got %3i %3i am=%f ph=%f\n", h, k, am, ph); + } + + } + } + + return reflections; + +} + +void reflist_fill_array(fftw_complex *array, const ReflectionList *reflections, int width, int height) { + + signed int h, k; + unsigned int i; + + bzero(array, width*height*sizeof(fftw_complex)); + + for ( i=0; in_reflections; i++ ) { + + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + if ( abs(h) > width/2 ) { + printf("Index %i %i (%i) is above the Nyquist frequency!\n", h, k, reflections->refs[i].l); + continue; + } + + if ( h < 0 ) { h = width+h; } + if ( k < 0 ) { k = height+k; } + + if ( reflections->refs[i].phase_known_set ) { + array[k + height*h][0] = reflections->refs[i].amplitude * cos(reflections->refs[i].phase_known); + array[k + height*h][1] = reflections->refs[i].amplitude * sin(reflections->refs[i].phase_known); + } + + } + +} diff --git a/src/reflist.h b/src/reflist.h new file mode 100644 index 0000000..87dbbea --- /dev/null +++ b/src/reflist.h @@ -0,0 +1,83 @@ +/* + * reflist.h + * + * Reflection-handling code + * + * (c) 2006-2008 Thomas White + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#ifndef REFLIST_H +#define REFLIST_H + +#include + +#define MAX_REFLECTIONS 20000 + +typedef struct { + + signed int h; + signed int k; + signed int l; + + /* Some, all or none of the following may be filled in, depending on the context */ + + double amplitude; /* Structure amplitude F (not I=F^2). Must be positive */ + double am_error; /* Standard error in amplitude */ + double weight; /* Statistical weight for this data point */ + + double phase_known; /* Phase known a priori */ + unsigned int phase_known_set; + double phase_calc; /* Phase calculated */ + unsigned int phase_calc_set; + + double re; + double im; /* LSQ routine does crazy things with these */ + + double alpha; /* For convergence procedure and tangent refinement */ + double delta_theta; /* Some kind of phase relationship, */ + signed int multiplier; /* for symmetry stuff */ + + double amplitude_dyn; + double phase_dyn; /* As calculated by the multislice routine */ + + unsigned int parent_index; /* Position of the same reflection in a related list */ + +} Reflection; + +typedef struct _reflectionlist { + Reflection refs[MAX_REFLECTIONS]; + unsigned int n_reflections; + struct _reflectionlist *parent; + double scale; +} ReflectionList; + +typedef struct { + ReflectionList *a; + ReflectionList *b; +} ReflectionListPair; + +extern ReflectionList *reflist_new(void); +extern ReflectionList *reflist_new_parent(ReflectionList *parent); +extern ReflectionList *reflist_copy(ReflectionList *list); +extern void reflist_free(ReflectionList *list); + +extern int reflist_addref(ReflectionList *list, signed int h, signed int k, signed int l); +extern int reflist_addref_parent(ReflectionList *list, signed int h, signed int k, signed int l, unsigned int parent_index); +extern int reflist_addref_am(ReflectionList *list, signed int h, signed int k, signed int l, double am); +extern int reflist_addref_am_ph(ReflectionList *list, signed int h, signed int k, signed int l, double am, double ph); +extern int reflist_addref_alpha_parent(ReflectionList *list, signed int h, signed int k, signed int l, double alpha, unsigned int parent_index); +extern int reflist_addref_phase(ReflectionList *list, signed int h, signed int k, signed int l, double phase); +extern int reflist_addref_deltatheta(ReflectionList *list, signed int h, signed int k, signed int l, double delta_theta, signed int multiplier); +extern void reflist_delref(ReflectionList *list, signed int h, signed int k, signed int l); +extern int reflist_inlist(const ReflectionList *list, signed int h, signed int k, signed int l); +extern int reflist_inlist_2d(const ReflectionList *list, signed int h, signed int k); +extern void reflist_set_components(ReflectionList *list, signed int h, signed int k, signed int l, double re, double im); +extern int reflist_addref_am_parent(ReflectionList *list, signed int h, signed int k, signed int l, double am, unsigned int parent_index); + +extern ReflectionList *reflist_new_from_array(fftw_complex *array, int width, int height); +extern void reflist_fill_array(fftw_complex *array, const ReflectionList *reflections, int width, int height); + +#endif /* REFLIST_H */ diff --git a/src/renderer.c b/src/renderer.c new file mode 100644 index 0000000..9384e30 --- /dev/null +++ b/src/renderer.c @@ -0,0 +1,205 @@ +/* + * renderer.c + * + * Render Fourier Transform output array into display array + * + * (c) 2006-2007 Thomas White + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "renderer.h" + +double renderer_width(int width, int height, double gamma, int nx, int ny) { + return nx*width + ny*height*fabs(cos(gamma)); +} + +double renderer_height(int width, int height, double gamma, int nx, int ny) { + return height*sin(gamma)*ny; +} + +/* Provide the coordinate in the image (ie xn,yn) given x,y in the Fourier array */ +double renderer_map_x(double x, double y, int width, int height, double gamma, int nx, int ny) { + + double xn, offs; + + if ( gamma > M_PI_2 ) { + offs = (double)ny*height*cos(M_PI-gamma); + } else { + offs = 0.0; + } + xn = x + offs + y*cos(gamma); + + return xn; + +} + +/* Same, but return y */ +double renderer_map_y(double x, double y, int width, int height, double gamma, int nx, int ny) { + return y*sin(gamma); +} + +static double renderer_map_reverse_x(double xn, double yn, int width, int height, double gamma, int nx, int ny) { + + double offs, xd, yd; + + if ( gamma > M_PI_2 ) { + offs = (double)ny*height*cos(M_PI-gamma); + } else { + offs = 0.0; + } + + yd = (double)yn / sin(gamma); + xd = (double)xn - offs - yd*cos(gamma); + + while ( xd < 0 ) xd += width; + xd = fmod(xd, width); + + return xd; + +} + +static double renderer_map_reverse_y(double xn, double yn, int width, int height, double gamma, int nx, int ny) { + + double yd; + + yd = (double)yn / sin(gamma); + while ( yd < 0 ) yd += height; + yd = fmod(yd, height); + + return yd; + +} + +static double renderer_interpolate_linear_re(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, re, re1, re2; + int x, y; + + /* Get the left-hand point value */ + x = floor(xd); y = floor(yd); + if ( x >= width ) x -= width; + if ( y >= height ) y -= height; + re1 = out[(height-1-y)+height*(width-1-x)][0]; + + /* Get the right-hand point value */ + x++; + if ( x >= width ) x -= width; + re2 = out[(height-1-y)+height*(width-1-x)][0]; + + frac = fmod(xd, 1); + re = (1-frac)*re1 + frac*re2; + + return re; + +} + +static double renderer_interpolate_linear_im(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, im, im1, im2; + int x, y; + + /* Get the left-hand point value */ + x = floor(xd); y = floor(yd); + if ( x >= width ) x -= width; + if ( y >= height ) y -= height; + im1 = out[(height-1-y)+height*(width-1-x)][1]; + + /* Get the right-hand point value */ + x++; + if ( x >= width ) x -= width; + im2 = out[(height-1-y)+height*(width-1-x)][1]; + + frac = fmod(xd, 1); + im = (1-frac)*im1 + frac*im2; + + return im; + +} + +static double renderer_interpolate_bilinear_re(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, re, re1, re2; + + /* Get the lower interpolated value */ + re1 = renderer_interpolate_linear_re(xd, yd, out, width, height); + + /* Get the upper interpolated value */ + yd++; + if ( yd >= height ) yd -= height; + re2 = renderer_interpolate_linear_re(xd, yd, out, width, height); + + frac = fmod(yd, 1); + re = (1-frac)*re1 + frac*re2; + + return re; + +} + +static double renderer_interpolate_bilinear_im(double xd, double yd, fftw_complex *out, int width, int height) { + + double frac, im, im1, im2; + + /* Get the lower interpolated value */ + im1 = renderer_interpolate_linear_im(xd, yd, out, width, height); + + /* Get the upper interpolated value */ + yd++; + if ( yd >= height ) yd -= height; + im2 = renderer_interpolate_linear_im(xd, yd, out, width, height); + + frac = fmod(yd, 1); + im = (1-frac)*im1 + frac*im2; + + return im; + +} + +ComplexArray renderer_draw(fftw_complex *out, int width, int height, double gamma, int nx, int ny) { + + ComplexArray cxar; + int width_n, height_n; + int xn, yn; + + width_n = (int)renderer_width(width, height, gamma, nx, ny); + height_n = (int)renderer_height(width, height, gamma, nx, ny); + + cxar.re = malloc(height_n*width_n*sizeof(double)); + cxar.im = malloc(height_n*width_n*sizeof(double)); + + for ( yn=0; yn + * + * synth2D - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef RENDERER_H +#define RENDERER_H + +typedef struct { + double *re; + double *im; +} ComplexArray; + +extern double renderer_width(int width, int height, double gamma, int nx, int ny); +extern double renderer_height(int width, int height, double gamma, int nx, int ny); +extern ComplexArray renderer_draw(fftw_complex *out, int width, int height, double gamma, int nx, int ny); +extern double renderer_map_x(double x, double y, int width, int height, double gamma, int nx, int ny); +extern double renderer_map_y(double x, double y, int width, int height, double gamma, int nx, int ny); + +#endif /* RENDERER_H */ + diff --git a/src/statistics.c b/src/statistics.c new file mode 100644 index 0000000..5fa4634 --- /dev/null +++ b/src/statistics.c @@ -0,0 +1,223 @@ +/* + * statistics.c + * + * Structure Factor Statistics + * + * (c) 2006 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "reflist.h" +#include "statistics.h" + +/* Return the least-squares estimate of the optimum scaling factor for intensities */ +double stat_scale_intensity(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double top = 0; + double bot = 0; + + if ( obs->n_reflections == 0 ) return 0; + if ( calc->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + top += (obs->refs[i].amplitude*obs->refs[i].amplitude) * (calc->refs[i].amplitude*calc->refs[i].amplitude); + bot += (calc->refs[i].amplitude*calc->refs[i].amplitude) * (calc->refs[i].amplitude*calc->refs[i].amplitude); + + } + + return top/bot; + +} + +/* Return the least-squares estimate of the optimum scaling factor */ +double stat_scale(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double top = 0; + double bot = 0; + + if ( obs->n_reflections == 0 ) return 0; + if ( calc->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + top += obs->refs[i].amplitude * calc->refs[i].amplitude; + bot += calc->refs[i].amplitude * calc->refs[i].amplitude; + + } + + return top/bot; + +} + +/* R-factor in terms of diffracted intensities */ +double stat_r2(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double scale; + double err; + double den; + + scale = stat_scale_intensity(obs, calc); + err = 0; den = 0; + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + err += fabs( (obs->refs[i].amplitude*obs->refs[i].amplitude) - (scale * (calc->refs[i].amplitude*calc->refs[i].amplitude)) ); + den += obs->refs[i].amplitude * obs->refs[i].amplitude; + + } + + return err/den; + +} + +/* R-factor in terms of amplitudes of structure factors */ +double stat_r1(ReflectionList *obs, ReflectionList *calc) { + + unsigned int i; + double scale; + double err; + double den; + + scale = stat_scale(obs, calc); + err = 0; den = 0; + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + + assert(obs->refs[i].amplitude >= 0); + assert(calc->refs[i].amplitude >= 0); + + err += fabs( obs->refs[i].amplitude - (scale * calc->refs[i].amplitude) ); + den += obs->refs[i].amplitude; + + } + + return err/den; + +#if 0 + double scale; + gsl_function F; + gsl_min_fminimizer *s; + int status; + int iter = 0, max_iter = 100; + + printf("Estimate: Scale = %f, R=%.2f%%\n", opt.scale, opt.r*100); + + opt.r = 999999999; + opt.scale = 0; + + F.function = &stat_calc_r; + F.params = &pair; + + s = gsl_min_fminimizer_alloc(gsl_min_fminimizer_brent); + gsl_min_fminimizer_set(s, &F, 10, 1, 1000); + + do { + + double lo, up; + + /* Iterate */ + gsl_min_fminimizer_iterate(s); + iter++; + + /* Get the current estimate */ + scale = gsl_min_fminimizer_x_minimum(s); + lo = gsl_min_fminimizer_x_lower(s); + up = gsl_min_fminimizer_x_upper(s); + + /* Check for convergence */ + status = gsl_min_test_interval(lo, up, 0.001, 0.0); + if (status == GSL_SUCCESS) { + + OptimumR opt; + + opt.r = stat_calc_r(scale, &pair); + opt.scale = scale; + + printf("Minimum: Scale=%f, R=%.2f%%\n", opt.scale, opt.r*100); + + gsl_min_fminimizer_free(s); + + return opt; + + } + + } while (status == GSL_CONTINUE && iter < max_iter); + + return opt; +#endif + +} + +double stat_sigma_f(ReflectionList *reflections) { + + unsigned int i; + double sigma_f = 0; + + if ( reflections->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + assert(reflections->refs[i].amplitude >= 0); + sigma_f += reflections->refs[i].amplitude; + } + + return sigma_f; + +} + +double stat_stddev(ReflectionList *a) { + + unsigned int i; + double sigma_f, mean; + double sigma_dev = 0; + + if ( a->n_reflections == 0 ) return 0; /* No reflections */ + + sigma_f = stat_sigma_f(a); + mean = sigma_f / a->n_reflections; + for ( i=1; in_reflections; i++ ) { /* 'hkl' loop */ + sigma_dev += (a->refs[i].amplitude - mean) * (a->refs[i].amplitude - mean); + } + + return sqrt(sigma_dev/a->n_reflections); + +} + +double stat_maxam(ReflectionList *a) { + + unsigned int i; + double max_am = 0; + + if ( a->n_reflections == 0 ) return 0; /* No reflections */ + + for ( i=1; in_reflections; i++ ) { + if ( a->refs[i].amplitude > max_am ) max_am = a->refs[i].amplitude; + } + + return max_am; + +} diff --git a/src/statistics.h b/src/statistics.h new file mode 100644 index 0000000..be49098 --- /dev/null +++ b/src/statistics.h @@ -0,0 +1,31 @@ +/* + * statistics.h + * + * Structure Factor Statistics + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef STATISTICS_H +#define STATISTICS_H + +#include "reflist.h" + +extern double stat_scale(ReflectionList *obs, ReflectionList *calc); +extern double stat_scale_intensity(ReflectionList *obs, ReflectionList *calc); + +extern double stat_r1(ReflectionList *a, ReflectionList *b); +extern double stat_r2(ReflectionList *a, ReflectionList *b); +#define stat_r(a, b) stat_r1((a), (b)) + +extern double stat_sigma_f(ReflectionList *reflections); +extern double stat_stddev(ReflectionList *a); +extern double stat_maxam(ReflectionList *a); + +#endif /* STATISTICS_H */ diff --git a/src/superlattice.c b/src/superlattice.c new file mode 100644 index 0000000..b07005a --- /dev/null +++ b/src/superlattice.c @@ -0,0 +1,142 @@ +/* + * superlattice.c + * + * Superlattice Operations + * + * (c) 2006-2007 Thomas White + * + * synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "displaywindow.h" +#include "main.h" +#include "reflist.h" +#include "data.h" + +typedef struct { + + GtkWidget *window; + GtkWidget *xcells; + GtkWidget *ycells; + +} SuperlatticeSplitWindow; + +SuperlatticeSplitWindow *superlattice_sls = NULL; + +ReflectionList *superlattice_split(ReflectionList *old, unsigned int xcells, unsigned int ycells) { + + ReflectionList *new; + unsigned int i; + + printf("SL: Splitting %ix%i superlattice cell\n", xcells, ycells); + + new = reflist_new_parent(old); + for ( i=1; in_reflections; i++ ) { + + signed int h, k, l; + + h = old->refs[i].h; k = old->refs[i].k; l = old->refs[i].l; + if ( (h % (signed)xcells == 0) && (k % (signed)ycells == 0) ) { + reflist_addref_am_parent(new, h/(signed)xcells, k/(signed)ycells, l, old->refs[i].amplitude, i); + } + + } + data_dividecell(xcells, ycells, 1); + displaywindow_createfourier(); + displaywindow_kicksize(); + + return new; + +} + +static gint superlattice_split_response(GtkWidget *widget, gint response, SuperlatticeSplitWindow *sls) { + + int done = 1; + + if ( response == GTK_RESPONSE_OK ) { + const char *xcells; + const char *ycells; + unsigned int xc, yc; + int scanval; + xcells = gtk_entry_get_text(GTK_ENTRY(sls->xcells)); + ycells = gtk_entry_get_text(GTK_ENTRY(sls->ycells)); + scanval = sscanf(xcells, "%u", &xc); + scanval += sscanf(ycells, "%u", &yc); + if ( scanval != 2 ) { + error_report("Please enter valid values for both superlattice cell dimensions."); + done = 0; + } else { + main_superlattice_split(xc, yc); + } + } + + if ( done ) { + gtk_widget_destroy(sls->window); + free(sls); + superlattice_sls = NULL; + } + + return 0; +} + +void superlattice_split_open() { + + SuperlatticeSplitWindow *sls; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *xcells_label; + GtkWidget *ycells_label; + + if ( superlattice_sls ) { + return; + } + sls = malloc(sizeof(SuperlatticeSplitWindow)); + superlattice_sls = sls; + + sls->window = gtk_dialog_new_with_buttons("Superlattice Split", GTK_WINDOW(displaywindow_gtkwindow()), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(sls->window)->vbox), GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + xcells_label = gtk_label_new("Number of cells along x:"); + gtk_misc_set_alignment(GTK_MISC(xcells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(xcells_label), 1, 2, 1, 2); + + sls->xcells = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(superlattice_sls->xcells), "1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(sls->xcells), 2, 3, 1, 2); + + ycells_label = gtk_label_new("Number of cells along y:"); + gtk_misc_set_alignment(GTK_MISC(ycells_label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ycells_label), 1, 2, 2, 3); + + sls->ycells = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(sls->ycells), "1"); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(sls->ycells), 2, 3, 2, 3); + + g_signal_connect(G_OBJECT(sls->window), "response", G_CALLBACK(superlattice_split_response), sls); + gtk_widget_show_all(sls->window); + gtk_widget_grab_focus(GTK_WIDGET(sls->xcells)); + +} + diff --git a/src/superlattice.h b/src/superlattice.h new file mode 100644 index 0000000..4622b6e --- /dev/null +++ b/src/superlattice.h @@ -0,0 +1,23 @@ +/* + * superlattice.h + * + * Superlattice Operations + * + * (c) 2006-2007 Thomas White + * Synth2D - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef SUPERLATTICE_H +#define SUPERLATTICE_H + +#include "reflist.h" + +extern ReflectionList *superlattice_split(ReflectionList *reflections, unsigned int xcells, unsigned int ycells); +extern void superlattice_split_open(void); + +#endif /* SUPERLATTICE_H */ diff --git a/src/symmetry.c b/src/symmetry.c new file mode 100644 index 0000000..65aef98 --- /dev/null +++ b/src/symmetry.c @@ -0,0 +1,464 @@ +/* + * symmetry.c + * + * Symmetry stuff + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "symmetry.h" +#include "reflist.h" +#include "model.h" + +#define is_odd(a) ((a)%2==1) + +/* Return a list of atoms containing the given atom and all its symmetry equivalents */ +AtomicModel *symmetry_generate_equivalent_atoms(AtomicModel *model, size_t j, ModelTarget target) { + + AtomicModel *list; + + list = model_new(); + list->atoms[list->n_atoms] = model->atoms[j]; list->n_atoms++; + + if ( target == MODEL_TARGET_DISPLAY ) { + + if ( model->atoms[j].x == 0 ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1.0; + list->n_atoms++; + } + + if ( model->atoms[j].y == 0 ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].y = 1.0; + list->n_atoms++; + } + + if ( (model->atoms[j].x == 0) && (model->atoms[j].y == 0) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1.0; + list->atoms[list->n_atoms].y = 1.0; + list->n_atoms++; + } + + } + + if ( model->sym & SYMMETRY_CENTRE ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1-list->atoms[list->n_atoms].x; + list->atoms[list->n_atoms].y = 1-list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_HORIZONTAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].y = 1-list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_VERTICAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1-list->atoms[list->n_atoms].x; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_HORIZONTAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(0.5 + list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = 1-list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_VERTICAL ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1-list->atoms[list->n_atoms].x; + list->atoms[list->n_atoms].y = fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(0.5 + list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = fmod(1.5 - list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_GLIDE_VERTICAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + /* Fix cm */ + if ( (model->sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER) && (model->sym & SYMMETRY_MIRROR_HORIZONTAL) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(0.5 + list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = 1 - fmod(1.5 - list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } else if ( (model->sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (model->sym & SYMMETRY_MIRROR_VERTICAL) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = 1 - fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + /* Fix c2mm */ + if ( (model->sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (model->sym & SYMMETRY_MIRROR_HORIZONTAL) ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = 1 - fmod(0.5 + list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_HORIZONTAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = list->atoms[list->n_atoms].x; + list->atoms[list->n_atoms].y = fmod(1.5 - list->atoms[list->n_atoms].y, 1); + list->n_atoms++; + } + + if ( model->sym & SYMMETRY_MIRROR_VERTICAL_QUARTER ) { + list->atoms[list->n_atoms] = model->atoms[j]; + list->atoms[list->n_atoms].x = fmod(1.5 - list->atoms[list->n_atoms].x, 1); + list->atoms[list->n_atoms].y = list->atoms[list->n_atoms].y; + list->n_atoms++; + } + + return list; + +} + +ReflectionList *symmetry_generate_equivalent_reflections(Symmetry sym, signed int h, signed int k, signed int l) { + + ReflectionList *result; + + result = reflist_new(); + + /* Remove the minus in front of 'l' to make even HOLZ conditional transforms real. + * This is generally the wrong thing to do. */ + if ( sym & SYMMETRY_FRIEDEL ) reflist_addref_deltatheta(result, -h, -k, -l, 0, -1); + + if ( sym & SYMMETRY_MIRROR_HORIZONTAL ) reflist_addref_deltatheta(result, h, -k, l, 0, 1); + if ( sym & SYMMETRY_MIRROR_VERTICAL ) reflist_addref_deltatheta(result, -h, k, l, 0, 1); + if ( sym & SYMMETRY_MIRROR_DIAGONAL ) reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + + if ( sym & SYMMETRY_GLIDE_HORIZONTAL ) reflist_addref_deltatheta(result, h, -k, l, M_PI, 1); + if ( sym & SYMMETRY_GLIDE_VERTICAL ) reflist_addref_deltatheta(result, -h, k, l, M_PI, 1); + + if ( sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER ) { + if ( is_odd(abs(h)+abs(k)) ) { + reflist_addref_deltatheta(result, h, -k, l, M_PI, 1); + } else { + reflist_addref_deltatheta(result, h, -k, l, 0, 1); + } + } + + if ( sym & SYMMETRY_GLIDE_VERTICAL_QUARTER ) { + if ( is_odd(abs(h)+abs(k)) ) { + reflist_addref_deltatheta(result, -h, k, l, M_PI, 1); + } else { + reflist_addref_deltatheta(result, -h, k, l, 0, 1); + } + } + + if ( sym & SYMMETRY_ROTATION_4 ) { + if ( sym & SYMMETRY_GLIDE_DIAGONAL ) { + if ( is_odd(abs(h)+abs(k)) ) { + reflist_addref_deltatheta(result, h, -k, l, M_PI, 1); + reflist_addref_deltatheta(result, -h, k, l, M_PI, 1); + reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + reflist_addref_deltatheta(result, k, h, l, 0, 1); + reflist_addref_deltatheta(result, -k, h, l, M_PI, 1); + reflist_addref_deltatheta(result, k, -h, l, M_PI, 1); + reflist_addref_deltatheta(result, -k, -h, l, 0, 1); + } else { + reflist_addref_deltatheta(result, h, -k, l, 0, 1); + reflist_addref_deltatheta(result, -h, k, l, 0, 1); + reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + reflist_addref_deltatheta(result, k, h, l, 0, 1); + reflist_addref_deltatheta(result, -k, h, l, 0, 1); + reflist_addref_deltatheta(result, k, -h, l, 0, 1); + reflist_addref_deltatheta(result, -k, -h, l, 0, 1); + } + } else { + reflist_addref_deltatheta(result, -h, -k, l, 0, 1); + reflist_addref_deltatheta(result, -h, k, l, 0, 1); + reflist_addref_deltatheta(result, h, -k, l, 0, 1); + } + } + + return result; + +} + +static double symmetry_realphase(double phk) { + + if ( (phk >= 0) && (phk < M_PI_2) ) phk = 0; + else if ( (phk > M_PI_2) && (phk < M_PI) ) phk = M_PI; + else if ( (phk < 0) && (phk > -M_PI/2) ) phk = 0; + else if ( (phk <= -M_PI_2) && (phk > -M_PI) ) phk = M_PI; + + return phk; + +} + +void symmetry_centricity(ReflectionList *reflections, unsigned int i, Symmetry sym, SymFlags flags) { + + double am, ph; + signed int h, k; + + ph = 0; + h = reflections->refs[i].h; + k = reflections->refs[i].k; + + am = reflections->refs[i].amplitude; + if ( flags & SYMFLAG_PHASES_KNOWN ) ph = fmod(reflections->refs[i].phase_known, 2*M_PI); + if ( flags & SYMFLAG_PHASES_CALC ) ph = fmod(reflections->refs[i].phase_calc, 2*M_PI); + + if ( (sym & SYMMETRY_MIRROR_HORIZONTAL) && (sym & SYMMETRY_FRIEDEL) && (h == 0) ) { + ph = symmetry_realphase(ph); + } + + if ( (sym & SYMMETRY_MIRROR_VERTICAL) && (sym & SYMMETRY_FRIEDEL) && (k == 0) ) { + ph = symmetry_realphase(ph); + } + + if ( (sym & SYMMETRY_CENTRE) && (sym & SYMMETRY_FRIEDEL) ) { + ph = symmetry_realphase(ph); + } + + if (( ( (sym & SYMMETRY_GLIDE_HORIZONTAL) && (sym & SYMMETRY_MIRROR_VERTICAL) ) + || ( (sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER) && (sym & SYMMETRY_MIRROR_VERTICAL) ) + || ( (sym & SYMMETRY_GLIDE_VERTICAL) && (sym & SYMMETRY_MIRROR_HORIZONTAL) ) + || ( (sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (sym & SYMMETRY_MIRROR_HORIZONTAL) ) ) + && (is_odd(abs(h)+abs(k))) ) { + if ( (ph >= 0) && (ph < M_PI) ) ph = M_PI_2; + else if ( (ph >= M_PI_2) && (ph < 2*M_PI) ) ph = -M_PI_2; + else if ( (ph < 0) && (ph > -M_PI) ) ph = -M_PI_2; + else if ( (ph <= -M_PI) ) ph = M_PI_2; + } + + if ( flags & SYMFLAG_PHASES_KNOWN ) reflections->refs[i].phase_known = ph; + if ( flags & SYMFLAG_PHASES_CALC ) reflections->refs[i].phase_calc = ph; + +} + +unsigned int symmetry_reflection_allowed(signed int h, signed int k, signed int l, Symmetry sym) { + + if ( (sym & SYMMETRY_GLIDE_HORIZONTAL) && (h == 0) && (k % 2) ) return 0; + if ( (sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER) && (h == 0) && (k % 2) ) return 0; + if ( (sym & SYMMETRY_GLIDE_VERTICAL) && (k == 0) && (h % 2) ) return 0; + if ( (sym & SYMMETRY_GLIDE_VERTICAL_QUARTER) && (k == 0) && (h % 2) ) return 0; + if ( (sym & SYMMETRY_ROTATION_4 ) && ( sym & SYMMETRY_GLIDE_DIAGONAL ) && (k == 0) && (h % 2) ) return 0; + if ( (sym & SYMMETRY_ROTATION_4 ) && ( sym & SYMMETRY_GLIDE_DIAGONAL ) && (h == 0) && (k % 2) ) return 0; + + return 1; + +} + +/* Symmetrise a list of reflections (expanding all equivalents to P1) */ +double symmetry_symmetrise(ReflectionList *reflections, Symmetry sym, SymFlags flags) { + + unsigned int i = 0; + double r_sym = 0; + double r_sym_norm = 0; + unsigned int new_ref = 0; + unsigned int elim_ref = 0; + double am_elim = 0; + unsigned int n = reflections->n_reflections; + + for ( i=1; irefs[i].h; + k = reflections->refs[i].k; + l = reflections->refs[i].l; + + if ( (h==0) && (k==0) && (l==0) ) continue; + if ( symmetry_reflection_allowed(h, k, l, sym) ) { + + double ph = 69; + unsigned int j; + double av; + unsigned int av_n; + ReflectionList *equivalents; + + equivalents = symmetry_generate_equivalent_reflections(sym, h, k, l); + symmetry_centricity(reflections, i, sym, flags); + + /* First pass: calculate average amplitude, determine phase */ + av = reflections->refs[i].amplitude; av_n = 1; + for ( j=1; jn_reflections; j++ ) { + unsigned int p; + p = reflist_inlist(reflections, equivalents->refs[j].h, equivalents->refs[j].k, equivalents->refs[j].l); + if ( p ) { + av += reflections->refs[p].amplitude; + av_n++; + } + } + av = av / av_n; + r_sym += fabs(av - reflections->refs[i].amplitude); + r_sym_norm += reflections->refs[i].amplitude; + if ( flags & SYMFLAG_PHASES_KNOWN ) ph = fmod(reflections->refs[i].phase_known, 2*M_PI); + if ( flags & SYMFLAG_PHASES_CALC ) ph = fmod(reflections->refs[i].phase_calc, 2*M_PI); + //printf("Phase of %3i %3i %3i is %f\n", h, k, l, ph); + + /* Second pass: set all equivalent reflections to the average amplitude */ + reflections->refs[i].amplitude = av; + for ( j=1; jn_reflections; j++ ) { + + unsigned int p; + double phn; + p = reflist_inlist(reflections, equivalents->refs[j].h, equivalents->refs[j].k, equivalents->refs[j].l); + if ( p ) { + r_sym += fabs(av - reflections->refs[p].amplitude); + r_sym_norm += reflections->refs[p].amplitude; + reflections->refs[p].amplitude = av; + } else { + p = reflist_addref_am(reflections, equivalents->refs[j].h, equivalents->refs[j].k, equivalents->refs[j].l, av); + //printf("SY: Generating %3i %3i %3i am=%f\n", equivalents->refs[j].h, + // equivalents->refs[j].k, equivalents->refs[j].l, av); + new_ref++; + } + phn = (ph + equivalents->refs[j].delta_theta) * equivalents->refs[j].multiplier; + if ( flags & SYMFLAG_PHASES_KNOWN ) { + reflections->refs[p].phase_known = phn; + reflections->refs[p].phase_known_set = 1; + } + if ( flags & SYMFLAG_PHASES_CALC ) { + reflections->refs[p].phase_calc = phn; + reflections->refs[p].phase_calc_set = 1; + } + //printf("Set phase of %3i %3i %3i to %f (dt=%f, mul=%i)\n", equivalents->refs[j].h, + //equivalents->refs[j].k, equivalents->refs[j].l, + //phn, equivalents->refs[j].delta_theta, equivalents->refs[j].multiplier); + + + } + + reflist_free(equivalents); + + } else { + + am_elim += reflections->refs[i].amplitude; + reflist_delref(reflections, h, k, l); + //printf("SY: Eliminating systematically absent reflection %i %i %i\n", h, k, l); + elim_ref++; + i--; + + } + + } + + if ( r_sym > 0 ) { + r_sym = r_sym / r_sym_norm; + } + //printf("SY: R_sym = %.2f%%, %i reflections generated, %i reflections eliminated (total amplitude %f)\n", r_sym*100, new_ref, elim_ref, am_elim); + + return r_sym; + +} + +Symmetry symmetry_encode(const char *symmetry) { + + if ( strcmp(symmetry, "p1") == 0 ) return PLANEGROUP_P1; + if ( strcmp(symmetry, "p2") == 0 ) return PLANEGROUP_P2; + if ( strcmp(symmetry, "pm (m // x)") == 0 ) return PLANEGROUP_PM_X; + if ( strcmp(symmetry, "pm (m // y)") == 0 ) return PLANEGROUP_PM_Y; + if ( strcmp(symmetry, "pg (g // x)") == 0 ) return PLANEGROUP_PG_X; + if ( strcmp(symmetry, "pg (g // y)") == 0 ) return PLANEGROUP_PG_Y; + if ( strcmp(symmetry, "cm (m // x)") == 0 ) return PLANEGROUP_CM_X; + if ( strcmp(symmetry, "cm (m // y)") == 0 ) return PLANEGROUP_CM_Y; + if ( strcmp(symmetry, "p2mm") == 0 ) return PLANEGROUP_P2MM; + if ( strcmp(symmetry, "p2mg (m // x)") == 0 ) return PLANEGROUP_P2MG_X; + if ( strcmp(symmetry, "p2mg (m // y)") == 0 ) return PLANEGROUP_P2MG_Y; + if ( strcmp(symmetry, "p2gg") == 0 ) return PLANEGROUP_P2GG; + if ( strcmp(symmetry, "c2mm") == 0 ) return PLANEGROUP_C2MM; + if ( strcmp(symmetry, "p4") == 0 ) return PLANEGROUP_P4; + if ( strcmp(symmetry, "p4mm") == 0 ) return PLANEGROUP_P4MM; + if ( strcmp(symmetry, "p4gm") == 0 ) return PLANEGROUP_P4GM; + if ( strcmp(symmetry, "p3") == 0 ) return PLANEGROUP_P3; + if ( strcmp(symmetry, "p3m1") == 0 ) return PLANEGROUP_P3M1; + if ( strcmp(symmetry, "p31m") == 0 ) return PLANEGROUP_P31M; + if ( strcmp(symmetry, "p6") == 0 ) return PLANEGROUP_P6; + if ( strcmp(symmetry, "p6mm") == 0 ) return PLANEGROUP_P6MM; + + fprintf(stderr, "Unrecognised symmetry identifier '%s'\n", symmetry); + return SYMMETRY_IDENTITY; + +} + +const char *symmetry_decode(Symmetry sym) { + + if ( sym == PLANEGROUP_P1 ) return "p1"; + if ( sym == PLANEGROUP_P2 ) return "p2"; + if ( sym == PLANEGROUP_PM_X ) return "pm (m // x)"; + if ( sym == PLANEGROUP_PM_Y ) return "pm (m // y)"; + if ( sym == PLANEGROUP_PG_X ) return "pg (g // x)"; + if ( sym == PLANEGROUP_PG_Y ) return "pg (g // y)"; + if ( sym == PLANEGROUP_CM_X ) return "cm (m // x)"; + if ( sym == PLANEGROUP_CM_Y ) return "cm (m // y)"; + if ( sym == PLANEGROUP_P2MM ) return "p2mm"; + if ( sym == PLANEGROUP_P2MG_X ) return "p2mg (m // x)"; + if ( sym == PLANEGROUP_P2MG_Y ) return "p2mg (m // y)"; + if ( sym == PLANEGROUP_P2GG ) return "p2gg"; + if ( sym == PLANEGROUP_C2MM ) return "c2mm"; + if ( sym == PLANEGROUP_P4 ) return "p4"; + if ( sym == PLANEGROUP_P4MM ) return "p4mm"; + if ( sym == PLANEGROUP_P4GM ) return "p4gm"; + if ( sym == PLANEGROUP_P3 ) return "p3"; + if ( sym == PLANEGROUP_P3M1 ) return "p3m1"; + if ( sym == PLANEGROUP_P31M ) return "p31m"; + if ( sym == PLANEGROUP_P6 ) return "p6"; + if ( sym == PLANEGROUP_P6MM ) return "p6mm"; + if ( sym == (PLANEGROUP_P1 | SYMMETRY_FRIEDEL) ) return "p1 & Friedel"; + if ( sym == (PLANEGROUP_P2 | SYMMETRY_FRIEDEL) ) return "p2 & Friedel"; + if ( sym == (PLANEGROUP_PM_X | SYMMETRY_FRIEDEL) ) return "pm (m // x) & Friedel"; + if ( sym == (PLANEGROUP_PM_Y | SYMMETRY_FRIEDEL) ) return "pm (m // y) & Friedel"; + if ( sym == (PLANEGROUP_PG_X | SYMMETRY_FRIEDEL) ) return "pg (g // x) & Friedel"; + if ( sym == (PLANEGROUP_PG_Y | SYMMETRY_FRIEDEL) ) return "pg (g // y) & Friedel"; + if ( sym == (PLANEGROUP_CM_X | SYMMETRY_FRIEDEL) ) return "cm (m // x) & Friedel"; + if ( sym == (PLANEGROUP_CM_Y | SYMMETRY_FRIEDEL) ) return "cm (m // y) & Friedel"; + if ( sym == (PLANEGROUP_P2MM | SYMMETRY_FRIEDEL) ) return "p2mm & Friedel"; + if ( sym == (PLANEGROUP_P2MG_X | SYMMETRY_FRIEDEL) ) return "p2mg (m // x) & Friedel"; + if ( sym == (PLANEGROUP_P2MG_Y | SYMMETRY_FRIEDEL) ) return "p2mg (m // y) & Friedel"; + if ( sym == (PLANEGROUP_P2GG | SYMMETRY_FRIEDEL) ) return "p2gg & Friedel"; + if ( sym == (PLANEGROUP_C2MM | SYMMETRY_FRIEDEL) ) return "c2mm & Friedel"; + if ( sym == (PLANEGROUP_P4 | SYMMETRY_FRIEDEL) ) return "p4 & Friedel"; + if ( sym == (PLANEGROUP_P4MM | SYMMETRY_FRIEDEL) ) return "p4mm & Friedel"; + if ( sym == (PLANEGROUP_P4GM | SYMMETRY_FRIEDEL) ) return "p4gm & Friedel"; + if ( sym == (PLANEGROUP_P3 | SYMMETRY_FRIEDEL) ) return "p3 & Friedel"; + if ( sym == (PLANEGROUP_P3M1 | SYMMETRY_FRIEDEL) ) return "p3m1 & Friedel"; + if ( sym == (PLANEGROUP_P31M | SYMMETRY_FRIEDEL) ) return "p31m & Friedel"; + if ( sym == (PLANEGROUP_P6 | SYMMETRY_FRIEDEL) ) return "p6 & Friedel"; + if ( sym == (PLANEGROUP_P6MM | SYMMETRY_FRIEDEL) ) return "p6mm & Friedel"; + + return "(unknown symmetry)"; + +} + +void symmetry_symmetrise_array(fftw_complex *in, signed int width, signed int height, Symmetry sym) { + + ReflectionList *reflections; + + reflections = reflist_new_from_array(in, width, height); + symmetry_symmetrise(reflections, sym, SYMFLAG_PHASES_KNOWN); + reflist_fill_array(in, reflections, width, height); + reflist_free(reflections); + +} diff --git a/src/symmetry.h b/src/symmetry.h new file mode 100644 index 0000000..bdcd63e --- /dev/null +++ b/src/symmetry.h @@ -0,0 +1,89 @@ +/* + * symmetry.h + * + * Symmetry stuff + * + * (c) 2006 Thomas White + * Synth2d - two-dimensional Fourier synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef SYMMETRY_H +#define SYMMETRY_H + +#include + +#include "reflist.h" + +/* Symmetry elements */ +typedef enum { + SYMMETRY_IDENTITY = 0, + SYMMETRY_FRIEDEL = 1<<0, + SYMMETRY_MIRROR_DIAGONAL = 1<<1, + SYMMETRY_CENTRE = 1<<2, + SYMMETRY_GLIDE_HORIZONTAL_QUARTER = 1<<3, + SYMMETRY_GLIDE_VERTICAL_QUARTER = 1<<4, + SYMMETRY_ROTATION_4 = 1<<5, + SYMMETRY_GLIDE_DIAGONAL = 1<<6, + SYMMETRY_MIRROR_VERTICAL = 1<<7, + SYMMETRY_MIRROR_HORIZONTAL = 1<<8, + SYMMETRY_GLIDE_HORIZONTAL = 1<<9, + SYMMETRY_GLIDE_VERTICAL = 1<<10, + SYMMETRY_TRIAD = 1<<11, + SYMMETRY_MIRROR_VERTICAL_QUARTER = 1<<12, + SYMMETRY_MIRROR_HORIZONTAL_QUARTER = 1<<13 +} Symmetry; + +/* Atoms at cell edges and corners need to be duplicated when displaying the model, + but not when calculating structure factors. */ +typedef enum { + MODEL_TARGET_DISPLAY, + MODEL_TARGET_CALCULATION +} ModelTarget; + +/* Planegroups */ +#define PLANEGROUP_P1 (SYMMETRY_IDENTITY) +#define PLANEGROUP_P2 (SYMMETRY_CENTRE) +#define PLANEGROUP_PM_X (SYMMETRY_MIRROR_HORIZONTAL) +#define PLANEGROUP_PM_Y (SYMMETRY_MIRROR_VERTICAL) +#define PLANEGROUP_PG_X (SYMMETRY_GLIDE_HORIZONTAL) +#define PLANEGROUP_PG_Y (SYMMETRY_GLIDE_VERTICAL) +#define PLANEGROUP_CM_X (SYMMETRY_MIRROR_HORIZONTAL | SYMMETRY_GLIDE_HORIZONTAL_QUARTER) +#define PLANEGROUP_CM_Y (SYMMETRY_MIRROR_VERTICAL | SYMMETRY_GLIDE_VERTICAL_QUARTER) +#define PLANEGROUP_P2MM (SYMMETRY_CENTRE | SYMMETRY_MIRROR_HORIZONTAL | SYMMETRY_MIRROR_VERTICAL) +#define PLANEGROUP_P2MG_X (SYMMETRY_CENTRE | SYMMETRY_GLIDE_VERTICAL | SYMMETRY_MIRROR_HORIZONTAL_QUARTER) +#define PLANEGROUP_P2MG_Y (SYMMETRY_CENTRE | SYMMETRY_GLIDE_HORIZONTAL | SYMMETRY_MIRROR_VERTICAL_QUARTER) +#define PLANEGROUP_P2GG (SYMMETRY_CENTRE | SYMMETRY_GLIDE_HORIZONTAL_QUARTER | SYMMETRY_GLIDE_VERTICAL_QUARTER) +#define PLANEGROUP_C2MM (SYMMETRY_CENTRE | SYMMETRY_GLIDE_HORIZONTAL_QUARTER | SYMMETRY_GLIDE_VERTICAL_QUARTER \ + | SYMMETRY_MIRROR_HORIZONTAL | SYMMETRY_MIRROR_VERTICAL) +#define PLANEGROUP_P4 (SYMMETRY_CENTRE | SYMMETRY_ROTATION_4) +#define PLANEGROUP_P4MM (SYMMETRY_CENTRE | SYMMETRY_MIRROR_DIAGONAL | SYMMETRY_ROTATION_4) /* Implies the other diagonal */ +#define PLANEGROUP_P4GM (SYMMETRY_CENTRE | SYMMETRY_ROTATION_4 | SYMMETRY_GLIDE_DIAGONAL) /*| SYMMETRY_GLIDE_HORIZONTAL_QUARTER */ +#define PLANEGROUP_P3 (SYMMETRY_TRIAD) +#define PLANEGROUP_P3M1 (SYMMETRY_TRIAD | SYMMETRY_MIRROR_DIAGONAL) +#define PLANEGROUP_P31M (SYMMETRY_TRIAD | SYMMETRY_MIRROR_HORIZONTAL) +#define PLANEGROUP_P6 (SYMMETRY_TRIAD | SYMMETRY_CENTRE) +#define PLANEGROUP_P6MM (SYMMETRY_TRIAD | SYMMETRY_CENTRE | SYMMETRY_MIRROR_VERTICAL | SYMMETRY_MIRROR_HORIZONTAL) + +typedef enum { + SYMFLAG_NONE = 0, + SYMFLAG_PHASES_KNOWN = 1<<0, + SYMFLAG_PHASES_CALC = 1<<1, +} SymFlags; + +/* Functions */ +extern struct struct_atomicmodel *symmetry_generate_equivalent_atoms(struct struct_atomicmodel *model, size_t j, ModelTarget target); +extern ReflectionList *symmetry_generate_equivalent_reflections(Symmetry sym, signed int h, signed int k, signed int l); +extern ReflectionList symmetry_reduce(ReflectionList *input, Symmetry sym); +extern double symmetry_symmetrise(ReflectionList *input, Symmetry sym, SymFlags flags); /* Returns R_sym */ +extern const char *symmetry_decode(Symmetry sym); +extern Symmetry symmetry_encode(const char *symmetry); +extern void symmetry_centricity(ReflectionList *reflections, unsigned int i, Symmetry sym, SymFlags flags); +extern void symmetry_symmetrise_array(fftw_complex *in, signed int width, signed int height, Symmetry sym); + +#endif /* SYMMETRY_H */ + diff --git a/src/testmain.c b/src/testmain.c new file mode 100644 index 0000000..6aeada6 --- /dev/null +++ b/src/testmain.c @@ -0,0 +1,68 @@ +/* + * main.c + * + * Test harness + * + * (c) 2006-2008 Thomas White + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "data.h" +#include "reflist.h" + +int main(int argc, char *argv[]) { + + int i; + ReflectionList *reflist = NULL; + + if ( data_read(argv[1]) ) { + return 1; + } + reflist = data_getreflections(); + if ( reflist == NULL ) { + printf("data_getreflections() returned NULL\n"); + return 1; + } + + printf("---------------- Initial list\n"); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Adding 200 = 1.5\n"); + reflist_addref_am(reflist, 2, 0, 0, 1.5); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Removing 100\n"); + reflist_delref(reflist, 1, 0, 0); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Trying to remove 000\n"); + reflist_delref(reflist, 0, 0, 0); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + printf("---------------- Trying to add 000 = 5.0\n"); + reflist_addref_am(reflist, 0, 0, 0, 5.0); + printf("%i reflections\n", reflist->n_reflections); + for ( i=0; in_reflections; i++ ) { + printf("%3i %3i %3i %f\n", reflist->refs[i].h, reflist->refs[i].k, reflist->refs[i].l, reflist->refs[i].amplitude); + } + + return 0; + +} + -- cgit v1.2.3