diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/crystfel_gui.c | 3 | ||||
-rw-r--r-- | src/gui_ambi.c | 262 | ||||
-rw-r--r-- | src/gui_ambi.h | 39 | ||||
-rw-r--r-- | src/gui_project.c | 67 | ||||
-rw-r--r-- | src/gui_project.h | 10 |
7 files changed, 380 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index bdd31cb4..a698bfb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,7 +397,7 @@ if (GTK_FOUND) src/gui_peaksearch.c src/gui_index.c src/gui_merge.c src/gui_backend_local.c src/gui_project.c src/crystfelindexingopts.c src/crystfelmergeopts.c src/crystfelsymmetryselector.c src/gtk-util-routines.c src/gui_fom.c - src/gui_export.c) + src/gui_export.c src/gui_ambi.c) if (HAVE_SLURM) set(CRYSTFEL_GUI_SOURCES ${CRYSTFEL_GUI_SOURCES} src/gui_backend_slurm.c) diff --git a/meson.build b/meson.build index 21bfa2f6..ed68aa03 100644 --- a/meson.build +++ b/meson.build @@ -228,6 +228,7 @@ if gtkdep.found() 'src/gui_merge.c', 'src/gui_fom.c', 'src/gui_export.c', + 'src/gui_ambi.c', 'src/gui_backend_local.c', 'src/gui_project.c', versionc] diff --git a/src/crystfel_gui.c b/src/crystfel_gui.c index e4151b43..f32153aa 100644 --- a/src/crystfel_gui.c +++ b/src/crystfel_gui.c @@ -50,6 +50,7 @@ #include "gui_merge.h" #include "gui_fom.h" #include "gui_export.h" +#include "gui_ambi.h" #include "gui_project.h" #include "version.h" @@ -857,7 +858,7 @@ static void add_task_buttons(GtkWidget *vbox, struct crystfelproject *proj) add_button(vbox, "Determine unit cell", "applications-engineering", G_CALLBACK(cell_explorer_sig), proj); add_button(vbox, "Indexing ambiguity", "face-worried", - G_CALLBACK(NULL), proj); + G_CALLBACK(ambi_sig), proj); add_button(vbox, "Merge", "applications-science", G_CALLBACK(merge_sig), proj); add_button(vbox, "Figures of merit", "trophy-gold", diff --git a/src/gui_ambi.c b/src/gui_ambi.c new file mode 100644 index 00000000..666da6eb --- /dev/null +++ b/src/gui_ambi.c @@ -0,0 +1,262 @@ +/* + * gui_ambi.c + * + * Resolve indexing ambiguities via CrystFEL GUI + * + * Copyright © 2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2021 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <gtk/gtk.h> +#include <assert.h> + +#include "crystfelsymmetryselector.h" +#include "gui_project.h" +#include "crystfel_gui.h" +#include "gtk-util-routines.h" + + +struct ambi_window +{ + struct crystfelproject *proj; + GtkWidget *dataset; + GtkWidget *limit_res; + GtkWidget *min_res; + GtkWidget *max_res; + GtkWidget *niter; + GtkWidget *use_ncorr; + GtkWidget *ncorr; + GtkWidget *sym; + GtkWidget *source_sym; + GtkWidget *operator; + GtkWidget *use_source_sym; + GtkWidget *use_operator; +}; + + +static char *get_sym(GtkWidget *w) +{ + return crystfel_symmetry_selector_get_group_symbol(CRYSTFEL_SYMMETRY_SELECTOR(w)); +} + + +static char *get_str(GtkWidget *w) +{ + const char *text = gtk_entry_get_text(GTK_ENTRY(w)); + if ( text == NULL ) return NULL; + if ( text[0] == '\0' ) return NULL; + return strdup(text); +} + + +static void ambi_response_sig(GtkWidget *dialog, gint resp, + struct ambi_window *win) +{ + int r = 0; + + if ( resp == GTK_RESPONSE_ACCEPT ) { + win->proj->ambi_use_res = get_bool(win->limit_res); + win->proj->ambi_res_min = get_float(win->min_res); + win->proj->ambi_res_max = get_float(win->max_res); + win->proj->ambi_niter = get_uint(win->niter); + win->proj->ambi_use_ncorr = get_bool(win->use_ncorr); + win->proj->ambi_ncorr = get_uint(win->ncorr); + win->proj->ambi_sym = get_sym(win->sym); + win->proj->ambi_source_sym = get_sym(win->source_sym); + win->proj->ambi_operator = get_str(win->operator); + } + + if ( !r ) gtk_widget_destroy(dialog); +} + + +gint ambi_sig(GtkWidget *widget, struct crystfelproject *proj) +{ + GtkWidget *dialog; + GtkWidget *content_area; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + struct ambi_window *win; + int i; + char tmp[64]; + + win = malloc(sizeof(struct ambi_window)); + if ( win == NULL ) return 0; + + win->proj = proj; + + dialog = gtk_dialog_new_with_buttons("Resolve indexing ambiguity", + GTK_WINDOW(proj->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + "Close", GTK_RESPONSE_CLOSE, + "Run", GTK_RESPONSE_ACCEPT, + NULL); + + g_signal_connect(G_OBJECT(dialog), "response", + G_CALLBACK(ambi_response_sig), + win); + + vbox = gtk_vbox_new(FALSE, 0.0); + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_add(GTK_CONTAINER(content_area), vbox); + gtk_container_set_border_width(GTK_CONTAINER(content_area), 8); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + label = gtk_label_new("Indexing result for input:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + win->dataset = gtk_combo_box_text_new(); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->dataset), + FALSE, FALSE, 4.0); + for ( i=0; i<proj->n_results; i++ ) { + gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(win->dataset), + proj->results[i].name, + proj->results[i].name); + } + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + label = gtk_label_new("Target ('real') point group:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + win->sym = crystfel_symmetry_selector_new(); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->sym), + FALSE, FALSE, 4.0); + crystfel_symmetry_selector_set_group_symbol(CRYSTFEL_SYMMETRY_SELECTOR(win->sym), + proj->ambi_sym); + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + win->use_source_sym = gtk_radio_button_new_with_label(NULL, "Source ('twinned') point group:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->use_source_sym), + FALSE, FALSE, 4.0); + win->source_sym = crystfel_symmetry_selector_new(); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->source_sym), + FALSE, FALSE, 4.0); + crystfel_symmetry_selector_set_group_symbol(CRYSTFEL_SYMMETRY_SELECTOR(win->source_sym), + proj->ambi_source_sym); + g_signal_connect(G_OBJECT(win->use_source_sym), "toggled", + G_CALLBACK(i_maybe_disable), win->source_sym); + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + win->use_operator = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(win->use_source_sym), + "Ambiguity operation:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->use_operator), + FALSE, FALSE, 4.0); + win->operator = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(win->operator), proj->ambi_operator); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->operator), + FALSE, FALSE, 4.0); + g_signal_connect(G_OBJECT(win->use_operator), "toggled", + G_CALLBACK(i_maybe_disable), win->operator); + + if ( proj->ambi_source_sym != NULL ) { + gtk_widget_set_sensitive(win->operator, FALSE); + set_active(win->use_source_sym, TRUE); + } else { + gtk_widget_set_sensitive(win->source_sym, FALSE); + set_active(win->use_operator, TRUE); + } + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + win->limit_res = gtk_check_button_new_with_label("Restrict resolution range:"); + set_active(win->limit_res, proj->ambi_use_res); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->limit_res), + FALSE, FALSE, 4.0); + win->min_res = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(win->min_res), 6); + snprintf(tmp, 64, "%.2f", proj->ambi_res_min); + gtk_entry_set_text(GTK_ENTRY(win->min_res), tmp); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->min_res), + FALSE, FALSE, 4.0); + label = gtk_label_new("to"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + win->max_res = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(win->max_res), 6); + snprintf(tmp, 64, "%.2f", proj->ambi_res_max); + gtk_entry_set_text(GTK_ENTRY(win->max_res), tmp); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->max_res), + FALSE, FALSE, 4.0); + label = gtk_label_new("Å"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + g_signal_connect(G_OBJECT(win->limit_res), "toggled", + G_CALLBACK(i_maybe_disable), win->min_res); + g_signal_connect(G_OBJECT(win->limit_res), "toggled", + G_CALLBACK(i_maybe_disable), win->max_res); + gtk_widget_set_sensitive(win->min_res, proj->ambi_use_res); + gtk_widget_set_sensitive(win->max_res, proj->ambi_use_res); + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + win->use_ncorr = gtk_check_button_new_with_label("Limit number of correlations to:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->use_ncorr), + FALSE, FALSE, 4.0); + win->ncorr = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(win->ncorr), 6); + snprintf(tmp, 64, "%i", proj->ambi_ncorr); + gtk_entry_set_text(GTK_ENTRY(win->ncorr), tmp); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->ncorr), + FALSE, FALSE, 4.0); + g_signal_connect(G_OBJECT(win->use_ncorr), "toggled", + G_CALLBACK(i_maybe_disable), win->ncorr); + set_active(win->use_ncorr, proj->ambi_use_ncorr); + gtk_widget_set_sensitive(win->ncorr, proj->ambi_use_ncorr); + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), + FALSE, FALSE, 4.0); + label = gtk_label_new("Number of iterations:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + win->niter = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(win->niter), 2); + snprintf(tmp, 64, "%i", proj->ambi_niter); + gtk_entry_set_text(GTK_ENTRY(win->niter), tmp); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->niter), + FALSE, FALSE, 4.0); + + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CLOSE); + gtk_widget_show_all(dialog); + + return FALSE; +} diff --git a/src/gui_ambi.h b/src/gui_ambi.h new file mode 100644 index 00000000..f63c29a0 --- /dev/null +++ b/src/gui_ambi.h @@ -0,0 +1,39 @@ +/* + * gui_ambi.h + * + * Resolve indexing ambiguities via CrystFEL GUI + * + * Copyright © 2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2021 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef GUI_AMBI_H +#define GUI_AMBI_H + +#include <gtk/gtk.h> + +#include "gui_project.h" + +extern gint ambi_sig(GtkWidget *widget, + struct crystfelproject *proj); + +#endif diff --git a/src/gui_project.c b/src/gui_project.c index 153fb55f..d89c66f9 100644 --- a/src/gui_project.c +++ b/src/gui_project.c @@ -312,6 +312,39 @@ static void add_metadata_to_copy(struct index_params *ip, } +static void parse_ambi_opt(const char *key, const char *val, + struct crystfelproject *proj) +{ + if ( strcmp(key, "ambi.min_res_A") == 0 ) { + proj->ambi_res_min = parse_float(val); + } + if ( strcmp(key, "ambi.max_res_A") == 0 ) { + proj->ambi_res_max = parse_float(val); + } + if ( strcmp(key, "ambi.use_res") == 0 ) { + proj->ambi_use_res = parse_int(val); + } + if ( strcmp(key, "ambi.niter") == 0 ) { + proj->ambi_niter = parse_int(val); + } + if ( strcmp(key, "ambi.ncorr") == 0 ) { + proj->ambi_ncorr = parse_int(val); + } + if ( strcmp(key, "ambi.use_ncorr") == 0 ) { + proj->ambi_use_ncorr = parse_int(val); + } + if ( strcmp(key, "ambi.sym") == 0 ) { + proj->ambi_sym = strdup(val); + } + if ( strcmp(key, "ambi.source_sym") == 0 ) { + proj->ambi_source_sym = strdup(val); + } + if ( strcmp(key, "ambi.operator") == 0 ) { + proj->ambi_operator = strdup(val); + } +} + + static void parse_fom_opt(const char *key, const char *val, struct crystfelproject *proj) { @@ -460,6 +493,10 @@ static void handle_var(const char *key, const char *val, parse_fom_opt(key, val, proj); } + if ( strncmp(key, "ambi.", 4) == 0 ) { + parse_ambi_opt(key, val, proj); + } + if ( strncmp(key, "stream.", 7) == 0 ) { parse_stream_opt(key, val, &proj->indexing_params); } else if ( strcmp(key, "stream") == 0 ) { @@ -871,6 +908,22 @@ int save_project(struct crystfelproject *proj) } } + fprintf(fh, "ambi.min_res_A %f\n", proj->ambi_res_min); + fprintf(fh, "ambi.max_res_A %f\n", proj->ambi_res_max); + fprintf(fh, "ambi.use_res %i\n", proj->ambi_use_res); + fprintf(fh, "ambi.niter %i\n", proj->ambi_niter); + fprintf(fh, "ambi.ncorr %i\n", proj->ambi_ncorr); + fprintf(fh, "ambi.use_ncorr %i\n", proj->ambi_use_ncorr); + if ( proj->ambi_sym != NULL ) { + fprintf(fh, "ambi.sym %s\n", proj->ambi_sym); + } + if ( proj->ambi_source_sym != NULL ) { + fprintf(fh, "ambi.source_sym %s\n", proj->ambi_source_sym); + } + if ( proj->ambi_operator != NULL ) { + fprintf(fh, "ambi.operator %s\n", proj->ambi_operator); + } + fprintf(fh, "merging.model %s\n", proj->merging_params.model); fprintf(fh, "merging.symmetry %s\n", @@ -1045,6 +1098,16 @@ void default_project(struct crystfelproject *proj) proj->indexing_params.metadata_to_copy = NULL; proj->indexing_params.n_metadata = 0; + proj->ambi_use_res = 1; + proj->ambi_res_min = 20; /* Angstroms */ + proj->ambi_res_max = 4; /* Angstroms */ + proj->ambi_niter = 4; + proj->ambi_use_ncorr = 0; + proj->ambi_ncorr = 1000; + proj->ambi_sym = NULL; + proj->ambi_source_sym = NULL; + proj->ambi_operator = NULL; + proj->merging_params.model = strdup("unity"); proj->merging_params.symmetry = strdup("1"); proj->merging_params.scale = 1; @@ -1066,8 +1129,8 @@ void default_project(struct crystfelproject *proj) proj->merge_results = NULL; proj->n_merge_results = 0; - proj->fom_res_min = 100.0; - proj->fom_res_max = 5.0; + proj->fom_res_min = 100.0; /* Angstroms */ + proj->fom_res_max = 5.0; /* Angstroms */ proj->fom_nbins = 20; proj->fom_min_snr = -INFINITY; proj->fom_min_meas = 1; diff --git a/src/gui_project.h b/src/gui_project.h index 4d2e75c3..37307d3b 100644 --- a/src/gui_project.h +++ b/src/gui_project.h @@ -277,6 +277,16 @@ struct crystfelproject { double export_res_min; double export_res_max; + + int ambi_use_res; + double ambi_res_min; /* Angstroms */ + double ambi_res_max; /* Angstroms */ + int ambi_niter; + int ambi_use_ncorr; + int ambi_ncorr; + char *ambi_sym; + char *ambi_source_sym; + char *ambi_operator; }; extern enum match_type_id decode_matchtype(const char *type_id); |