aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am14
-rw-r--r--src/amplitude-r.c86
-rw-r--r--src/amplitude-r.h19
-rw-r--r--src/anneal.h20
-rw-r--r--src/argand.c118
-rw-r--r--src/argand.h26
-rw-r--r--src/cdm.c1202
-rw-r--r--src/cdm.h77
-rw-r--r--src/cflip.c910
-rw-r--r--src/cflip.h28
-rw-r--r--src/clean.c544
-rw-r--r--src/clean.h27
-rw-r--r--src/colwheel.c153
-rw-r--r--src/colwheel.h26
-rw-r--r--src/contourise.c441
-rw-r--r--src/contourise.h21
-rw-r--r--src/correspondence.c73
-rw-r--r--src/correspondence.h20
-rw-r--r--src/data.c162
-rw-r--r--src/data.h34
-rw-r--r--src/displaywindow.c1609
-rw-r--r--src/displaywindow.h76
-rw-r--r--src/dpsynth.c291
-rw-r--r--src/dpsynth.h37
-rw-r--r--src/elements.c86
-rw-r--r--src/elements.h33
-rw-r--r--src/elser.c811
-rw-r--r--src/elser.h25
-rw-r--r--src/geometry.c324
-rw-r--r--src/geometry.h29
-rw-r--r--src/gsf.c669
-rw-r--r--src/gsf.h30
-rw-r--r--src/gtk-symmetry.c243
-rw-r--r--src/gtk-symmetry.h50
-rw-r--r--src/gtk-valuegraph.c234
-rw-r--r--src/gtk-valuegraph.h41
-rw-r--r--src/luzzatti.c91
-rw-r--r--src/luzzatti.h21
-rw-r--r--src/main.c471
-rw-r--r--src/main.h69
-rw-r--r--src/model-display.c411
-rw-r--r--src/model-display.h27
-rw-r--r--src/model-editor.c831
-rw-r--r--src/model-editor.h69
-rw-r--r--src/model.c523
-rw-r--r--src/model.h79
-rw-r--r--src/multislice.c126
-rw-r--r--src/multislice.h25
-rw-r--r--src/normalise.c721
-rw-r--r--src/normalise.h32
-rw-r--r--src/options.c191
-rw-r--r--src/options.h23
-rw-r--r--src/png-file.c125
-rw-r--r--src/png-file.h23
-rw-r--r--src/refine-brent.c138
-rw-r--r--src/refine-brent.h26
-rw-r--r--src/refine-cgrad.c339
-rw-r--r--src/refine-cgrad.h26
-rw-r--r--src/refine-lmder.c439
-rw-r--r--src/refine-lmder.h26
-rw-r--r--src/refine-lsq.c320
-rw-r--r--src/refine-lsq.h26
-rw-r--r--src/refine-rns.c122
-rw-r--r--src/refine-rns.h26
-rw-r--r--src/refine.c359
-rw-r--r--src/refine.h102
-rw-r--r--src/reflist.c287
-rw-r--r--src/reflist.h83
-rw-r--r--src/renderer.c205
-rw-r--r--src/renderer.h31
-rw-r--r--src/statistics.c223
-rw-r--r--src/statistics.h31
-rw-r--r--src/superlattice.c142
-rw-r--r--src/superlattice.h23
-rw-r--r--src/symmetry.c464
-rw-r--r--src/symmetry.h89
-rw-r--r--src/testmain.c68
77 files changed, 16042 insertions, 0 deletions
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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#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; i<reflections->n_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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <png.h>
+#include <math.h>
+#include <stdlib.h>
+
+#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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ data[y + height*x][0] = 0;
+ data[y + height*x][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_reflections; i++ ) {
+ double am = reflections->refs[i].amplitude;
+ if ( am > max ) max = am;
+ }
+ mag = (width/2) / max;
+
+ for ( i=0; i<reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
+#include <gsl/gsl_sf_bessel.h>
+#include <assert.h>
+#include <stdint.h>
+#include <fftw3.h>
+
+#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; i<reflections->n_reflections; i++ ) {
+ reflections->refs[i].parent_index = i;
+ }
+
+ for ( i=1; i<reflections->n_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; j<reflections->n_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; i<triplet_copy->n_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; i<triplet_copy->n_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; p<strongest_reflections->n_reflections; p++ ) {
+ for ( q=1; q<strongest_reflections->n_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; i<reflections->n_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; t<triplet_list->n_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; p<equivalents->n_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; i<reflections->n_reflections; i++ ) {
+ reflections->refs[i].phase_calc_set = 0;
+ }
+
+ /* Assign random phases to the basis set */
+ for ( i=1; i<basis_list->n_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; p<equivalents->n_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; i<reflections->n_reflections; i++ ) {
+ reflections->refs[i].phase_calc_set = 0;
+ }
+
+ /* Assign phases to the basis set */
+ for ( i=1; i<basis_list->n_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; p<equivalents->n_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; i<strongest_reflections->n_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; t<triplet_list->n_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; p<equivalents->n_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; h<data_width(); h++ ) {
+ for ( k=0; k<data_height(); k++ ) {
+ cdm_in[k+data_height()*h][0] = 0;
+ cdm_in[k+data_height()*h][1] = 0;
+ }
+ }
+
+ /* Step through each basis set in turn, and expand phases */
+ reflections = cdm->reflections;
+ 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; i<reflections->n_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<data_height(); y++ ) {
+ for ( x=0; x<data_width(); x++ ) {
+ double re = cdm_out[y+data_height()*x][0];
+ double im = cdm_out[y+data_height()*x][1];
+ double am = sqrt(re*re + im*im);
+ if ( am > max ) max = am;
+ }
+ }
+ unflatness = 0; entropy = 0;
+ for ( y=0; y<data_height(); y++ ) {
+ for ( x=0; x<data_width(); x++ ) {
+ double re = cdm_out[y+data_height()*x][0];
+ double im = cdm_out[y+data_height()*x][1];
+ double am = sqrt(re*re + im*im);
+ unflatness += (am/max) * (am/max) * (am/max) * (am/max);
+ entropy += (am/max) * log(am/max);
+ }
+ }
+ unflatness /= (data_height() * data_width());
+ entropy = -entropy;
+ sol->unflatness = 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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef CDM_H
+#define CDM_H
+
+#include <stdint.h>
+
+#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 <taw27@cam.ac.uk>
+ * (c) 2008 Alex Eggeman <ase25@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <fftw3.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#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; i<listout->n_reflections; i++ ) {
+ listout->refs[i].parent_index = i;
+ }
+
+ for ( i=1; i<listout->n_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; p<strongest_reflections->n_reflections; p++ ) {
+ for ( q=1; q<strongest_reflections->n_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; i<strongest_reflections->n_reflections; i++ ) {
+
+ signed int h = strongest_reflections->refs[i].h;
+ signed int k = strongest_reflections->refs[i].k;
+
+ for ( t=0; t<triplet_list->n_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; i<strongest_reflections->n_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; i<reflections->n_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; x<width; x++ ) {
+ for ( y=0; y<((height/2)+1); y++ ) {
+ cflip_dialog->cf_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; i<reflections->n_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ re = cflip_dialog->cf_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ if ( cflip_dialog->cf_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ re = cflip_dialog->cf_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ entropy += (am/mean) * log (am/mean);
+ }
+ }
+ entropy = -entropy;
+ printf("step %i; Entropy : %f, Negativity : %f \n", cflip_dialog->n_iterations, entropy, neg);*/
+
+
+ for ( y=0; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+
+ if ( !cflip_dialog->cf_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; x<width; x++ ) {
+ for ( y=0; y<((height/2)+1); y++ ) {
+ cflip_dialog->cf_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; i<listout->n_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; i<reflections->n_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; i<listout->n_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ re = cflip_dialog->cf_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ re = cflip_dialog->cf_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ if ( cflip_dialog->cf_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; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+
+ if ( !cflip_dialog->cf_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; i<reflections->n_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), "<span weight=\"bold\">Threshold</span>");
+ 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 <taw27@cam.ac.uk>
+ * (c) 2008 Alexander Eggeman <ase25@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+#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; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+
+ signed int xd, yd;
+
+ xd = (((signed)x + width/2) % width)-width/2;
+ yd = (((signed)y + height/2) % height)-height/2;
+
+ in[y+height*x][0] = (ax*ay)/((ax*ax + xd*xd) + (ay*ay + yd*yd));
+ in[y+height*x][1] = 0;
+
+ }
+ }
+ plan = fftw_plan_dft_2d(width, height, in, clean_beam_reciprocal, FFTW_BACKWARD, FFTW_MEASURE);
+ fftw_execute(plan);
+ fftw_destroy_plan(plan);
+
+ /* Transform to reciprocal space, and scale */
+ plan = fftw_plan_dft_2d(width, height, cleaned, in, FFTW_BACKWARD, FFTW_MEASURE);
+ fftw_execute(plan);
+ fftw_destroy_plan(plan);
+ for ( h=0; h<width; h++ ) {
+ for ( k=0; k<height; k++ ) {
+ in[k+height*h][0] = in[k+height*h][0] / (width*height); /* Real */
+ in[k+height*h][1] = in[k+height*h][1] / (width*height); /* Imaginary */
+ }
+ }
+
+ /* Perform the convolution */
+ for ( h=-width/2; h<=width/2; h++ ) {
+ for ( k=-height/2; k<=height/2; k++ ) {
+
+ double re, im, am, ph;
+ unsigned int hl, kl;
+
+ if ( h < 0 ) hl = width+h; else hl = h;
+ if ( k < 0 ) kl = height+k; else kl = k;
+ re = in[kl+height*hl][0];
+ im = in[kl+height*hl][1];
+ am = sqrt(re*re + im*im);
+ ph = atan2(im, re);
+
+ am = am*clean_beam_reciprocal[kl+height*hl][0];
+ in[kl+height*hl][0] = am*cos(ph);
+ in[kl+height*hl][1] = am*sin(ph);
+
+ }
+ }
+
+ /* Transform back to real space */
+ planr = fftw_plan_dft_2d(width, height, in, cleaned, FFTW_BACKWARD, FFTW_MEASURE);
+ fftw_execute(planr);
+ fftw_destroy_plan(planr);
+
+ fftw_free(in);
+ fftw_free(clean_beam_reciprocal);
+
+}
+
+/* Calculate and return the aperture function */
+static fftw_complex *clean_aperture(ReflectionList *reflections, signed int width, signed int height) {
+
+ fftw_complex *aperture;
+ fftw_plan plan;
+ double aperture_max;
+ fftw_complex *delta_array;
+ unsigned int i;
+ unsigned int x, y;
+
+ delta_array = fftw_malloc(width*height*sizeof(fftw_complex));
+ for ( i=1; i<reflections->n_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<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ double re, im, am;
+ re = aperture[y+height*x][0]; im = aperture[y+height*x][1];
+ am = sqrt(re*re + im*im);
+ if ( am > aperture_max ) aperture_max = am;
+ }
+ }
+
+ /* Normalise the aperture function */
+ for ( x=0; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ aperture[y+height*x][0] = aperture[y+height*x][0] / aperture_max;
+ aperture[y+height*x][1] = aperture[y+height*x][1] / aperture_max;
+ }
+ }
+
+ return aperture;
+
+}
+
+static void clean_execute(fftw_complex *input, ReflectionList *reflections, double tau, double cutoff, int positive, int convolve, double ax, double ay) {
+
+ fftw_complex *aperture;
+ fftw_complex *dirty;
+ signed int width = data_width();
+ signed int height = data_height();
+
+ unsigned int x, y;
+ fftw_complex *cleaned;
+ unsigned int npx = 0;
+
+ double max_am;
+
+ aperture = clean_aperture(reflections, width, height);
+
+ /* The aperture function won't necessarily be real: if there was no measured reflection -h,-k,-l for a corresponding
+ h,k,l, Friedel's Law will not apply to the delta array. */
+
+ /* Initialise 'clean' array */
+ printf("CL: Loop gain = %f, Cutoff = %f, %s\n", tau, cutoff, positive?"Constrained positive":"Negativity allowed");
+ cleaned = fftw_malloc(width*height*sizeof(fftw_complex));
+ for ( x=0; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ cleaned[y+height*x][0] = 0;
+ cleaned[y+height*x][1] = 0;
+ }
+ }
+
+ /* Initialise 'dirty' array */
+ dirty = malloc(width*height*sizeof(fftw_complex));
+ memcpy(dirty, input, width*height*sizeof(fftw_complex));
+
+ /* Iterate */
+ do {
+
+ double max_re = 0;
+ double max_im = 0;
+ unsigned int max_x, max_y;
+
+ /* Find largest modulus, record its location */
+ max_am = 0; max_x = 0; max_y = 0;
+ for ( x=0; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+
+ double re, im, am;
+ re = dirty[y+height*x][0]; im = dirty[y+height*x][1];
+ am = sqrt(re*re + im*im);
+
+ if ( fabs(am) > 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<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ unsigned int fx, fy;
+ fx = (x + max_x) % width;
+ fy = (y + max_y) % height;
+ dirty[fy+height*fx][0] -= tau * max_am
+ * ((aperture[y+height*x][0]*max_re - aperture[y+height*x][1]*max_im) / max_am);
+ dirty[fy+height*fx][1] -= tau * max_am
+ * ((aperture[y+height*x][0]*max_im - aperture[y+height*x][1]*max_re) / max_am);
+ }
+ }
+
+ /* Repeat */
+ npx++;
+
+ } while ( fabs(max_am) > 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<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ double re, im, am;
+ re = cleaned[y+height*x][0]; im = cleaned[y+height*x][1];
+ am = sqrt(re*re + im*im);
+ if ( fabs(am) > 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<width/2; i++ ) {
+
+ double re, im, am, next_am;
+
+ re = aperture[0+height*i][0];
+ im = aperture[0+height*i][1];
+ am = sqrt(re*re + im*im);
+ if ( re < 0 ) am = -am;
+ fprintf(fh, "%i %f\n", i, am);
+
+ if ( i+1<width/2 ) {
+ re = aperture[0+height*(i+1)][0];
+ im = aperture[0+height*(i+1)][1];
+ next_am = sqrt(re*re + im*im);
+ } else {
+ next_am = INFINITY;
+ }
+
+ if ( (am > 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<height/2; i++ ) {
+
+ double re, im, am, next_am;
+
+ re = aperture[i+height*0][0];
+ im = aperture[i+height*0][1];
+ am = sqrt(re*re + im*im);
+ if ( re < 0 ) am = -am;
+ fprintf(fh, "%i %f\n", i, am);
+
+ if ( i+1<width/2 ) {
+ re = aperture[(i+1)+height*0][0];
+ im = aperture[(i+1)+height*0][1];
+ next_am = sqrt(re*re + im*im);
+ } else {
+ next_am = INFINITY;
+ }
+
+ if ( (am > 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), "a<sub>x</sub> = ");
+ 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), "a<sub>y</sub> = ");
+ 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 <taw27@cam.ac.uk>
+ *
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef CLEAN_H
+#define CLEAN_H
+
+#include <gtk/gtk.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <stdlib.h>
+#include <fftw3.h>
+
+#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<COLWHEEL_SIZE; x++ ) {
+ for ( y=0; y<COLWHEEL_SIZE; y++ ) {
+ colwheel[y + COLWHEEL_SIZE*x][0] = 0;
+ colwheel[y + COLWHEEL_SIZE*x][1] = 0;
+ }
+ }
+
+ /* Plot out a disc containing smoothly varying amplitude and phase */
+ for ( am=0.0; am<1.0; am+=(1.0/COLWHEEL_SIZE) ) {
+ for ( ph=0.0; ph<2.0*M_PI; ph+=1.0/(2.0*M_PI*(double)COLWHEEL_SIZE) ) {
+ x = (unsigned int)(COLWHEEL_HALF + (COLWHEEL_HALF*am*cos(ph)));
+ y = (unsigned int)(COLWHEEL_HALF + (COLWHEEL_HALF*am*sin(ph)));
+ colwheel[(COLWHEEL_SIZE-1-y) + COLWHEEL_SIZE*(COLWHEEL_SIZE-1-x)][0] = am*cos(ph);
+ colwheel[(COLWHEEL_SIZE-1-y) + COLWHEEL_SIZE*(COLWHEEL_SIZE-1-x)][1] = am*sin(ph);
+ }
+ }
+
+ /* Draw a line to remind the user where zero phase is */
+ for ( x=COLWHEEL_HALF+1; x<COLWHEEL_SIZE; x++ ) {
+ colwheel[COLWHEEL_HALF + COLWHEEL_SIZE*(COLWHEEL_SIZE-1-x)][0] = 0;
+ colwheel[COLWHEEL_HALF + COLWHEEL_SIZE*(COLWHEEL_SIZE-1-x)][1] = 0;
+ }
+
+ colwheel_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(colwheel_window), "Colour Wheel");
+ colwheel_pixbuf = displaywindow_render_pixbuf(colwheel, 1, COLWHEEL_SIZE, COLWHEEL_SIZE, M_PI_2, 1, 1);
+ free(colwheel);
+ colwheel_pixmap_widget = gtk_image_new_from_pixbuf(colwheel_pixbuf);
+ gtk_container_add(GTK_CONTAINER(colwheel_window), colwheel_pixmap_widget);
+ gtk_widget_show_all(colwheel_window);
+
+ return 0;
+
+}
+
+double colwheel_blue(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;
+ 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 <taw27@cam.ac.uk>
+ *
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef COLWHEEL_H
+#define COLWHEEL_H
+
+#include <gtk/gtk.h>
+
+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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fftw3.h>
+#include <math.h>
+
+#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; xn<width_n; xn++ ) {
+
+ double re, im;
+
+ if ( !first ) griwrite(gri, " "); else first = 0;
+
+ re = cxar.re[xn+width_n*yn];
+ im = cxar.im[xn+width_n*yn];
+
+ if ( (vals == VALUES_MODULUS)
+ || (vals == VALUES_MODULUS_SIGN) ) {
+ double val = sqrt(re*re + im*im);
+ if ( (vals == VALUES_MODULUS_SIGN)
+ && (re < 0) ) {
+ snprintf(tmp, 1023, "-%f", val);
+ } else {
+ snprintf(tmp, 1023, "%f", val);
+ }
+ } else if ( vals == VALUES_REAL ) {
+ double val = re;
+ snprintf(tmp, 1023, "%f", val);
+ } else if ( vals == VALUES_IMAGINARY ) {
+ double val = im;
+ snprintf(tmp, 1023, "%f", val);
+ }
+ griwrite(gri, tmp);
+
+ }
+ griwrite(gri, "\n");
+
+ }
+
+ griwrite(gri, "set line width 1.5\n");
+ snprintf(tmp, 1023, "draw contour 0 %f %f unlabelled\n",
+ maxval, maxval/ncont);
+ griwrite(gri, tmp);
+ griwrite(gri, "set line width 1.0\n");
+ griwrite(gri, "set color blue\n");
+ griwrite(gri, "set dash 7\n");
+ snprintf(tmp, 1023, "draw contour %f %f %f unlabelled\n",
+ -maxval, -maxval/ncont, maxval/ncont);
+ griwrite(gri, tmp);
+
+ /* Mark the atomic coordinates if appropriate */
+ if ( model_marks ) {
+
+ AtomicModel *model;
+ int i;
+
+ griwrite(gri, "set color red\n");
+ griwrite(gri, "set dash 0\n");
+ griwrite(gri, "set symbol size 0.5\n");
+ griwrite(gri, "set line width symbol 0.8\n");
+
+ model = model_get_current();
+
+ for ( i=0; i<model->n_atoms; i++ ) {
+
+ double p, q;
+ int xc, yc;
+
+ p = model->atoms[i].x;
+ q = model->atoms[i].y;
+
+ for ( xc=0; xc<nx; xc++ ) {
+ for ( yc=0; yc<ny; yc++ ) {
+ double x, y;
+ x = renderer_map_x((p+xc)*(double)width,
+ (q+yc)*(double)height,
+ width, height, gamma, nx, ny);
+ x *= scale;
+ y = renderer_map_y((p+xc)*(double)width,
+ (q+yc)*(double)height,
+ width, height, gamma, nx, ny);
+ y *= scale;
+ snprintf(tmp, 1023,
+ "draw symbol 1 at %f %f cm\n",
+ 1.0+x, 1.0+y);
+ griwrite(gri, tmp);
+ }
+ }
+
+ }
+
+ }
+
+ if ( pclose(gri) == -1 ) {
+ fprintf(stderr, "gri returned an error code.");
+ return;
+ }
+
+ pid = fork();
+ if ( (pid != 0) && (pid != -1) ) {
+ printf("CO: Invoking ps2pdf...\n");
+ } else {
+ if ( pid == -1 ) {
+ fprintf(stderr, "fork() failed.\n");
+ return;
+ } else {
+ /* Forked successfully, child process */
+ system("ps2pdf -sPAPERSIZE=a4 synth2d.ps");
+ _exit(0);
+ }
+ }
+
+ waitpid(pid, &status, 0);
+
+ pid = fork();
+ if ( (pid != 0) && (pid != -1) ) {
+ printf("CO: Invoking evince...\n");
+ } else {
+ if ( pid == -1 ) {
+ fprintf(stderr, "fork() failed.\n");
+ return;
+ } else {
+ /* Forked successfully, child process */
+ system("evince synth2d.pdf");
+ _exit(0);
+ }
+ }
+
+ free(cxar.re);
+ free(cxar.im);
+}
+
+static gint contourise_dialog_close(GtkWidget *window, gint response,
+ ContourDialog *cd)
+{
+ int error = 0;
+
+ if ( response == GTK_RESPONSE_OK ) {
+
+ ContourValues vals;
+ int ncont = 0;
+ const char *ncont_s;
+
+ vals = VALUES_NONE;
+ if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ cd->rad_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),
+ "<span style=\"italic\" weight=\"light\">"
+ "What do you want to plot?</span>");
+ 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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#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; i<reflections->n_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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fftw3.h>
+#include <string.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fftw3.h>
+
+#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 <taw27@cam.ac.uk>",
+ 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 <taw27@cam.ac.uk>");
+ 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 <taw27@cam.ac.uk>\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<height_n; yn++ ) {
+ for ( xn=0; xn<width_n; xn++ ) {
+
+ double re, im, am, ph;
+
+ re = cxar.re[xn+width_n*yn];
+ im = cxar.im[xn+width_n*yn];
+
+ am = sqrt(re*re + im*im) / brightness;
+ ph = atan2(im, re);
+ if ( am > 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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ double re = displaywindow_out[k+displaywindow_height*h][0];
+ double im = displaywindow_out[k+displaywindow_height*h][1];
+ re_t += re*re;
+ im_t += im*im;
+ }
+ }
+ printf("Real total = %f\nImaginary total = %f\n", re_t, im_t); */
+
+}
+
+/* Display the Patterson transform with E^2-1 based on the given reflections */
+void displaywindow_show_pattersone(ReflectionList *reflections) {
+
+ signed int h, k;
+ unsigned int i;
+ GtkAction *action;
+ double sigma = 0; /* Actually this is "Epsilon times sigma" */
+ unsigned int n = 0;
+ double dev = 0;
+
+ for ( h=0; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=1; i<reflections->n_reflections; i++ ) {
+ if ( reflections->refs[i].amplitude > 0 ) {
+ sigma += reflections->refs[i].amplitude * reflections->refs[i].amplitude;
+ n++;
+ }
+ }
+ sigma = sigma / n;
+
+ for ( i=1; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ model_reflections = model_calculate_f(NULL, NULL, 0);
+
+ for ( i=0; i<model_reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ model_reflections = model_calculate_f(NULL, NULL, 1);
+
+ for ( i=0; i<model_reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ for ( i=0; i<reflections->n_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; h<displaywindow_width; h++ ) {
+ for ( k=0; k<displaywindow_height; k++ ) {
+ displaywindow_in[k+displaywindow_height*h][0] = 0;
+ displaywindow_in[k+displaywindow_height*h][1] = 0;
+ }
+ }
+
+ model_reflections = model_calculate_f(NULL, NULL, 0);
+
+ for ( i=0; i<model_reflections->n_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...", "<Ctrl>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<displaywindow_width; x++ ) {
+ for ( y=0; y<displaywindow_height; y++ ) {
+ double re, im, am;
+ re = out[y + displaywindow_height*x][0];
+ im = out[y + displaywindow_height*x][1];
+ am = sqrt(re*re + im*im);
+ if ( fabs(am) > 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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef DISPLAYWINDOW_H
+#define DISPLAYWINDOW_H
+
+#include <gtk/gtk.h>
+#include <fftw3.h>
+
+#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 <taw27@cam.ac.uk>
+ * (c) 2007-2008 Alex Eggeman <ase25@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <png.h>
+#include <math.h>
+#include <stdlib.h>
+
+#if HAVE_CAIRO
+#include <cairo.h>
+#endif
+
+#include <inttypes.h>
+
+#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; y<height/2; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ uint32_t word;
+ word = dataw[x+width*y];
+ dataw[x+width*y] = dataw[x+width*(height-1-y)];
+ dataw[x+width*(height-1-y)] = word;
+ }
+ }
+
+}
+
+static void dpsynth_free_data(guchar *image_data, cairo_t *dctx)
+{
+ cairo_surface_finish(cairo_get_target(dctx));
+ cairo_destroy(dctx);
+}
+#endif
+
+static GdkPixbuf *dpsynth_render_pixbuf(DPSynthWindow *dpsynth,
+ ReflectionList *reflections)
+{
+#ifdef HAVE_CAIRO
+ cairo_surface_t *surface;
+ cairo_t *dctx;
+ GdkPixbuf *pixbuf;
+ unsigned char *data;
+ size_t i;
+ double max_u, max_v, max_res, max_intensity, scale;
+ double sep_u, sep_v, max_r;
+ double as, bs, theta;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ dpsynth->width, 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; i<reflections->n_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; i<reflections->n_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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2D - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2D - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <fftw3.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#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; i<size/sizeof(fftw_complex); i++ ) {
+ a[i][0] *= b;
+ a[i][1] *= b;
+ }
+}
+
+static void elser_grid_add(fftw_complex *a, fftw_complex *b, size_t size) {
+ size_t i;
+ for ( i=0; i<size/sizeof(fftw_complex); i++ ) {
+ a[i][0] += b[i][0];
+ a[i][1] += b[i][1];
+ }
+}
+
+static void elser_grid_subtract(fftw_complex *a, fftw_complex *b, size_t size) {
+ size_t i;
+ for ( i=0; i<size/sizeof(fftw_complex); i++ ) {
+ a[i][0] -= b[i][0];
+ a[i][1] -= b[i][1];
+ }
+}
+
+static void elser_update_itnum(ElserDialog *elser_dialog) {
+
+ char tmp[64];
+
+ snprintf(tmp, 64, "<span weight=\"bold\">Iteration number: </span>%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; dx<disp_width; dx++ ) {
+ for ( dy=0; dy<disp_height; dy++ ) {
+ unsigned int gx, gy;
+ gx = dx / sx; gy = dy / sy;
+ display[dy + disp_height*dx][0] = grid[gy + grid_height*gx][0];
+ display[dy + disp_height*dx][1] = grid[gy + grid_height*gx][1];
+ }
+ }
+
+}
+
+static gboolean elser_update_display(gpointer data) {
+
+ ElserDialog *elser_dialog = data;
+
+ displaywindow_switchview();
+ elser_update_itnum(elser_dialog);
+
+ gtk_value_graph_set_data(GTK_VALUE_GRAPH(elser_dialog->graph), 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<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+
+ double re, im, am, ph;
+
+ re = f[y + height*x][0];
+ im = f[y + height*x][1];
+ am = sqrt(re*re + im*im);
+ ph = atan2(im, re);
+ ph -= ph_offs;
+
+ f[y + height*x][0] = am * cos(ph);
+ f[y + height*x][1] = am * sin(ph);
+
+ }
+ }
+
+}
+#endif
+
+/* Find N atom supports of size 1+2ssx,1+2ssy in array f */
+static ElserPeak *elser_projection_atomicity_findatoms(fftw_complex *f, unsigned int width, unsigned int height, unsigned int N,
+ unsigned int ssx, unsigned int ssy) {
+
+ ElserPeak *peaks;
+ unsigned int i, x, y;
+ unsigned int positivity = 1;
+
+ peaks = malloc(sizeof(ElserPeak)*N);
+
+ for ( i=0; i<N; i++ ) {
+
+ unsigned int max_x, max_y;
+ double max;
+
+ max = 0; max_x = 0; max_y = 0;
+ for ( y=0; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ double re, im, am;
+ re = f[y + height*x][0];
+ im = f[y + height*x][1];
+ am = sqrt(re*re + im*im);
+ if ( fabs(am) > fabs(max) ) {
+ unsigned int p, allowed;
+ allowed = 1;
+ for ( p=0; p<i; p++ ) {
+ if ( (abs(x - peaks[p].x) < ssx) || (abs(y - peaks[p].y) < ssy) ) allowed = 0;
+ }
+ if ( positivity && (re < 0) ) allowed = 0;
+ if ( allowed ) {
+ max = am;
+ max_x = x; max_y = y;
+ }
+ }
+ }
+ }
+
+ peaks[i].x = max_x;
+ peaks[i].y = max_y;
+
+ }
+
+ /* Fixed support for SnOx data */
+// peaks[0].x = 0; peaks[0].y = 0;
+// peaks[1].x = 0; peaks[1].y = 20;
+// peaks[2].x = 21; peaks[2].y = 0;
+// peaks[3].x = 21; peaks[3].y = 20;
+// peaks[4].x = 42; peaks[4].y = 0;
+// peaks[5].x = 42; peaks[5].y = 20;
+
+ return peaks;
+
+}
+
+/* Find "atoms" in the grid and eliminate everything else */
+static void elser_projection_atomicity(fftw_complex *f, ElserDialog *elser_dialog) {
+
+ int width, height;
+ unsigned int N, i; /* Number of atoms sought */
+ int ssx, ssy, css;
+ ElserPeak *peaks;
+ fftw_complex *grid_copy;
+
+ width = elser_dialog->width;
+ 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; i<N; i++ ) {
+ signed int x, y;
+ for ( x=(signed)peaks[i].x-ssx; x<=(signed)peaks[i].x+ssx; x++ ) {
+ for ( y=(signed)peaks[i].y-ssy; y<=(signed)peaks[i].y+ssy; y++ ) {
+ signed int xd, yd;
+ if ( ((x-peaks[i].x)*(x-peaks[i].x))+((y-peaks[i].y)*(y-peaks[i].y)) <= css*css ) {
+ xd = x % width;
+ yd = y % height;
+ if ( xd < 0 ) xd += width;
+ if ( yd < 0 ) yd += height;
+ grid_copy[yd + height*xd][0] = f[yd + height*xd][0];
+ grid_copy[yd + height*xd][1] = f[yd + height*xd][1];
+ }
+ }
+ }
+ }
+ free(peaks);
+ memcpy(f, grid_copy, elser_dialog->array_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; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ elser_dialog->in[y+height*x][0] /= (width*height);
+ elser_dialog->in[y+height*x][1] /= (width*height);
+ }
+ }
+
+ /* Apply constraints */
+ for ( i=1; i<elser_dialog->reflections->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; i<array_size/sizeof(fftw_complex); i++ ) {
+ double re, im;
+ re = delta[i][0];
+ im = delta[i][1];
+ total += re*re + im*im;
+ }
+ norm = sqrt(total);
+
+ //printf("||e||=%f\n", norm);
+
+ return norm;
+
+}
+
+static void *elser_work(void *data) {
+
+ ElserDialog *elser_dialog;
+ unsigned int width, height;
+ fftw_complex *f1;
+ fftw_complex *f2;
+
+ elser_dialog = data;
+ elser_dialog->running = 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; i<elser_dialog->array_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), "<span style=\"italic\">(uninitialised)</span>");
+ 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; i<reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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; i<reflections->n_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; i<reflections->n_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<sup>-1</sup>");
+ 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<sup>-1</sup>");
+ 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 <taw27@cam.ac.uk>
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ * (c) 2008 Alex Eggeman <ase25@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <fftw3.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#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; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ xc += (x+1) * out[y+height*x][0];
+ yc += (y+1) * out[y+height*x][0];
+ total_int += out[y+height*x][0];
+ }
+ }
+ xc = xc / total_int; xc--;
+ yc = yc / total_int; yc--;
+
+ /* Calculate displacements needed to put centroid at centre... */
+ xs = (width/2) - xc;
+ ys = (height/2) - yc;
+
+ out_copy = malloc(width*height*sizeof(fftw_complex));
+ memcpy(out_copy, out, width*height*sizeof(fftw_complex));
+ for ( x=0; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ signed int xn = x-xs;
+ signed int yn = y-ys;
+ if ( xn < 0 ) { xn = width+xn; }
+ if ( yn < 0 ) { yn = height+yn; }
+ out[y+height*x][0] = out_copy[yn+height*xn][0];
+ out[y+height*x][1] = out_copy[yn+height*xn][1];
+ }
+ }
+
+ memcpy(out_copy, drive, width*height*sizeof(fftw_complex));
+ for ( x=0; x<width; x++ ) {
+ for ( y=0; y<height; y++ ) {
+ signed int xn = x-xs;
+ signed int yn = y-ys;
+ if ( xn < 0 ) { xn = width+xn; }
+ if ( yn < 0 ) { yn = height+yn; }
+ drive[y+height*x][0] = out_copy[yn+height*xn][0];
+ drive[y+height*x][1] = out_copy[yn+height*xn][1];
+ }
+ }
+
+ free(out_copy);
+
+}
+
+void gsf_mask(ReflectionList *reflections, fftw_complex *in, unsigned int width, unsigned int height) {
+
+ unsigned int i;
+ fftw_complex *in_new = malloc(width*height*sizeof(fftw_complex));
+ bzero(in_new, width*height*sizeof(fftw_complex));
+
+ for ( i=1; i<reflections->n_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; x<width; x++ ) {
+ for ( y=0; y<((height/2)+1); y++ ) {
+ gsf_in[y+((height/2)+1)*x][0] = gsf_in[y+((height/2)+1)*x][0] / (width*height); /* Real */
+ gsf_in[y+((height/2)+1)*x][1] = gsf_in[y+((height/2)+1)*x][1] / (width*height); /* Imaginary */
+ }
+ }
+
+ /* Impose observed intensities */
+ for ( i=0; i<gsf_reflections->n_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<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ if ( gsf_drive[y+height*x][0] > 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<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ double am = sqrt(gsf_out[y+height*x][0] * gsf_out[y+height*x][0] + gsf_out[y+height*x][1] * gsf_out[y+height*x][1]);
+ if ( (!gsf_threshsup) || (am > 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<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ gsf_support[y+height*x] = 1;
+ }
+ }
+
+ }
+
+ /* Impose non-negativity, sharpen peaks and apply feedback */
+ mean = 0; mean_n = 0; top =0;
+ for ( y=0; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ double re, im, am;
+ re = gsf_drive[y+height*x][0];
+ im = gsf_drive[y+height*x][1];
+ am = sqrt(re*re + im*im);
+ if ( am > 0 ) {
+ mean += am;
+ mean_n++;
+ if ( am > top ) top = am;
+
+ }
+ }
+ }
+
+ mean = mean / mean_n;
+ for ( y=0; y<height; y++ ) {
+ for ( x=0; x<width; x++ ) {
+
+ if ( !gsf_support[y+height*x] ) {
+
+ gsf_drive[y+height*x][0] = gsf_drive_old[y+height*x][0] - gsf_hio_beta*gsf_drive[y+height*x][0];
+ gsf_drive[y+height*x][1] = gsf_drive_old[y+height*x][1] - gsf_hio_beta*gsf_drive[y+height*x][1];
+
+ } else {
+
+ if ( gsf_atomicity ) {
+ double re, im, am, ph;
+ re = gsf_drive[y+height*x][0];
+ im = gsf_drive[y+height*x][1];
+ am = sqrt(re*re + im*im);
+ ph = atan2(im, re);
+ am = am * log(am/mean);
+ gsf_drive[y+height*x][0] = am*cos(ph);
+ gsf_drive[y+height*x][1] = am*sin(ph);
+ }
+
+ }
+
+ }
+ }
+
+}
+
+static gint gsf_atomicity_toggled() {
+ gsf_atomicity = 1-gsf_atomicity;
+ return 0;
+}
+
+static gint gsf_zerofzero_toggled() {
+ gsf_zerofzero = 1-gsf_zerofzero;
+ return 0;
+}
+
+static gint gsf_antistag_toggled() {
+ gsf_antistag = 1-gsf_antistag;
+ return 0;
+}
+
+static gint gsf_centroid_toggled() {
+ gsf_centroid = 1-gsf_centroid;
+ return 0;
+}
+
+GtkWidget *gsfw_fracsup_phi_label;
+static gint gsf_fracsup_toggled(GtkWidget *widget, GtkWidget *gsfw_fracsup_phi) {
+ gsf_fracsup = 1-gsf_fracsup;
+ 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);
+ }
+ return 0;
+}
+
+static gint gsf_fracsup_phichanged(GtkWidget *gsfw_fracsup_phi) {
+ gsf_fracsup_phi = gtk_range_get_value(GTK_RANGE(gsfw_fracsup_phi));
+ return 0;
+}
+
+
+GtkWidget *gsfw_hio_beta_label;
+
+static gint gsf_hio_betachanged(GtkWidget *gsfw_hio_beta) {
+ gsf_hio_beta = gtk_range_get_value(GTK_RANGE(gsfw_hio_beta));
+ return 0;
+}
+
+GtkWidget *gsfw_threshsup_delta_label;
+static gint gsf_threshsup_toggled(GtkWidget *widget, GtkWidget *gsfw_threshsup_delta) {
+ gsf_threshsup = 1-gsf_threshsup;
+ 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);
+ }
+ return 0;
+}
+
+static gint gsf_threshsup_deltachanged(GtkWidget *gsfw_threshsup_delta) {
+ gsf_threshsup_delta = gtk_range_get_value(GTK_RANGE(gsfw_threshsup_delta));
+ return 0;
+}
+
+void gsf_finalise() {
+
+ free(gsf_drive); gsf_drive = NULL;
+ free(gsf_drive_old); gsf_drive_old = NULL;
+ free(gsf_support); gsf_support = NULL;
+ free(gsf_in); gsf_in = NULL;
+ free(gsf_reflections); gsf_reflections = NULL;
+ /* Don't free gsf_out, since it is now backing up the display */
+
+ fftw_destroy_plan(gsf_plan_i2d);
+ fftw_destroy_plan(gsf_plan_d2i);
+
+}
+
+
+static void gsf_window_close(GtkWidget *widget, gint arg1, gpointer data) {
+
+ gsf_window_open = 0;
+ gsf_running = 0;
+ gtk_widget_destroy(widget);
+
+ gsf_finalise();
+
+}
+
+static void gsf_update_iterations() {
+
+ char *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);
+
+}
+
+void gsf_reset(ReflectionList *reflections) {
+
+ unsigned int i;
+ unsigned int width = data_width();
+ unsigned int height = data_height();
+
+ gsf_n_iterations = 0;
+ gsf_update_iterations();
+
+ for ( i=0; i<gsf_reflections->n_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), "<span weight=\"bold\">Dynamic Support</span>");
+ 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), "<span weight=\"bold\">Points Outside Support</span>");
+ 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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#include <gtk/gtk.h>
+
+#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(), &gtk_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifndef GTKSYMMETRY_H
+#define GTKSYMMETRY_H
+
+#include <gtk/gtk.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <math.h>
+
+#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; i<vg->n; 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(), &gtk_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<n; i++ ) {
+ if ( data[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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifndef GTKVALUEGRAPH_H
+#define GTKVALUEGRAPH_H
+
+#include <gtk/gtk.h>
+
+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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#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; i<reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <fftw3.h>
+#include <string.h>
+#include <math.h>
+#include <gsl/gsl_errno.h>
+#include <assert.h>
+
+#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; i<main_reflections->n_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; i<calc->n_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; i<main_reflections->n_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; i<main_reflections->n_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 <data file>\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; i<main_reflections->n_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; i<main_reflections->n_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; i<main_reflections->n_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; i<main_reflections->n_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; i<main_reflections->n_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; i<main_reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <gtk/gtk.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <inttypes.h>
+
+#if HAVE_CAIRO
+#include <cairo.h>
+#include <cairo-pdf.h>
+#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; i<model->n_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; j<equivalents->n_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; xp<nx; xp++ ) {
+ for ( yp=0; yp<ny; yp++ ) {
+ cairo_new_path(dctx);
+
+ if ( model->atoms[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; x<nx; x++ ) {
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_diad(dctx, model_oblique_x(0.0, 0.0, height, gamma, ny),
+ h-model_oblique_y(0.0, 0.0, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width/2, 0.0, height, gamma, ny),
+ h-model_oblique_y((x*width)+width/2, 0.0, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width, 0.0, height, gamma, ny),
+ h-model_oblique_y((x*width)+width, 0.0, gamma));
+ model_display_draw_diad(dctx, model_oblique_x(0.0, (y*height)+height/2, height, gamma, ny),
+ h-model_oblique_y(0.0, (y*height)+height/2, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width/2, (y*height)+height/2, height, gamma, ny),
+ h-model_oblique_y((x*width)+width/2, (y*height)+height/2, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width, (y*height)+height/2, height, gamma, ny),
+ h-model_oblique_y((x*width)+width, (y*height)+height/2, gamma));
+ model_display_draw_diad(dctx, model_oblique_x(0.0, (y+1)*height, height, gamma, ny),
+ h-model_oblique_y(0.0, (y+1)*height, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width/2, (y*height)+height, height, gamma, ny),
+ h-model_oblique_y((x*width)+width/2, (y*height)+height, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width, (y*height)+height, height, gamma, ny),
+ h-model_oblique_y((x*width)+width, (y*height)+height, gamma));
+ }
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_VERTICAL_QUARTER ) {
+ int x;
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_glide(dctx, (x*width)+width/4, 0.0, (x*width)+width/4, height*ny, height, gamma, ny, h);
+ model_display_draw_glide(dctx, (x*width)+3*width/4, 0.0, (x*width)+3*width/4, height*ny, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER ) {
+ int y;
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_glide(dctx, 0.0, (height*y)+height/4, nx*width, (height*y)+height/4, height, gamma, ny, h);
+ model_display_draw_glide(dctx, 0.0, (height*y)+3*height/4, nx*width, (height*y)+3*height/4, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_HORIZONTAL ) {
+ int y;
+ model_display_draw_glide(dctx, 0.0, 0.0, nx*width, 0.0, height, gamma, ny, h);
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_glide(dctx, 0.0, (height*y)+height/2, nx*width, (height*y)+height/2, height, gamma, ny, h);
+ model_display_draw_glide(dctx, 0.0, (height*y)+height, nx*width, (height*y)+height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_VERTICAL ) {
+ int x;
+ model_display_draw_glide(dctx, 0.0, 0.0, 0.0, height*ny, height, gamma, ny, h);
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_glide(dctx, (x*width)+width/2, 0.0, (x*width)+width/2, ny*height, height, gamma, ny, h);
+ model_display_draw_glide(dctx, (x*width)+width, 0.0, (x*width)+width, ny*height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_HORIZONTAL ) {
+ int y;
+ model_display_draw_mirror(dctx, 0.0, 0.0, nx*width, 0.0, height, gamma, ny, h);
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_mirror(dctx, 0.0, (height*y)+height/2, nx*width, (height*y)+height/2, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, 0.0, (height*y)+height, nx*width, (height*y)+height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_VERTICAL ) {
+ int x;
+ model_display_draw_mirror(dctx, 0.0, 0.0, 0.0, height*ny, height, gamma, ny, h);
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_mirror(dctx, (x*width)+width/2, 0.0, (x*width)+width/2, ny*height, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, (x*width)+width, 0.0, (x*width)+width, ny*height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_VERTICAL_QUARTER ) {
+ int x;
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_mirror(dctx, (width*x)+width/4, 0.0, (width*x)+width/4, ny*height, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, (width*x)+3*width/4, 0.0, (width*x)+3*width/4, ny*height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_HORIZONTAL_QUARTER ) {
+ int y;
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_mirror(dctx, 0.0, (height*y)+height/4, nx*width, (height*y)+height/4, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, 0, (height*y)+3*height/4, nx*width, (height*y)+3*height/4, height, gamma, ny, h);
+ }
+ }
+
+}
+
+static void model_display_swizzle_data(unsigned char *data, size_t n) {
+
+ size_t i;
+
+ for ( i=0; i<4*n; 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;
+ }
+
+}
+
+static double model_display_surface_width(int width_o, int height_o, double gamma) {
+ if ( gamma <= M_PI_2 ) {
+ return 40+width_o+height_o*cos(gamma);
+ } else {
+ return 40+width_o-height_o*cos(gamma);
+ }
+}
+
+static double model_display_surface_height(int width_o, int height_o, double gamma) {
+ return 40+height_o*sin(gamma);
+}
+
+static cairo_t *model_display_do_cairo(cairo_t *dctx, AtomicModel *model, int width, int height, double gamma, int nx, int ny, double h, int names, int heights) {
+
+ int x, y;
+
+ cairo_rectangle(dctx, 0.0, 0.0, model_display_surface_width(nx*width, ny*height, gamma),
+ model_display_surface_height(nx*width, ny*height, gamma));
+ cairo_set_source_rgb(dctx, 1.0, 1.0, 1.0);
+ cairo_fill(dctx);
+
+ cairo_set_line_width(dctx, 0.5);
+
+ for ( x=0; x<nx; x++ ) {
+ for ( y=0; y<ny; y++ ) {
+ cairo_move_to(dctx, model_oblique_x(width*x, height*y, height, gamma, ny),
+ h-model_oblique_y(width*x, height*y, gamma));
+ cairo_line_to(dctx, model_oblique_x(width*(x+1), height*y, height, gamma, ny),
+ h-model_oblique_y(width*(x+1), height*y, gamma));
+ cairo_line_to(dctx, model_oblique_x(width*(x+1), height*(y+1), height, gamma, ny),
+ h-model_oblique_y(width*(x+1), height*(y+1), gamma));
+ cairo_line_to(dctx, model_oblique_x(width*x, height*(y+1), height, gamma, ny),
+ h-model_oblique_y(width*x, height*(y+1), gamma));
+ cairo_line_to(dctx, model_oblique_x(width*x, height*y, height, gamma, ny),
+ h-model_oblique_y(width*x, height*y, gamma));
+ }
+ }
+ cairo_set_source_rgb(dctx, 0.8, 0.8, 0.8);
+ cairo_stroke(dctx);
+
+ model_display_draw_atoms(dctx, model, width, height, gamma, nx, ny, h, names, heights);
+ model_display_draw_symmetry(dctx, model->sym, 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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifndef MODEL_DISPLAY_H
+#define MODEL_DISPLAY_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
+
+#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; i<editor->model->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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifndef MODEL_EDITOR_H
+#define MODEL_EDITOR_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#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; i<strlen(line); i++ ) {
+ if ( line[i] == '\n' ) {
+ line[i] = '\0';
+ return;
+ }
+ if ( line[i] == '\r' ) {
+ line[i] = '\0';
+ return;
+ }
+ }
+
+}
+
+AtomicModel *model_load(const char *filename) {
+
+ FILE *fh;
+ AtomicModel *model;
+ unsigned int i;
+ char line[256];
+
+ model = model_new();
+
+ fh = fopen(filename, "r");
+ i = 0;
+ while ( fgets(line, 255, fh) != NULL ) {
+
+ float x, y, z, B, occ, thickness;
+ char active, refine;
+ char tmp[256];
+
+ chomp(line);
+ if ( strlen(line) == 0 ) continue;
+
+ /* The '%s' is guaranteed to be shorter than the 'line' which contains it */
+ if ( sscanf(line, "%f %f %f %s %f %f %c %c", &x, &y, &z, tmp, &B, &occ, &active, &refine) == 8 ) {
+ model->atoms[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; i<model->n_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; i<equivalents->n_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; i<equivalents->n_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; j<model->n_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; i<template->n_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; j<model->n_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; i<reflections->n_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; i<reflections->n_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; i<reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifndef MODEL_H
+#define MODEL_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <fftw3.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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; i<size; i++ ) {
+ a[i][0] *= b[i][0];
+ a[i][1] *= b[i][1];
+ }
+}
+
+static void multislice_convolve(fftw_complex *a, fftw_complex *b, size_t size) {
+
+
+
+}
+
+/* Calculate dynamical diffraction amplitudes and phases */
+ReflectionList *multislice_calculate_f_dyn(AtomicModel *model, ReflectionList *template) {
+
+ size_t width, height;
+ fftw_complex *wavefunction;
+ fftw_complex *phasegrating;
+ fftw_complex *propogator;
+ double t;
+ double c;
+
+ c = data_c();
+ width = 128; height = 128;
+
+ printf("MS: Calculating phase grating function...\n"); fflush(stdout);
+ phasegrating = multislice_phasegrating(model, width, height);
+
+ printf("MS: Calculating propogration function...\n"); fflush(stdout);
+ propogator = multislice_propogator(model, width, height, c);
+
+ /* Initial value of wavefunction */
+ wavefunction = fftw_malloc(width*height*sizeof(fftw_complex));
+ memcpy(wavefunction, phasegrating, width*height*sizeof(fftw_complex));
+
+ /* Iterate */
+ for ( t=0.0; t<model->thickness; 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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
+
+#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; i<reflections->n_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; i<reflections->n_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; i<reflections->n_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; i<reflections->n_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; i<reflections->n_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; i<reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fftw3.h>
+
+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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <png.h>
+#include <math.h>
+#include <stdlib.h>
+#include <fftw3.h>
+
+#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<height_n; yn++ ) {
+
+ row_pointers[yn] = malloc(width_n*3);
+ for ( xn=0; xn<width_n; xn++ ) {
+ row_pointers[yn][3*xn] = 0;
+ row_pointers[yn][3*xn+1] = 0;
+ row_pointers[yn][3*xn+2] = 0;
+ }
+
+ }
+
+ for ( yn=0; yn<height_n; yn++ ) {
+ for ( xn=0; xn<width_n; xn++ ) {
+
+ double re, im, am, ph;
+
+ re = cxar.re[xn+width_n*yn];
+ im = cxar.im[xn+width_n*yn];
+ am = sqrt(re*re + im*im) / brightness;
+ ph = atan2(im, re);
+ if ( am > 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<height_n/2+1; yn++ ) {
+ png_bytep scratch;
+ scratch = row_pointers[yn];
+ row_pointers[yn] = row_pointers[height_n-yn-1];
+ row_pointers[height_n-yn-1] = scratch;
+ }
+
+ png_set_rows(png_ptr, info_ptr, row_pointers);
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ for ( yn=0; yn<height_n; yn++ ) {
+ free(row_pointers[yn]);
+ }
+ free(row_pointers);
+ fclose(fh);
+
+ return 0;
+
+}
+
+int png_write(const char *filename, fftw_complex *out, double norm, int nx, int ny) {
+ return png_write_real(filename, out, norm, data_gamma(), data_width(), data_height(), nx, ny);
+}
+
diff --git a/src/png-file.h b/src/png-file.h
new file mode 100644
index 0000000..48e57ac
--- /dev/null
+++ b/src/png-file.h
@@ -0,0 +1,23 @@
+/*
+ * png-file.h
+ *
+ * PNG output
+ *
+ * (c) 2006-2007 Thomas White <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef PNGFILE_H
+#define PNGFILE_H
+
+#include <fftw3.h>
+
+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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_min.h>
+#include <gsl/gsl_blas.h>
+#include <gsl/gsl_linalg.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_blas.h>
+#include <gsl/gsl_multimin.h>
+#include <gsl/gsl_linalg.h>
+
+#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; j<model->n_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; j<model->n_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; j<model->n_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; j<model->n_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; j<model->n_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; j<model->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_multifit_nlin.h>
+#include <gsl/gsl_blas.h>
+
+#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; j<model->n_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; i<calc->n_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; j<model->n_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; i<calc->n_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; j<model->n_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; i<obs->n_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; i<obs->n_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; i<obs->n_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; i<obs->n_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; i<obs->n_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; j<model->n_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; j<model->n_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; j<model->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_blas.h>
+#include <gsl/gsl_linalg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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; j<model->n_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; j<model->n_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; j<n_params; j++ ) {
+ size_t k;
+ for ( k=0; k<n_params; k++ ) {
+ int i;
+ double total_grad = 0;
+ for ( i=1; i<=n_obs; i++ ) {
+ double grad1, grad2;
+ grad1 = refine_lsq_gradient(parameters, obs, spec, j, i, f_calc, model);
+ grad2 = refine_lsq_gradient(parameters, obs, spec, k, i, f_calc, model);
+ total_grad += grad1 * grad2;
+ }
+ gsl_matrix_set(B, j, k, total_grad);
+ }
+ }
+
+ return B;
+
+}
+
+static gsl_matrix *refine_lsq_D(const gsl_vector *parameters, ReflectionList *obs, RefinementSpec spec, AtomicModel *model, ReflectionList *f_calc) {
+
+ gsl_matrix *D;
+ size_t j;
+ int n_params, n_obs;
+ double scale;
+
+ n_params = parameters->size;
+ n_obs = obs->n_reflections - 1;
+ scale = gsl_vector_get(parameters, 0);
+
+ D = gsl_matrix_alloc(n_params, 1);
+ for ( j=0; j<n_params; j++ ) {
+ int i;
+ double total = 0;
+ for ( i=1; i<=n_obs; i++ ) {
+
+ double grad;
+ double res;
+
+ grad = refine_lsq_gradient(parameters, obs, spec, j, i, f_calc, model);
+
+ if ( spec & REFINE_SPEC_INTENSITIES ) {
+ res = (obs->refs[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<n_params; i++ ) {
+ double old, new;
+ old = gsl_vector_get(parameters, i);
+ new = old + gsl_matrix_get(shift, i, 0);
+ gsl_vector_set(parameters, i, new);
+ //printf("Parameter %i: %f -> %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; j<model->n_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; j<model->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#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; i<model->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#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), "<span weight=\"bold\">Refinement Algorithm</span>");
+ 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), "<span weight=\"bold\">Parameters to Refine</span>");
+ 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), "<span weight=\"bold\">Refine Against</span>");
+ 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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef REFINE_H
+#define REFINE_H
+
+#include <gtk/gtk.h>
+
+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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#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; i<list->n_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; j<list->n_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; i<list->n_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; i<list->n_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; i<reflections->n_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 <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifndef REFLIST_H
+#define REFLIST_H
+
+#include <fftw3.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2D - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <fftw3.h>
+
+#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<height_n; yn++ ) {
+ for ( xn=0; xn<width_n; xn++ ) {
+
+ double xd, yd;
+ double re, im;
+
+ /* Map this pixel onto the FFTW output array */
+ xd = renderer_map_reverse_x(xn, yn, width, height, gamma, nx, ny);
+ yd = renderer_map_reverse_y(xn, yn, width, height, gamma, nx, ny);
+
+ /* Extract (interpolatively) the value */
+ re = renderer_interpolate_bilinear_re(xd, yd, out, width, height);
+ im = renderer_interpolate_bilinear_im(xd, yd, out, width, height);
+
+ /* Store the values */
+ cxar.re[xn+width_n*yn] = re;
+ cxar.im[xn+width_n*yn] = im;
+
+ }
+ }
+
+ return cxar;
+
+}
+
diff --git a/src/renderer.h b/src/renderer.h
new file mode 100644
index 0000000..770a893
--- /dev/null
+++ b/src/renderer.h
@@ -0,0 +1,31 @@
+/*
+ * renderer.c
+ *
+ * Render Fourier Transform output array into display array
+ *
+ * (c) 2006-2007 Thomas White <taw27@cam.ac.uk>
+ *
+ * synth2D - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <gsl/gsl_errno.h>
+#include <gsl/gsl_math.h>
+#include <gsl/gsl_min.h>
+#include <assert.h>
+
+#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; i<obs->n_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; i<obs->n_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; i<obs->n_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; i<obs->n_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; i<reflections->n_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; i<a->n_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; i<a->n_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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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; i<old->n_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 <taw27@cam.ac.uk>
+ * Synth2D - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#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; i<n; i++ ) { /* Don't symmetrise 000 */
+
+ signed int h, k, l;
+
+ h = reflections->refs[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; j<equivalents->n_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; j<equivalents->n_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 <taw27@cam.ac.uk>
+ * Synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef SYMMETRY_H
+#define SYMMETRY_H
+
+#include <fftw3.h>
+
+#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 <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+
+#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; i<reflist->n_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; i<reflist->n_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; i<reflist->n_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; i<reflist->n_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; i<reflist->n_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;
+
+}
+