diff options
author | Thomas White <taw@physics.org> | 2021-02-08 17:58:40 +0100 |
---|---|---|
committer | Thomas White <taw@physics.org> | 2021-02-08 17:58:40 +0100 |
commit | c59bc9ef66c5d4b35ee76dc1b95bcd93aca8d48d (patch) | |
tree | 7807867c4b6dde610c8e11514304f29973196e9a | |
parent | 21f7896cc23ff79fa1e4cb593f8685bdb5c7a082 (diff) |
GUI: Initial FoM calculation
-rw-r--r-- | src/gui_fom.c | 413 | ||||
-rw-r--r-- | src/gui_project.c | 9 | ||||
-rw-r--r-- | src/gui_project.h | 3 |
3 files changed, 368 insertions, 57 deletions
diff --git a/src/gui_fom.c b/src/gui_fom.c index 936a39fb..1947f19f 100644 --- a/src/gui_fom.c +++ b/src/gui_fom.c @@ -37,22 +37,240 @@ #include <assert.h> #include <utils.h> +#include <fom.h> +#include <reflist-utils.h> +#include <cell-utils.h> #include "gui_project.h" #include "crystfel_gui.h" #include "gtk-util-routines.h" +#define MAX_DATASETS (64) + + +struct fom_dataset +{ + SymOpList *sym; + RefList *all_refls; + RefList *half1; + RefList *half2; + RefList *all_refls_anom; + RefList *half1_anom; + RefList *half2_anom; +}; + + +struct fom_window +{ + struct crystfelproject *proj; + GtkWidget *min_res; + GtkWidget *max_res; + GtkWidget *num_bins; + GtkWidget *min_snr; + GtkWidget *min_meas; + GtkWidget *cell_chooser; + + int n_datasets; + GtkWidget *dataset_checkboxes[MAX_DATASETS]; + char *dataset_names[MAX_DATASETS]; + struct fom_dataset datasets[MAX_DATASETS]; + + int n_foms; + GtkWidget *fom_checkboxes[16]; + enum fom_type fom_types[16]; +}; + + +static int menu_selected(GtkWidget *w) +{ + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); +} + + +static int fom_selected(struct fom_window *f, int i) +{ + return menu_selected(f->fom_checkboxes[i]); +} + + +static int anomalous_foms_selected(struct fom_window *f) +{ + int i; + for ( i=0; i<f->n_foms; i++ ) { + if ( fom_selected(f, i) ) { + if ( fom_is_anomalous(f->fom_types[i]) ) return 1; + } + } + return 0; +} + + +static void show_fom(enum fom_type fom, + struct fom_context *fctx, + struct fom_shells *shells) +{ + int i; + + STATUS("Overall %s: %f (%i reflections)\n", + fom_name(fom), fom_overall_value(fctx), + fom_overall_num_reflections(fctx)); + STATUS("%10s %10s %10s\n", "1/d / nm^-1", fom_name(fom), "num refl"); + for ( i=0; i<shells->nshells; i++ ) { + STATUS("%10f %10f %10i\n", + fom_shell_centre(shells, i)/1e9, + fom_shell_value(fctx, i), + fom_shell_num_reflections(fctx, i)); + } +} + static void fom_response_sig(GtkWidget *dialog, gint resp, - struct crystfelproject *proj) + struct fom_window *f) { - gtk_widget_destroy(dialog); + int ds, fom; + int need_ano; + UnitCell *cell; + struct fom_shells *shells; + + if ( resp != GTK_RESPONSE_APPLY ) { + gtk_widget_destroy(dialog); + return; + } + + f->proj->fom_res_min = get_float(f->min_res); + f->proj->fom_res_max = get_float(f->max_res); + f->proj->fom_min_snr = get_float(f->min_snr); + f->proj->fom_min_meas = get_float(f->min_meas); + f->proj->fom_nbins = get_uint(f->num_bins); + if ( isnan(f->proj->fom_res_min) + || isnan(f->proj->fom_res_max) + || isnan(f->proj->fom_min_snr) ) + { + ERROR("Invalid parameters\n"); + return; + } + + /* "Minimum resolution" should be the bigger number */ + if ( f->proj->fom_res_min < f->proj->fom_res_max ) { + double tmp = f->proj->fom_res_min; + f->proj->fom_res_min = f->proj->fom_res_max; + f->proj->fom_res_max = tmp; + } + + shells = fom_make_resolution_shells(1e10/f->proj->fom_res_min, + 1e10/f->proj->fom_res_max, + f->proj->fom_nbins); + if ( shells == NULL ) { + ERROR("Failed to make resolution shells\n"); + return; + } + + f->proj->fom_cell_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(f->cell_chooser)); + cell = load_cell_from_file(f->proj->fom_cell_filename); + if ( cell == NULL ) { + ERROR("Invalid cell file '%s'\n", f->proj->fom_cell_filename); + return; + } + + need_ano = anomalous_foms_selected(f); + + for ( ds=0; ds<f->n_datasets; ds++ ) { + + struct gui_merge_result *result; + RefList *raw_refl; + char *sym_str; + + if ( !menu_selected(f->dataset_checkboxes[ds]) ) continue; + + /* Load dataset */ + result = find_merge_result_by_name(f->proj, + f->dataset_names[ds]); + + raw_refl = read_reflections_2(result->hkl, &sym_str); + if ( raw_refl == NULL ) { + ERROR("Failed to load dataset %s (%s)\n", + f->dataset_names[ds], result->hkl); + continue; + } + + if ( sym_str == NULL ) { + ERROR("Reflection list has no point group\n"); + reflist_free(raw_refl); + continue; + } + f->datasets[ds].sym = get_pointgroup(sym_str); + free(sym_str); + + fom_select_reflections(raw_refl, + &f->datasets[ds].all_refls, + cell, + f->datasets[ds].sym, + 1e10/f->proj->fom_res_min, + 1e10/f->proj->fom_res_max, + f->proj->fom_min_snr, + 0, 0, + f->proj->fom_min_meas); + + if ( f->datasets[ds].all_refls == NULL ) { + ERROR("Failed to load dataset '%s'\n", + f->dataset_names[ds]); + reflist_free(raw_refl); + continue; + } + + STATUS("%s: accepted %i reflections out of %i\n", + result->hkl, + num_reflections(f->datasets[ds].all_refls), + num_reflections(raw_refl)); + + if ( need_ano ) { + fom_select_reflections(raw_refl, + &f->datasets[ds].all_refls_anom, + cell, + f->datasets[ds].sym, + 1e10/f->proj->fom_res_min, + 1e10/f->proj->fom_res_max, + f->proj->fom_min_snr, + 0, 0, + f->proj->fom_min_meas); + if ( f->datasets[ds].all_refls_anom == NULL ) { + ERROR("Failed to load dataset '%s'\n", + f->dataset_names[ds]); + reflist_free(raw_refl); + continue; + } + } + + reflist_free(raw_refl); + + /* FIXME: Load half datasets as well */ + + for ( fom=0; fom<f->n_foms; fom++ ) { + + struct fom_context *fctx; + + if ( !fom_selected(f, fom) ) continue; + + fctx = fom_calculate(f->datasets[ds].all_refls, + NULL, cell, shells, + f->fom_types[fom], 1, + f->datasets[ds].sym); + if ( fctx == NULL ) { + ERROR("Failed to calculate FoM %i for dataset %s\n", + f->fom_types[fom], f->dataset_names[ds]); + continue; + } + show_fom(f->fom_types[fom], fctx, shells); + } + + } + } -static void add_fom(GtkWidget *menu, - const char *text, - const char *markup) +static GtkWidget *add_item(GtkWidget *menu, + const char *text, + const char *markup) { GtkWidget *label; GtkWidget *item; @@ -69,6 +287,8 @@ static void add_fom(GtkWidget *menu, gtk_widget_show_all(item); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + + return item; } @@ -81,7 +301,7 @@ static void add_separator(GtkWidget *menu) } -static GtkWidget *make_fom_menu() +static GtkWidget *make_fom_menu(struct fom_window *fom) { GtkWidget *menu; GtkWidget *item; @@ -92,23 +312,86 @@ static GtkWidget *make_fom_menu() gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show_all(item); - add_fom(menu, "I/σ(I), completeness etc", NULL); - add_fom(menu, "Rsplit", "R<sub>split</sub>"); - add_fom(menu, "CC", "CC<sub>½</sub>"); - add_fom(menu, "CC*", "CC<sup>*</sup>"); + /* Order of FoMs must match list below */ + fom->fom_checkboxes[0] = add_item(menu, "I/σ(I)", NULL); + fom->fom_checkboxes[1] = add_item(menu, "Completeness", NULL); + fom->fom_checkboxes[2] = add_item(menu, "Redundancy", NULL); add_separator(menu); - add_fom(menu, "CCano", "CC<sub>ano</sub>"); - add_fom(menu, "Rano", "R<sub>ano</sub>"); - add_fom(menu, "Rano ÷ Rsplit", "R<sub>ano</sub> ÷ R<sub>split</sub>"); - add_fom(menu, "RMS anomalous correlation ratio", NULL); + fom->fom_checkboxes[3] = add_item(menu, "Rsplit", "R<sub>split</sub>"); + fom->fom_checkboxes[4] = add_item(menu, "CC", "CC<sub>½</sub>"); + fom->fom_checkboxes[5] = add_item(menu, "CC*", "CC<sup>*</sup>"); add_separator(menu); - add_fom(menu, "Fraction of differences within 1σ(I)", NULL); - add_fom(menu, "Fraction of differences within 2σ(I)", NULL); + fom->fom_checkboxes[6] = add_item(menu, "CCano", "CC<sub>ano</sub>"); + fom->fom_checkboxes[7] = add_item(menu, "Rano", "R<sub>ano</sub>"); + fom->fom_checkboxes[8] = add_item(menu, "Rano ÷ Rsplit", + "R<sub>ano</sub> ÷ R<sub>split</sub>"); + fom->fom_checkboxes[9] = add_item(menu, "RMS anomalous correlation ratio", NULL); + add_separator(menu); + fom->fom_checkboxes[10] = add_item(menu, "Fraction of differences within 1σ(I)", NULL); + fom->fom_checkboxes[11] = add_item(menu, "Fraction of differences within 2σ(I)", NULL); + + /* Order must match the list above */ + fom->fom_types[0] = FOM_SNR; + fom->fom_types[1] = FOM_COMPLETENESS; + fom->fom_types[2] = FOM_REDUNDANCY; + + fom->fom_types[3] = FOM_RSPLIT; + fom->fom_types[4] = FOM_CC; + fom->fom_types[5] = FOM_CCSTAR; + + fom->fom_types[6] = FOM_CCANO; + fom->fom_types[7] = FOM_RANO; + fom->fom_types[8] = FOM_RANORSPLIT; + fom->fom_types[9] = FOM_CRDANO; + + fom->fom_types[10] = FOM_D1SIG; + fom->fom_types[11] = FOM_D2SIG; + + fom->n_foms = 12; return menu; } +static GtkWidget *make_dataset_menu(struct fom_window *win) +{ + GtkWidget *menu; + GtkWidget *item; + int i; + + menu = gtk_menu_new(); + + item = gtk_tearoff_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show_all(item); + + for ( i=0; i<win->proj->n_merge_results; i++ ) { + + const char *ds_name = win->proj->merge_results[i].name; + + /* Yes, I'm lazy */ + if ( i >= MAX_DATASETS ) { + ERROR("Too many datasets - ignoring %s\n", ds_name); + continue; + } + + win->dataset_checkboxes[i] = add_item(menu, ds_name, NULL); + win->dataset_names[i] = strdup(ds_name); + win->n_datasets++; + } + + return menu; +} + + +static void cell_file_clear_sig(GtkButton *buton, + struct fom_window *f) +{ + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(f->cell_chooser), + "(none)"); +} + + gint fom_sig(GtkWidget *widget, struct crystfelproject *proj) { GtkWidget *dialog; @@ -116,23 +399,28 @@ gint fom_sig(GtkWidget *widget, struct crystfelproject *proj) GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; - GtkWidget *combo; GtkWidget *button; - GtkWidget *entry; - GtkWidget *check; GtkWidget *da; - int i; char tmp[64]; + struct fom_window *f; + + f = malloc(sizeof(struct fom_window)); + if ( f == NULL ) return 0; + + f->proj = proj; + f->n_datasets = 0; + f->n_foms = 0; dialog = gtk_dialog_new_with_buttons("Calculate figures of merit", GTK_WINDOW(proj->window), GTK_DIALOG_DESTROY_WITH_PARENT, + "Calculate", GTK_RESPONSE_APPLY, "Close", GTK_RESPONSE_CLOSE, NULL); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(fom_response_sig), - proj); + f); vbox = gtk_vbox_new(FALSE, 0.0); content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); @@ -142,19 +430,14 @@ gint fom_sig(GtkWidget *widget, struct crystfelproject *proj) 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("Merged results:"); + label = gtk_label_new("Results to show:"); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 4.0); - combo = gtk_combo_box_text_new(); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(combo), + button = gtk_menu_button_new(); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 4.0); - for ( i=0; i<proj->n_merge_results; i++) { - gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo), - proj->merge_results[i].name, - proj->merge_results[i].name); - } - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), - proj->n_merge_results-1); + gtk_menu_button_set_popup(GTK_MENU_BUTTON(button), + make_dataset_menu(f)); label = gtk_label_new("Figures of merit to show:"); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), @@ -163,7 +446,7 @@ gint fom_sig(GtkWidget *widget, struct crystfelproject *proj) gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 4.0); gtk_menu_button_set_popup(GTK_MENU_BUTTON(button), - make_fom_menu()); + make_fom_menu(f)); hbox = gtk_hbox_new(FALSE, 0.0); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), @@ -171,45 +454,71 @@ gint fom_sig(GtkWidget *widget, struct crystfelproject *proj) label = gtk_label_new("Resolution range:"); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 4.0); - entry = gtk_entry_new(); - gtk_entry_set_width_chars(GTK_ENTRY(entry), 4); + f->min_res = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(f->min_res), 4); snprintf(tmp, 64, "%.2f", proj->fom_res_min); - gtk_entry_set_text(GTK_ENTRY(entry), tmp); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), + gtk_entry_set_text(GTK_ENTRY(f->min_res), tmp); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(f->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); - entry = gtk_entry_new(); - gtk_entry_set_width_chars(GTK_ENTRY(entry), 4); + f->max_res = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(f->max_res), 4); snprintf(tmp, 64, "%.2f", proj->fom_res_max); - gtk_entry_set_text(GTK_ENTRY(entry), tmp); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), + gtk_entry_set_text(GTK_ENTRY(f->max_res), tmp); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(f->max_res), FALSE, FALSE, 4.0); label = gtk_label_new("Å. Number of bins:"); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 4.0); - entry = gtk_entry_new(); - gtk_entry_set_width_chars(GTK_ENTRY(entry), 4); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), + f->num_bins = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(f->num_bins), 4); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(f->num_bins), FALSE, FALSE, 4.0); snprintf(tmp, 64, "%i", proj->fom_nbins); - gtk_entry_set_text(GTK_ENTRY(entry), tmp); + gtk_entry_set_text(GTK_ENTRY(f->num_bins), tmp); hbox = gtk_hbox_new(FALSE, 0.0); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 4.0); - check = gtk_check_button_new_with_label("Discard reflections with I/sigI less than"); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), - proj->fom_use_min_snr); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(check), + label = gtk_label_new("Minimum I/sigI:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 4.0); - entry = gtk_entry_new(); - gtk_entry_set_width_chars(GTK_ENTRY(entry), 4); - gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), + f->min_snr = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(f->min_snr), 4); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(f->min_snr), FALSE, FALSE, 4.0); snprintf(tmp, 64, "%.2f", proj->fom_min_snr); - gtk_entry_set_text(GTK_ENTRY(entry), tmp); + gtk_entry_set_text(GTK_ENTRY(f->min_snr), tmp); + label = gtk_label_new("Minimum measurements per reflection:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + f->min_meas = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(f->min_meas), 4); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(f->min_meas), + FALSE, FALSE, 4.0); + snprintf(tmp, 64, "%i", proj->fom_min_meas); + gtk_entry_set_text(GTK_ENTRY(f->min_meas), tmp); + + 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("Unit cell file:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + f->cell_chooser = gtk_file_chooser_button_new("Unit cell file", + GTK_FILE_CHOOSER_ACTION_OPEN); + gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(f->cell_chooser), + TRUE); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(f->cell_chooser), + FALSE, FALSE, 4.0); + button = gtk_button_new_from_icon_name("edit-clear", + GTK_ICON_SIZE_BUTTON); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(cell_file_clear_sig), f); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), + FALSE, FALSE, 4.0); da = gtk_drawing_area_new(); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(da), diff --git a/src/gui_project.c b/src/gui_project.c index c8257d49..cfede5e1 100644 --- a/src/gui_project.c +++ b/src/gui_project.c @@ -1017,11 +1017,12 @@ void default_project(struct crystfelproject *proj) proj->merge_results = NULL; proj->n_merge_results = 0; - proj->fom_res_min = 0.0; - proj->fom_res_max = INFINITY; + proj->fom_res_min = 100.0; + proj->fom_res_max = 5.0; proj->fom_nbins = 20; - proj->fom_min_snr = 0.0; - proj->fom_use_min_snr = 0; + proj->fom_min_snr = -INFINITY; + proj->fom_min_meas = 1; + proj->fom_cell_filename = NULL; } diff --git a/src/gui_project.h b/src/gui_project.h index f9f34194..3458b928 100644 --- a/src/gui_project.h +++ b/src/gui_project.h @@ -270,7 +270,8 @@ struct crystfelproject { double fom_res_max; int fom_nbins; double fom_min_snr; - int fom_use_min_snr; + int fom_min_meas; + char *fom_cell_filename; }; extern enum match_type_id decode_matchtype(const char *type_id); |