diff options
29 files changed, 608 insertions, 50 deletions
diff --git a/data/crystfel-geometry.png b/data/crystfel-geometry.png Binary files differnew file mode 100644 index 00000000..df5ee8b2 --- /dev/null +++ b/data/crystfel-geometry.png diff --git a/data/crystfel-geometry.svg b/data/crystfel-geometry.svg new file mode 100644 index 00000000..7cac81a1 --- /dev/null +++ b/data/crystfel-geometry.svg @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="48" + height="48" + viewBox="0 0 48 48" + version="1.1" + id="svg5" + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" + sodipodi:docname="crystfel-geometry.svg" + inkscape:export-filename="crystfel-geometry.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview7" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + inkscape:document-units="px" + showgrid="false" + inkscape:zoom="18.1875" + inkscape:cx="21.415808" + inkscape:cy="22.762887" + inkscape:window-width="1965" + inkscape:window-height="1278" + inkscape:window-x="316" + inkscape:window-y="144" + inkscape:window-maximized="0" + inkscape:current-layer="layer1" + showguides="false" + inkscape:showpageshadow="2" + inkscape:deskcolor="#d1d1d1"> + <inkscape:grid + type="xygrid" + id="grid7330" + originx="0" + originy="0" + spacingy="1" + spacingx="1" + units="px" + visible="false" /> + </sodipodi:namedview> + <defs + id="defs2"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="9.0721649 : 28.728522 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="48 : 24 : 1" + inkscape:persp3d-origin="24 : 16 : 1" + id="perspective852" /> + </defs> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <rect + style="fill:#001aff;fill-opacity:1;stroke:none;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-28" + width="7.6862044" + height="13.835168" + x="11.046838" + y="26.27951" /> + <rect + style="fill:#001aff;fill-opacity:1;stroke:none;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-2-9" + width="7.6862044" + height="13.835168" + x="1.823392" + y="26.27951" /> + <rect + style="fill:#001aff;fill-opacity:1;stroke:none;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-0-7" + width="7.6862044" + height="13.835168" + x="27.816751" + y="-34.10545" + transform="rotate(90)" /> + <rect + style="fill:#001aff;fill-opacity:1;stroke:none;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-0-6-3" + width="7.6862044" + height="13.835168" + x="37.040195" + y="-34.10545" + transform="rotate(90)" /> + <rect + style="fill:none;fill-opacity:1;stroke:#121212;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-9-6" + width="7.6862044" + height="13.835168" + x="29.962494" + y="-5.9090691" + transform="rotate(14.022074)" /> + <rect + style="fill:none;fill-opacity:1;stroke:#121212;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-9-0-1" + width="7.6862044" + height="13.835168" + x="39.185944" + y="-5.9090691" + transform="rotate(14.022074)" /> + <rect + style="fill:#001aff;fill-opacity:1;stroke:none;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-3-2" + width="7.6862044" + height="13.835168" + x="-24.742268" + y="7.9723563" + transform="rotate(-90)" /> + <rect + style="fill:#001aff;fill-opacity:1;stroke:none;stroke-width:0.467433;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" + id="rect9322-3-6-9" + width="7.6862044" + height="13.835168" + x="-15.518825" + y="7.9723563" + transform="rotate(-90)" /> + <path + style="fill:#001aff;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1" + d="M 31.780069,17.319588 Z" + id="path2" /> + </g> +</svg> diff --git a/data/crystfel.gresource.xml b/data/crystfel.gresource.xml index c000973a..fa7a1752 100644 --- a/data/crystfel.gresource.xml +++ b/data/crystfel.gresource.xml @@ -21,5 +21,6 @@ <file>crystfel-merge.png</file> <file>crystfel-peak-detection.png</file> <file>crystfel-unitcell.png</file> + <file>crystfel-geometry.png</file> </gresource> </gresources> diff --git a/doc/man/align_detector.1.md b/doc/man/align_detector.1.md index aa5f5061..f5991641 100644 --- a/doc/man/align_detector.1.md +++ b/doc/man/align_detector.1.md @@ -45,6 +45,10 @@ panel "dominoes", and level **3** would refine the individual (roughly square) ASICs. Higher refinement levels will generally require more and higher-resolution diffraction data. +You can use **indexamajig** option **--max-mille-level** to reduce the size of +the calibration data files, at the cost of limiting the maximum refinement +level. + The default behaviour is to refine only the position components in the x-y plane, perpendicular to the beam. In favourable circumstances, you can add option **--out-of-plane** to refine the panel tilts and shifts out of this diff --git a/doc/man/indexamajig.1.md b/doc/man/indexamajig.1.md index 85566240..1955ef76 100644 --- a/doc/man/indexamajig.1.md +++ b/doc/man/indexamajig.1.md @@ -594,6 +594,13 @@ INDEXING OPTIONS **--mille-dir=dirname** : Write the Millepede-II data into _dirname_. +**--max-mille-level=n** +: Write the Millepede-II data up to a maximum hierarchy depth _n_. If _n_ is +: 0, only the overall detector position can be refined. Larger numbers allow +: finer-grained refinement. Set this to a large number (9 is high enough!) +: to disable the limit, although this can make the Millepede-II files quite +: large. + **--wavelength-estimate=m** **--camera-length-estimate=m** : Some indexing algorithms need to know the camera length or the wavelength of : the incident radiation in advance, e.g. to prepare an internal look-up table. diff --git a/libcrystfel/src/crystfel-mille.c b/libcrystfel/src/crystfel-mille.c index 0fca6f30..8eacaa60 100644 --- a/libcrystfel/src/crystfel-mille.c +++ b/libcrystfel/src/crystfel-mille.c @@ -145,11 +145,28 @@ static void mille_add_measurement(Mille *m, } +/* group must not be NULL + * count_depth() = 0 means this is the top group */ +static int count_depth(const struct detgeom_panel_group *group) +{ + int depth = 0; + assert(group != NULL); + do { + depth++; + group = group->parent; + } while ( group != NULL ); + return depth-1; +} + + void write_mille(Mille *mille, int n, UnitCell *cell, struct reflpeak *rps, struct image *image, - gsl_matrix **Minvs) + int max_level, gsl_matrix **Minvs) { int i; + int depth; + + assert(max_level >= 0); /* No groups -> no refinement */ if ( image->detgeom->top_group == NULL ) { @@ -212,8 +229,13 @@ void write_mille(Mille *mille, int n, UnitCell *cell, /* Global gradients for each hierarchy level, starting at the * individual panel and working up to the top level */ j = 0; - levels = 0; group = image->detgeom->panels[rps[i].peak->pn].group; + depth = count_depth(group); + while ( depth > max_level ) { + depth--; + group = group->parent; + } + levels = 0; while ( group != NULL ) { double cx, cy, cz; diff --git a/libcrystfel/src/crystfel-mille.h b/libcrystfel/src/crystfel-mille.h index a4b83815..d6602ef0 100644 --- a/libcrystfel/src/crystfel-mille.h +++ b/libcrystfel/src/crystfel-mille.h @@ -51,7 +51,7 @@ extern enum gparam mille_unlabel(int n); extern void write_mille(Mille *mille, int n, UnitCell *cell, struct reflpeak *rps, struct image *image, - gsl_matrix **Minvs); + int max_level, gsl_matrix **Minvs); extern void crystfel_mille_delete_last_record(Mille *m); diff --git a/libcrystfel/src/index.c b/libcrystfel/src/index.c index f519c3b9..d93664b1 100644 --- a/libcrystfel/src/index.c +++ b/libcrystfel/src/index.c @@ -592,7 +592,7 @@ static float real_time() /* Return non-zero for "success" */ static int try_indexer(struct image *image, IndexingMethod indm, IndexingPrivate *ipriv, void *mpriv, char *last_task, - Mille *mille) + Mille *mille, int max_mille_level) { int i, r; int n_bad = 0; @@ -720,7 +720,7 @@ static int try_indexer(struct image *image, IndexingMethod indm, { int r; profile_start("refine"); - r = refine_prediction(image, cr, mille); + r = refine_prediction(image, cr, mille, max_mille_level); profile_end("refine"); if ( r ) { crystal_set_user_flag(cr, 1); @@ -921,25 +921,25 @@ static int finished_retry(IndexingMethod indm, IndexingFlags flags, void index_pattern(struct image *image, IndexingPrivate *ipriv) { - index_pattern_4(image, ipriv, NULL, NULL, NULL); + index_pattern_4(image, ipriv, NULL, NULL, NULL, 0); } void index_pattern_2(struct image *image, IndexingPrivate *ipriv, int *ping) { - index_pattern_4(image, ipriv, ping, NULL, NULL); + index_pattern_4(image, ipriv, ping, NULL, NULL, 0); } void index_pattern_3(struct image *image, IndexingPrivate *ipriv, int *ping, char *last_task) { - index_pattern_4(image, ipriv, ping, last_task, NULL); + index_pattern_4(image, ipriv, ping, last_task, NULL, 0); } void index_pattern_4(struct image *image, IndexingPrivate *ipriv, int *ping, - char *last_task, Mille *mille) + char *last_task, Mille *mille, int max_mille_level) { int n = 0; ImageFeatureList *orig; @@ -994,7 +994,7 @@ void index_pattern_4(struct image *image, IndexingPrivate *ipriv, int *ping, r = try_indexer(image, ipriv->methods[n], ipriv, ipriv->engine_private[n], - last_task, mille); + last_task, mille, max_mille_level); success += r; ntry++; done = finished_retry(ipriv->methods[n], ipriv->flags, diff --git a/libcrystfel/src/index.h b/libcrystfel/src/index.h index fa371276..10d07f08 100644 --- a/libcrystfel/src/index.h +++ b/libcrystfel/src/index.h @@ -253,7 +253,8 @@ extern void index_pattern_3(struct image *image, IndexingPrivate *ipriv, int *ping, char *last_task); extern void index_pattern_4(struct image *image, IndexingPrivate *ipriv, - int *ping, char *last_task, Mille *mille); + int *ping, char *last_task, Mille *mille, + int max_mille_level); extern void cleanup_indexing(IndexingPrivate *ipriv); diff --git a/libcrystfel/src/predict-refine.c b/libcrystfel/src/predict-refine.c index 32d0f77b..d5a3b403 100644 --- a/libcrystfel/src/predict-refine.c +++ b/libcrystfel/src/predict-refine.c @@ -796,7 +796,8 @@ static void free_rps_noreflist(struct reflpeak *rps, int n) } -int refine_prediction(struct image *image, Crystal *cr, Mille *mille) +int refine_prediction(struct image *image, Crystal *cr, + Mille *mille, int max_mille_level) { int n; int i; @@ -879,7 +880,8 @@ int refine_prediction(struct image *image, Crystal *cr, Mille *mille) if ( mille != NULL ) { profile_start("mille-calc"); - write_mille(mille, n, crystal_get_cell(cr), rps, image, Minvs); + write_mille(mille, n, crystal_get_cell(cr), rps, image, + max_mille_level, Minvs); profile_end("mille-calc"); } diff --git a/libcrystfel/src/predict-refine.h b/libcrystfel/src/predict-refine.h index 604799c0..080e5923 100644 --- a/libcrystfel/src/predict-refine.h +++ b/libcrystfel/src/predict-refine.h @@ -67,7 +67,8 @@ struct reflpeak { * Prediction refinement: refinement of indexing solutions before integration. */ -extern int refine_prediction(struct image *image, Crystal *cr, Mille *mille); +extern int refine_prediction(struct image *image, Crystal *cr, + Mille *mille, int max_mille_level); extern int refine_radius(Crystal *cr, struct image *image); diff --git a/libcrystfel/src/utils.c b/libcrystfel/src/utils.c index 10e4c38e..44faa7ee 100644 --- a/libcrystfel/src/utils.c +++ b/libcrystfel/src/utils.c @@ -933,6 +933,20 @@ int file_exists(const char *filename) } +int is_dir(const char *filename) +{ + struct stat statbuf; + int r; + + r = stat(filename, &statbuf); + if ( r != 0 ) { + return 0; + } + + return S_ISDIR(statbuf.st_mode); +} + + int compare_double(const void *av, const void *bv) { double a = *(double *)av; diff --git a/libcrystfel/src/utils.h b/libcrystfel/src/utils.h index 3a228b22..cb68069e 100644 --- a/libcrystfel/src/utils.h +++ b/libcrystfel/src/utils.h @@ -247,6 +247,7 @@ extern char *safe_strdup(const char *in); extern void strip_extension(char *bfn); extern char *load_entire_file(const char *filename); extern int file_exists(const char *filename); +extern int is_dir(const char *filename); extern const char *filename_extension(const char *fn, const char **ext2); diff --git a/meson.build b/meson.build index 248a75fa..d0cb0081 100644 --- a/meson.build +++ b/meson.build @@ -218,6 +218,7 @@ if gtkdep.found() 'src/gui_fom.c', 'src/gui_export.c', 'src/gui_ambi.c', + 'src/gui_align.c', 'src/gui_backend_local.c', 'src/gui_backend_slurm.c', 'src/gui_project.c', diff --git a/src/align_detector.c b/src/align_detector.c index bdad7039..bb2eb029 100644 --- a/src/align_detector.c +++ b/src/align_detector.c @@ -34,6 +34,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include <dirent.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -255,6 +256,22 @@ static int run_pede() } +static void write_children(FILE *fh, const char *dir) +{ + DIR *d = opendir(dir); + struct dirent *dent = readdir(d); + + while ( dent != NULL ) { + if ( dent->d_name[0] == 'm' ) { + fprintf(fh, "%s/%s\n", dir, dent->d_name); + } + dent = readdir(d); + } + + closedir(d); +} + + int main(int argc, char *argv[]) { int c; @@ -352,7 +369,7 @@ int main(int argc, char *argv[]) r = stat(argv[i], &statbuf); if ( r != 0 ) { - ERROR("File '%s' not found\n", argv[i]); + ERROR("File/directory '%s' not found\n", argv[i]); return 1; } @@ -364,7 +381,11 @@ int main(int argc, char *argv[]) } } - fprintf(fh, "%s\n", argv[i]); + if ( is_dir(argv[i]) ) { + write_children(fh, argv[i]); + } else { + fprintf(fh, "%s\n", argv[i]); + } } if ( warn_times ) print_time_warning(); diff --git a/src/crystfel_gui.c b/src/crystfel_gui.c index d0fab3b1..b3d3bc3f 100644 --- a/src/crystfel_gui.c +++ b/src/crystfel_gui.c @@ -56,6 +56,7 @@ #include "gui_merge.h" #include "gui_fom.h" #include "gui_export.h" +#include "gui_align.h" #include "gui_ambi.h" #include "gui_project.h" #include "version.h" @@ -467,34 +468,6 @@ static gint rescan_sig(GtkWidget *widget, struct crystfelproject *proj) } -static gint detector_shift_sig(GtkWidget *widget, struct crystfelproject *proj) -{ - struct gui_indexing_result *res = current_result(proj); - if ( res != NULL ) { - GError *error = NULL; - const gchar *args[64]; - GSubprocess *sp; - int i; - args[0] = "detector-shift"; - args[1] = "--"; - for ( i=0; i<MIN(res->n_streams, 60); i++ ) { - args[2+i] = res->streams[i]; - } - args[2+res->n_streams] = NULL; - - sp = g_subprocess_newv(args, G_SUBPROCESS_FLAGS_NONE, &error); - if ( sp == NULL ) { - ERROR("Failed to invoke detector-shift: %s\n", - error->message); - g_error_free(error); - } - } else { - ERROR("Select indexing result first!\n"); - } - return FALSE; -} - - static gint peakogram_sig(GtkWidget *widget, struct crystfelproject *proj) { struct gui_indexing_result *res = current_result(proj); @@ -825,7 +798,6 @@ static void add_menu_bar(struct crystfelproject *proj, GtkWidget *vbox) " <menuitem name=\"rescan\" action=\"RescanAction\" />" " <menuitem name=\"jumpframe\" action=\"JumpFrameAction\" />" " <separator />" - " <menuitem name=\"detectorshift\" action=\"DetectorShiftAction\" />" " <menuitem name=\"peakogram\" action=\"PeakogramAction\" />" "</menu>" "<menu name=\"help\" action=\"HelpAction\">" @@ -852,8 +824,6 @@ static void add_menu_bar(struct crystfelproject *proj, GtkWidget *vbox) G_CALLBACK(rescan_sig) }, { "JumpFrameAction", NULL, "Jump to frame", NULL, NULL, G_CALLBACK(goto_frame_sig) }, - { "DetectorShiftAction", NULL, "Check detector shift", NULL, NULL, - G_CALLBACK(detector_shift_sig) }, { "PeakogramAction", NULL, "Check detector saturation (peakogram)", NULL, NULL, G_CALLBACK(peakogram_sig) }, @@ -933,6 +903,8 @@ static void add_task_buttons(GtkWidget *vbox, struct crystfelproject *proj) G_CALLBACK(index_all_sig), proj); add_button(vbox, "Determine unit cell", "crystfel-unitcell", G_CALLBACK(cell_explorer_sig), proj); + add_button(vbox, "Refine detector geometry", "crystfel-geometry", + G_CALLBACK(align_sig), proj); add_button(vbox, "Indexing ambiguity", "crystfel-ambiguity", G_CALLBACK(ambi_sig), proj); add_button(vbox, "Merge", "crystfel-merge", diff --git a/src/crystfelindexingopts.c b/src/crystfelindexingopts.c index d1cf515f..50a36748 100644 --- a/src/crystfelindexingopts.c +++ b/src/crystfelindexingopts.c @@ -734,6 +734,7 @@ static GtkWidget *stream_parameters(CrystFELIndexingOpts *io) GtkCellRenderer *renderer; GtkWidget *button; GtkWidget *hbox; + GtkWidget *label; box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); gtk_container_set_border_width(GTK_CONTAINER(box), 8); @@ -759,6 +760,24 @@ static GtkWidget *stream_parameters(CrystFELIndexingOpts *io) gtk_widget_set_tooltip_text(io->no_refls_in_stream, "--no-refls-in-stream"); + io->millepede = gtk_check_button_new_with_label("Write calibration data for detector alignment"); + gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(io->millepede), + FALSE, FALSE, 4.0); + gtk_widget_set_tooltip_text(io->millepede, "--mille"); + + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(hbox), + FALSE, FALSE, 0.0); + label = gtk_label_new("Maximum refinement level:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 16.0); + io->max_mille = gtk_spin_button_new_with_range(0.0, 9.0, 1.0); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(io->max_mille), + FALSE, FALSE, 4.0); + gtk_widget_set_tooltip_text(io->max_mille, "--max-mille-level"); + i_disable_if_not(io->millepede, io->max_mille); + i_disable_if_not(io->millepede, label); + io->copy_metadata_store = gtk_list_store_new(1, G_TYPE_STRING); treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(io->copy_metadata_store)); @@ -1055,6 +1074,14 @@ int crystfel_indexing_opts_get_exclude_reflections(CrystFELIndexingOpts *opts) } +int crystfel_indexing_opts_get_millepede(CrystFELIndexingOpts *opts, + int *pmax_level) +{ + *pmax_level = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(opts->max_mille)); + return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opts->millepede)); +} + + char **crystfel_indexing_opts_get_metadata_to_copy(CrystFELIndexingOpts *opts, int *pn) { @@ -1381,6 +1408,14 @@ void crystfel_indexing_opts_set_integration_radii(CrystFELIndexingOpts *opts, } +void crystfel_indexing_opts_set_millepede(CrystFELIndexingOpts *opts, + int enable, int max_level) +{ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(opts->max_mille), max_level); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(opts->millepede), enable); +} + + void crystfel_indexing_opts_set_metadata_to_copy(CrystFELIndexingOpts *opts, char *const *headers, int n) diff --git a/src/crystfelindexingopts.h b/src/crystfelindexingopts.h index 567bdb8e..e87d2dac 100644 --- a/src/crystfelindexingopts.h +++ b/src/crystfelindexingopts.h @@ -96,6 +96,8 @@ struct _crystfelindexingopts GtkWidget *exclude_nonhits; GtkWidget *no_peaks_in_stream; GtkWidget *no_refls_in_stream; + GtkWidget *millepede; + GtkWidget *max_mille; GtkListStore *copy_metadata_store; }; @@ -133,6 +135,8 @@ extern int crystfel_indexing_opts_get_exclude_peaks(CrystFELIndexingOpts *opts); extern int crystfel_indexing_opts_get_exclude_reflections(CrystFELIndexingOpts *opts); extern char **crystfel_indexing_opts_get_metadata_to_copy(CrystFELIndexingOpts *opts, int *n); +extern int crystfel_indexing_opts_get_millepede(CrystFELIndexingOpts *opts, + int *pmax_level); extern double crystfel_indexing_opts_get_fixed_profile_radius(CrystFELIndexingOpts *opts, int *active); extern double crystfel_indexing_opts_get_fixed_divergence(CrystFELIndexingOpts *opts); @@ -191,6 +195,10 @@ extern void crystfel_indexing_opts_set_exclude_peaks(CrystFELIndexingOpts *opts, int flag); extern void crystfel_indexing_opts_set_exclude_reflections(CrystFELIndexingOpts *opts, int flag); + +extern void crystfel_indexing_opts_set_millepede(CrystFELIndexingOpts *opts, + int enable, int max_level); + extern void crystfel_indexing_opts_set_fixed_profile_radius(CrystFELIndexingOpts *opts, int active, double val); diff --git a/src/gui_align.c b/src/gui_align.c new file mode 100644 index 00000000..b712681b --- /dev/null +++ b/src/gui_align.c @@ -0,0 +1,235 @@ +/* + * gui_align.c + * + * Align detector via CrystFEL GUI + * + * Copyright © 2024 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2024 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 "version.h" +#include "gui_project.h" +#include "crystfel_gui.h" +#include "gtk-util-routines.h" + + +struct align_window +{ + struct crystfelproject *proj; + GtkWidget *window; + GtkWidget *input_combo; + GtkWidget *out_of_plane; + GtkWidget *level; +}; + + + +static int run_align(const char *input_name, int level, int out_of_plane, + const char *out_geom, struct crystfelproject *proj) +{ + GSubprocess *sp; + struct gui_indexing_result *res; + GError *error; + const char *cmdline[16]; + char level_str[64]; + GFile *gstream; + GFile *gworkdir; + GFile *ggeom; + GFile *gmilledir; + char *input_geom; + int i; + char *mille_dir; + + res = find_indexing_result_by_name(proj, input_name); + if ( res == NULL ) { + ERROR("Results for '%s' not found!\n", input_name); + return 1; + } + + /* Figure out working directory for indexing job */ + if ( res->n_streams < 1 ) { + ERROR("No streams?\n"); + return 1; + } + gstream = g_file_new_for_path(res->streams[0]); + gworkdir = g_file_get_parent(gstream); + g_object_unref(gstream); + + /* Look for Millepede files */ + gmilledir = g_file_get_child(gworkdir, "mille-data"); + if ( !g_file_query_exists(gmilledir, NULL) ) { + ERROR("No detector alignment data found for indexing run '%s'\n", input_name); + return 1; + } + mille_dir = g_file_get_path(gmilledir); + g_object_unref(gmilledir); + + /* Input geometry file */ + ggeom = g_file_get_child(gworkdir, "detector.geom"); + input_geom = g_file_get_path(ggeom); + g_object_unref(ggeom); + + /* Build command line */ + snprintf(level_str, 63, "%i", level); + cmdline[0] = "align_detector"; + cmdline[1] = "-g"; + cmdline[2] = input_geom; + cmdline[3] = "--level"; + cmdline[4] = level_str; + cmdline[5] = "-o"; + cmdline[6] = out_geom; + if ( out_of_plane ) { + cmdline[7] = "--out-of-plane"; + cmdline[8] = mille_dir; + cmdline[9] = NULL; + } else { + cmdline[7] = mille_dir; + cmdline[8] = NULL; + } + + STATUS("Running program: "); + i = 0; + while ( cmdline[i] != NULL ) { + STATUS("%s ", cmdline[i++]); + } + STATUS("\n"); + + error = NULL; + sp = g_subprocess_newv(cmdline, G_SUBPROCESS_FLAGS_NONE, &error); + if ( sp == NULL ) { + ERROR("Failed to run align_detector: %s\n", error->message); + g_error_free(error); + return 1; + } + + g_object_unref(gworkdir); + g_free(mille_dir); + g_free(input_geom); + + return 0; +} + + +static void align_response_sig(GtkWidget *dialog, gint resp, + struct align_window *win) +{ + int r = 0; + + if ( resp == GTK_RESPONSE_ACCEPT ) { + + int level; + const char *input_name; + int out_of_plane; + gchar *filename; + + level = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(win->level)); + + input_name = get_combo_id(win->input_combo); + if ( input_name == NULL ) { + ERROR("Please select the input\n"); + r = 1; + } + + out_of_plane = get_bool(win->out_of_plane); + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + + r = run_align(input_name, level, out_of_plane, filename, win->proj); + + g_free(filename); + } + + if ( !r ) gtk_widget_destroy(dialog); +} + + +gint align_sig(GtkWidget *widget, struct crystfelproject *proj) +{ + GtkWidget *hbox; + GtkWidget *label; + struct align_window *win; + int i; + + win = malloc(sizeof(struct align_window)); + if ( win == NULL ) return 0; + + win->proj = proj; + + win->window = gtk_file_chooser_dialog_new("Align detector", + GTK_WINDOW(proj->window), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(win->window), + TRUE); + + hbox = gtk_hbox_new(FALSE, 0.0); + gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(win->window), + GTK_WIDGET(hbox)); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 4); + + label = gtk_label_new("Refine using indexing result:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 4.0); + win->input_combo = gtk_combo_box_text_new(); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->input_combo), + FALSE, FALSE, 4.0); + for ( i=0; i<proj->n_results; i++ ) { + gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(win->input_combo), + proj->results[i].name, + proj->results[i].name); + } + gtk_combo_box_set_active_id(GTK_COMBO_BOX(win->input_combo), + selected_result(proj)); + + label = gtk_label_new("Hierarchy level:"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), + FALSE, FALSE, 16.0); + win->level = gtk_spin_button_new_with_range(0.0, 9.0, 1.0); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->level), + FALSE, FALSE, 4.0); + gtk_widget_set_tooltip_text(win->level, "--level"); + + win->out_of_plane = gtk_check_button_new_with_label("Include out-of-plane positions and tilts"); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(win->out_of_plane), + FALSE, FALSE, 4.0); + gtk_widget_set_tooltip_text(win->out_of_plane, "--out-of-plane"); + + g_signal_connect(G_OBJECT(win->window), "response", + G_CALLBACK(align_response_sig), win); + + gtk_dialog_set_default_response(GTK_DIALOG(win->window), + GTK_RESPONSE_CLOSE); + gtk_widget_show_all(win->window); + + return FALSE; +} diff --git a/src/gui_align.h b/src/gui_align.h new file mode 100644 index 00000000..50250e6a --- /dev/null +++ b/src/gui_align.h @@ -0,0 +1,38 @@ +/* + * gui_align.h + * + * Align detector via CrystFEL GUI + * + * Copyright © 2024 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2024 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_ALIGN_H +#define GUI_ALIGN_H + +#include <gtk/gtk.h> + +#include "gui_project.h" + +extern gint align_sig(GtkWidget *widget, struct crystfelproject *proj); + +#endif diff --git a/src/gui_backend_local.c b/src/gui_backend_local.c index 03761474..71c94871 100644 --- a/src/gui_backend_local.c +++ b/src/gui_backend_local.c @@ -534,6 +534,10 @@ static void *run_indexing(const char *job_title, gchar *files_rel_filename; gchar *stream_rel_filename; gchar *harvest_rel_filename; + gchar *mille_rel_filename; + GFile *ggeom; + GFile *ggeomcopy; + GError *error; workdir = make_job_folder(job_title, job_notes); if ( workdir == NULL ) return NULL; @@ -555,6 +559,17 @@ static void *run_indexing(const char *job_title, files_rel_filename = relative_to_cwd(workdir, "files.lst"); stream_rel_filename = relative_to_cwd(workdir, "crystfel.stream"); harvest_rel_filename = relative_to_cwd(workdir, "parameters.json"); + mille_rel_filename = relative_to_cwd(workdir, "mille-data"); + + /* Copy geometry file into working directory + * Used for geometry refinement, not indexing! */ + ggeom = g_file_new_for_path(proj->geom_filename); + ggeomcopy = g_file_get_child(workdir, "detector.geom"); + error = NULL; + g_file_copy(ggeom, ggeomcopy, G_FILE_COPY_BACKUP | G_FILE_COPY_ALL_METADATA, + NULL, NULL, NULL, &error); + g_object_unref(ggeom); + g_object_unref(ggeomcopy); if ( !write_indexamajig_script(sc_rel_filename, proj->geom_filename, @@ -564,6 +579,7 @@ static void *run_indexing(const char *job_title, stdout_rel_filename, stderr_rel_filename, harvest_rel_filename, + mille_rel_filename, NULL, &proj->peak_search_params, &proj->indexing_params, @@ -598,6 +614,7 @@ static void *run_indexing(const char *job_title, free(stdout_rel_filename); free(stderr_rel_filename); free(harvest_rel_filename); + free(mille_rel_filename); return job; } diff --git a/src/gui_backend_slurm.c b/src/gui_backend_slurm.c index 3961a92d..f5ad31c5 100644 --- a/src/gui_backend_slurm.c +++ b/src/gui_backend_slurm.c @@ -797,6 +797,7 @@ static void *run_indexing(const char *job_title, gchar *files_rel_filename; gchar *stream_rel_filename; gchar *harvest_rel_filename; + gchar *mille_rel_filename; char *slurm_prologue; workdir = make_job_folder(job_title, job_notes); @@ -842,6 +843,7 @@ static void *run_indexing(const char *job_title, stdout_rel_filename = relative_to_cwd(workdir, "stdout-%a.log"); stderr_rel_filename = relative_to_cwd(workdir, "stderr-%a.log"); harvest_rel_filename = relative_to_cwd(workdir, "parameters.json"); + mille_rel_filename = relative_to_cwd(workdir, "mille-data"); slurm_prologue = sbatch_bits(&opts->common, job_title, array_inx, stdout_rel_filename, stderr_rel_filename); @@ -853,6 +855,7 @@ static void *run_indexing(const char *job_title, stream_rel_filename, NULL, NULL, harvest_rel_filename, + mille_rel_filename, serial_offs, &proj->peak_search_params, &proj->indexing_params, @@ -888,6 +891,7 @@ static void *run_indexing(const char *job_title, free(stdout_rel_filename); free(stderr_rel_filename); free(harvest_rel_filename); + free(mille_rel_filename); g_object_unref(workdir); return job; diff --git a/src/gui_index.c b/src/gui_index.c index aa2301ef..2cc8e8db 100644 --- a/src/gui_index.c +++ b/src/gui_index.c @@ -140,6 +140,8 @@ static void get_indexing_opts(struct crystfelproject *proj, proj->indexing_params.exclude_nonhits = crystfel_indexing_opts_get_exclude_blanks(opts); proj->indexing_params.exclude_peaks = crystfel_indexing_opts_get_exclude_peaks(opts); proj->indexing_params.exclude_refls = crystfel_indexing_opts_get_exclude_reflections(opts); + proj->indexing_params.millepede = crystfel_indexing_opts_get_millepede(opts, + &proj->indexing_params.max_mille_level); proj->indexing_params.metadata_to_copy = crystfel_indexing_opts_get_metadata_to_copy(opts, &proj->indexing_params.n_metadata); } @@ -383,6 +385,9 @@ static void set_indexing_opts(struct crystfelproject *proj, proj->indexing_params.exclude_peaks); crystfel_indexing_opts_set_exclude_reflections(opts, proj->indexing_params.exclude_refls); + crystfel_indexing_opts_set_millepede(opts, + proj->indexing_params.millepede, + proj->indexing_params.max_mille_level); crystfel_indexing_opts_set_metadata_to_copy(opts, proj->indexing_params.metadata_to_copy, proj->indexing_params.n_metadata); @@ -846,6 +851,7 @@ static char **indexamajig_command_line(const char *geom_filename, const char *files_list, const char *stream_filename, const char *harvest_filename, + const char *mille_filename, const char *serial_start, struct peak_params *peak_search_params, struct index_params *indexing_params, @@ -997,6 +1003,11 @@ static char **indexamajig_command_line(const char *geom_filename, if ( indexing_params->exclude_nonhits ) add_arg(args, n_args++, "--no-non-hits-in-stream"); if ( indexing_params->exclude_peaks ) add_arg(args, n_args++, "--no-peaks-in-stream"); if ( indexing_params->exclude_refls ) add_arg(args, n_args++, "--no-refls-in-stream"); + if ( indexing_params->millepede ) { + add_arg(args, n_args++, "--mille"); + add_arg_string(args, n_args++, "mille-dir", mille_filename); + add_arg_int(args, n_args++, "max-mille-level", indexing_params->max_mille_level); + } for ( i=0; i<indexing_params->n_metadata; i++ ) { add_arg_string(args, n_args++, "copy-header", indexing_params->metadata_to_copy[i]); @@ -1079,6 +1090,7 @@ int write_indexamajig_script(const char *script_filename, const char *stdout_filename, const char *stderr_filename, const char *harvest_filename, + const char *mille_filename, const char *serial_start, struct peak_params *peak_search_params, struct index_params *indexing_params, @@ -1095,6 +1107,7 @@ int write_indexamajig_script(const char *script_filename, files_list, stream_filename, harvest_filename, + mille_filename, serial_start, peak_search_params, indexing_params, diff --git a/src/gui_index.h b/src/gui_index.h index 2f5c1a56..33594e8e 100644 --- a/src/gui_index.h +++ b/src/gui_index.h @@ -51,6 +51,7 @@ extern int write_indexamajig_script(const char *script_filename, const char *stdout_filename, const char *stderr_filename, const char *harvest_filename, + const char *mille_filename, const char *serial_start, struct peak_params *peak_search_params, struct index_params *indexing_params, diff --git a/src/gui_project.c b/src/gui_project.c index 32d6473c..c77e2704 100644 --- a/src/gui_project.c +++ b/src/gui_project.c @@ -429,6 +429,12 @@ static void parse_stream_opt(const char *key, const char *val, if ( strcmp(key, "stream.exclude_refls") == 0 ) { ip->exclude_refls = parse_int(val); } + if ( strcmp(key, "stream.millepede") == 0 ) { + ip->millepede = parse_int(val); + } + if ( strcmp(key, "stream.max_mille_level") == 0 ) { + ip->max_mille_level = parse_int(val); + } if ( strcmp(key, "stream.metadata") == 0 ) { add_metadata_to_copy(ip, val); } @@ -1048,6 +1054,10 @@ int save_project(struct crystfelproject *proj) proj->indexing_params.exclude_peaks); fprintf(fh, "stream.exclude_refls %i\n", proj->indexing_params.exclude_refls); + fprintf(fh, "stream.millepede %i\n", + proj->indexing_params.millepede); + fprintf(fh, "stream.max_mille_level %i\n", + proj->indexing_params.max_mille_level); if ( proj->indexing_params.metadata_to_copy != NULL ) { int i; for ( i=0; i<proj->indexing_params.n_metadata; i++ ) { @@ -1287,6 +1297,8 @@ int default_project(struct crystfelproject *proj) proj->indexing_params.exclude_nonhits = 0; proj->indexing_params.exclude_peaks = 0; proj->indexing_params.exclude_refls = 0; + proj->indexing_params.millepede = 1; + proj->indexing_params.max_mille_level = 1; proj->indexing_params.metadata_to_copy = NULL; proj->indexing_params.n_metadata = 0; proj->indexing_params.fix_profile_radius = 0.01e9; diff --git a/src/gui_project.h b/src/gui_project.h index 318f9453..6d704b81 100644 --- a/src/gui_project.h +++ b/src/gui_project.h @@ -87,6 +87,8 @@ struct index_params { int exclude_refls; char **metadata_to_copy; int n_metadata; + int millepede; + int max_mille_level; }; struct merging_params { diff --git a/src/indexamajig.c b/src/indexamajig.c index cca7dbba..ce8b2cbf 100644 --- a/src/indexamajig.c +++ b/src/indexamajig.c @@ -748,6 +748,18 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) args->iargs.milledir = strdup(arg); break; + case 418 : + if (sscanf(arg, "%d", &args->iargs.max_mille_level) != 1) + { + ERROR("Invalid value for --max-mille-level\n"); + return EINVAL; + } + if ( args->iargs.max_mille_level < 0 ) { + ERROR("Invalid value for --max-mille-level\n"); + return EINVAL; + } + break; + /* ---------- Integration ---------- */ case 501 : @@ -996,6 +1008,7 @@ int main(int argc, char *argv[]) args.iargs.data_format = DATA_SOURCE_TYPE_UNKNOWN; args.iargs.mille = 0; args.iargs.milledir = strdup("."); + args.iargs.max_mille_level = 99; argp_program_version_hook = show_version; @@ -1122,8 +1135,8 @@ int main(int argc, char *argv[]) "Estimate of the camera length, in metres."}, {"mille", 416, NULL, 0, "Generate data for detector geometry refinement using Millepede"}, - {"mille-dir", 417, "dirname", 0, - "Save Millepede data in folder"}, + {"mille-dir", 417, "dirname", 0, "Save Millepede data in folder"}, + {"max-mille-level", 418, "n", 0, "Maximum geometry refinement level"}, {NULL, 0, 0, OPTION_DOC, "Integration options:", 5}, {"integration", 501, "method", OPTION_NO_USAGE, "Integration method"}, diff --git a/src/process_image.c b/src/process_image.c index 57a994ac..2f856594 100644 --- a/src/process_image.c +++ b/src/process_image.c @@ -420,7 +420,7 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, set_last_task(last_task, "indexing"); profile_start("index"); index_pattern_4(image, iargs->ipriv, &sb_shared->pings[cookie], - last_task, mille); + last_task, mille, iargs->max_mille_level); profile_end("index"); r = chdir(rn); diff --git a/src/process_image.h b/src/process_image.h index f5e27631..10c9ec31 100644 --- a/src/process_image.h +++ b/src/process_image.h @@ -76,6 +76,7 @@ struct index_args int n_threads; int mille; char *milledir; + int max_mille_level; /* Integration */ IntegrationMethod int_meth; |