aboutsummaryrefslogtreecommitdiff
path: root/libcrystfel/src
diff options
context:
space:
mode:
Diffstat (limited to 'libcrystfel/src')
-rw-r--r--libcrystfel/src/index.c33
-rw-r--r--libcrystfel/src/index.h16
-rw-r--r--libcrystfel/src/indexers/ffbidx.c646
-rw-r--r--libcrystfel/src/indexers/ffbidx.h55
4 files changed, 749 insertions, 1 deletions
diff --git a/libcrystfel/src/index.c b/libcrystfel/src/index.c
index 66075ae3..e74580ec 100644
--- a/libcrystfel/src/index.c
+++ b/libcrystfel/src/index.c
@@ -60,6 +60,7 @@
#include "indexers/xgandalf.h"
#include "indexers/pinkindexer.h"
#include "indexers/fromfile.h"
+#include "indexers/ffbidx.h"
#include "uthash.h"
@@ -145,6 +146,10 @@ char *base_indexer_str(IndexingMethod indm)
strcpy(str, "file");
break;
+ case INDEXING_FFBIDX :
+ strcpy(str, "ffbidx");
+ break;
+
default :
strcpy(str, "(unknown)");
break;
@@ -177,6 +182,7 @@ static void *prepare_method(IndexingMethod *m, UnitCell *cell,
double wavelength_estimate,
double clen_estimate,
struct xgandalf_options *xgandalf_opts,
+ struct ffbidx_options *ffbidx_opts,
struct pinkindexer_options* pinkIndexer_opts,
struct felix_options *felix_opts,
struct taketwo_options *taketwo_opts,
@@ -225,6 +231,10 @@ static void *prepare_method(IndexingMethod *m, UnitCell *cell,
priv = xgandalf_prepare(m, cell, xgandalf_opts);
break;
+ case INDEXING_FFBIDX :
+ priv = ffbidx_prepare(m, cell, ffbidx_opts);
+ break;
+
case INDEXING_PINKINDEXER :
priv = pinkIndexer_prepare(m, cell, pinkIndexer_opts,
wavelength_estimate,
@@ -309,6 +319,7 @@ IndexingPrivate *setup_indexing(const char *method_list,
int n_threads,
struct taketwo_options *ttopts,
struct xgandalf_options *xgandalf_opts,
+ struct ffbidx_options *ffbidx_opts,
struct pinkindexer_options *pinkIndexer_opts,
struct felix_options *felix_opts,
struct fromfile_options *fromfile_opts,
@@ -380,11 +391,12 @@ IndexingPrivate *setup_indexing(const char *method_list,
wavelength_estimate,
clen_estimate,
xgandalf_opts,
+ ffbidx_opts,
pinkIndexer_opts,
felix_opts,
ttopts,
fromfile_opts,
- asdf_opts);
+ asdf_opts);
if ( ipriv->engine_private[i] == NULL ) {
cffree(ipriv->engine_private);
@@ -498,6 +510,10 @@ void cleanup_indexing(IndexingPrivate *ipriv)
xgandalf_cleanup(ipriv->engine_private[n]);
break;
+ case INDEXING_FFBIDX:
+ ffbidx_cleanup(ipriv->engine_private[n]);
+ break;
+
case INDEXING_PINKINDEXER :
pinkIndexer_cleanup(ipriv->engine_private[n]);
break;
@@ -643,6 +659,13 @@ static int try_indexer(struct image *image, IndexingMethod indm,
profile_end("xgandalf");
break;
+ case INDEXING_FFBIDX:
+ set_last_task("indexing:ffbidx");
+ profile_start("ffbidx");
+ r = run_ffbidx(image, mpriv);
+ profile_end("ffbidx");
+ break;
+
default :
ERROR("Unrecognised indexing method: %i\n", indm);
return 0;
@@ -1131,6 +1154,11 @@ IndexingMethod get_indm_from_string_2(const char *str, int *err)
method = INDEXING_FILE;
return method;
+ } else if ( strcmp(bits[i], "ffbidx") == 0) {
+ if ( have_method ) return warn_method(str);
+ method = INDEXING_DEFAULTS_FFBIDX;
+ have_method = 1;
+
} else if ( strcmp(bits[i], "latt") == 0) {
method = set_lattice(method);
@@ -1215,6 +1243,7 @@ char *detect_indexing_methods(UnitCell *cell)
do_probe(asdf_probe, cell, methods);
do_probe(dirax_probe, cell, methods);
do_probe(xds_probe, cell, methods);
+ do_probe(ffbidx_probe, cell, methods);
//do_probe(felix_probe, cell, methods);
//do_probe(pinkIndexer_probe, cell, methods);
@@ -1230,6 +1259,7 @@ char *detect_indexing_methods(UnitCell *cell)
void default_method_options(struct taketwo_options **ttopts,
struct xgandalf_options **xgandalf_opts,
+ struct ffbidx_options **ffbidx_opts,
struct pinkindexer_options **pinkIndexer_opts,
struct felix_options **felix_opts,
struct fromfile_options **fromfile_opts,
@@ -1237,6 +1267,7 @@ void default_method_options(struct taketwo_options **ttopts,
{
taketwo_default_options(ttopts);
xgandalf_default_options(xgandalf_opts);
+ ffbidx_default_options(ffbidx_opts);
pinkIndexer_default_options(pinkIndexer_opts);
felix_default_options(felix_opts);
fromfile_default_options(fromfile_opts);
diff --git a/libcrystfel/src/index.h b/libcrystfel/src/index.h
index c7e9efe3..b6f62bf8 100644
--- a/libcrystfel/src/index.h
+++ b/libcrystfel/src/index.h
@@ -39,6 +39,9 @@
* The indexing subsystem
*/
+#ifdef HAVE_FFBIDX
+#include <ffbidx/c_api.h>
+#endif
#define INDEXING_DEFAULTS_DIRAX (INDEXING_DIRAX)
@@ -61,6 +64,8 @@
#define INDEXING_DEFAULTS_PINKINDEXER (INDEXING_PINKINDEXER | INDEXING_USE_CELL_PARAMETERS)
+#define INDEXING_DEFAULTS_FFBIDX (INDEXING_FFBIDX | INDEXING_USE_CELL_PARAMETERS)
+
/**
* An enumeration of all the available indexing methods.
**/
@@ -78,6 +83,7 @@ typedef enum {
INDEXING_TAKETWO = 9, /**< Use built-in TakeTwo algorithm */
INDEXING_XGANDALF = 10, /**< Use XGANDALF (via optional library) */
INDEXING_PINKINDEXER = 11,/**< Use PinkIndexer (via optional library) */
+ INDEXING_FFBIDX = 12, /**< Use PSI Fast Indexer (via optional library) */
INDEXING_ERROR = 255, /**< Special value for unrecognised indexing
* engine */
@@ -157,6 +163,13 @@ struct xgandalf_options {
int maxPeaksForIndexing;
};
+struct ffbidx_options {
+#ifdef HAVE_FFBIDX
+ struct config_runtime cruntime;
+ struct config_persistent cpers;
+ struct config_ifssr cifssr;
+#endif
+};
struct taketwo_options
{
@@ -216,11 +229,13 @@ extern struct argp felix_argp;
extern struct argp pinkIndexer_argp;
extern struct argp taketwo_argp;
extern struct argp xgandalf_argp;
+extern struct argp ffbidx_argp;
extern struct argp fromfile_argp;
extern struct argp asdf_argp;
extern void default_method_options(struct taketwo_options **ttopts,
struct xgandalf_options **xgandalf_opts,
+ struct ffbidx_options **ffbidx_opts,
struct pinkindexer_options **pinkIndexer_opts,
struct felix_options **felix_opts,
struct fromfile_options **fromfile_opts,
@@ -235,6 +250,7 @@ extern IndexingPrivate *setup_indexing(const char *methods,
int n_threads,
struct taketwo_options *ttopts,
struct xgandalf_options *xgandalf_opts,
+ struct ffbidx_options *ffbidx_opts,
struct pinkindexer_options *pinkIndexer_opts,
struct felix_options *felix_opts,
struct fromfile_options *fromfile_opts,
diff --git a/libcrystfel/src/indexers/ffbidx.c b/libcrystfel/src/indexers/ffbidx.c
new file mode 100644
index 00000000..66cac8db
--- /dev/null
+++ b/libcrystfel/src/indexers/ffbidx.c
@@ -0,0 +1,646 @@
+/*
+ * ffbidx.c
+ *
+ * Invoke the Fast Feedback Indexer library
+ *
+ * Copyright © 2023-2024 Paul Scherrer Institute
+ * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2023-2024 Filip Leonarski <filip.leonarski@psi.ch>
+ *
+ * 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/>.
+ *
+ * Info:
+ * - This code uses the c_api.h defined at
+ * https://github.com/paulscherrerinstitute/fast-feedback-indexer/blob/main/indexer/src/ffbidx/c_api.h
+ * - struct ffbidx_options is defined in crystfels index.h
+ */
+
+#include <libcrystfel-config.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cell-utils.h"
+#include "ffbidx.h"
+
+#ifdef HAVE_FFBIDX
+
+#include <ffbidx/c_api.h>
+
+#define ERR_MSG_LEN 128u
+
+/* ffbidx_private_data state */
+#define STATE_UNDEF 0
+#define STATE_CREATED 1
+#define STATE_USED 2
+
+struct ffbidx_private_data {
+ UnitCell *cellTemplate; // cell template from crystfel, set in ffbidx_prepare()
+ struct ffbidx_options opts; // indexer persistent/runtime/refinement configuration
+ struct input in; // indexer input
+ struct output out; // indexer output
+ int handle; // indexer state handle
+ char msg[ERR_MSG_LEN]; // space for error message
+ struct error err_msg; // error message space description
+ int state;
+};
+
+static void ffbidx_error_msg(struct error *err, const char* activity)
+{
+ const size_t msg_len = 128;
+ char msg[msg_len];
+ if ((err != NULL) && (err->message != NULL)) {
+ snprintf(msg, msg_len, "ffbidx error - %s: %s\n", activity, err->message);
+ ERROR(msg);
+ } else {
+ ERROR("ffbidx error - %s\n", activity);
+ }
+}
+
+static void ffbidx_error(int handle, const char* activity)
+{
+ if (handle>=0) {
+ ffbidx_error_msg(error_message(handle), activity);
+ } else {
+ ERROR("ffbidx: Invalid handle in ffbidx_error()!\n");
+ }
+}
+
+static void check_ffbidx_state(const struct ffbidx_private_data *priv, int state_expected)
+{
+ if (priv == NULL) {
+ fprintf(stderr, "ffbidx - FATAL: private data pointer is NULL!\n");
+ exit(-1);
+ }
+ if (priv->state != state_expected) {
+ fprintf(stderr, "ffbidx - FATAL: state is %d, but should be %d!\n", priv->state, state_expected);
+ exit(-1);
+ }
+}
+
+static void makeRightHanded(UnitCell *cell)
+{
+ // From xgandalf.c
+ double ax, ay, az, bx, by, bz, cx, cy, cz;
+ cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
+
+ if ( !right_handed(cell) ) {
+ cell_set_cartesian(cell, -ax, -ay, -az, -bx, -by, -bz, -cx, -cy, -cz);
+ }
+}
+
+int run_ffbidx(struct image *image, void *ipriv) {
+ unsigned npk, i;
+ int bcell;
+
+ struct ffbidx_private_data *priv = (struct ffbidx_private_data *) ipriv;
+ check_ffbidx_state(priv, STATE_CREATED);
+ priv->state = STATE_USED;
+
+ // how many spots to consider
+ npk = (unsigned)image_feature_count(image->features);
+ if (npk < priv->opts.cifssr.min_spots) {
+ priv->state = STATE_CREATED;
+ return 0; // too few spots
+ }
+
+ if (npk > priv->opts.cpers.max_spots)
+ npk = priv->opts.cpers.max_spots;
+
+ priv->in.n_spots = npk;
+
+ // convert spots to indexer spot input format
+ for ( i=0; i<npk; i++ ) {
+
+ struct imagefeature *f;
+ double r[3];
+
+ f = image_get_feature(image->features, i);
+ if ( f == NULL ) {
+ ERROR("ffbidx: Empty feature ???");
+ continue;
+ }
+
+ detgeom_transform_coords(&image->detgeom->panels[f->pn],
+ f->fs, f->ss, image->lambda,
+ 0.0, 0.0, r);
+ priv->in.spot.x[i] = r[0] * 1e-10;
+ priv->in.spot.y[i] = r[1] * 1e-10;
+ priv->in.spot.z[i] = r[2] * 1e-10;
+ }
+
+ // start indexing
+ if (index_start(priv->handle, &priv->in, &priv->out, &priv->opts.cruntime, NULL, NULL)) {
+ ffbidx_error(priv->handle, "index_start");
+ priv->state = STATE_CREATED;
+ return 0;
+ }
+
+ // wait for indexing output
+ if (index_end(priv->handle, &priv->out)) {
+ ffbidx_error(priv->handle, "index_end");
+ priv->state = STATE_CREATED;
+ return 0;
+ }
+
+ // refine output cells
+ if (refine(priv->handle, &priv->in, &priv->out, &priv->opts.cifssr, 0, 1)) {
+ ffbidx_error(priv->handle, "refine");
+ priv->state = STATE_CREATED;
+ return 0;
+ }
+
+ // cell with best score
+ if ((bcell = best_cell(priv->handle, &priv->out)) < 0) {
+ ffbidx_error(priv->handle, "best_cell");
+ priv->state = STATE_CREATED;
+ return 0;
+ }
+
+ if (priv->opts.cifssr.max_distance < priv->out.score[bcell]) {
+ priv->state = STATE_CREATED;
+ return 0; // cell not viable
+ }
+
+ // convert best cell to crystfel output format
+ UnitCell *uc;
+ uc = cell_new();
+
+ cell_set_cartesian(uc,
+ priv->out.x[bcell * 3 + 0] * 1e-10, priv->out.y[bcell * 3 + 0] * 1e-10, priv->out.z[bcell * 3 + 0] * 1e-10,
+ priv->out.x[bcell * 3 + 1] * 1e-10, priv->out.y[bcell * 3 + 1] * 1e-10, priv->out.z[bcell * 3 + 1] * 1e-10,
+ priv->out.x[bcell * 3 + 2] * 1e-10, priv->out.y[bcell * 3 + 2] * 1e-10, priv->out.z[bcell * 3 + 2] * 1e-10);
+
+ makeRightHanded(uc);
+
+ cell_set_lattice_type(uc, cell_get_lattice_type(priv->cellTemplate));
+ cell_set_centering(uc, cell_get_centering(priv->cellTemplate));
+ cell_set_unique_axis(uc, cell_get_unique_axis(priv->cellTemplate));
+
+ priv->state = STATE_CREATED;
+
+ // validate and produce result in format expected by crystfel
+ if ( validate_cell(uc) ) {
+ ERROR("ffbidx: problem with returned cell!\n");
+ cell_free(uc);
+ return 0;
+ }
+
+ Crystal *cr = crystal_new();
+ if ( cr == NULL ) {
+ ERROR("ffbidx: Failed to allocate crystal.\n");
+ return 0;
+ }
+ crystal_set_cell(cr, uc);
+ crystal_set_det_shift(cr, 0, 0);
+ image_add_crystal(image, cr);
+ return 1;
+}
+
+void *ffbidx_prepare(IndexingMethod *indm, UnitCell *cell, struct ffbidx_options *opts) {
+ if ( cell == NULL ) {
+ ERROR("ffbidx: Unit cell information is required but missing.\n");
+ return NULL;
+ }
+
+ struct ffbidx_private_data *priv = (struct ffbidx_private_data *) calloc(1, sizeof(struct ffbidx_private_data));
+
+ priv->state = STATE_UNDEF;
+ priv->cellTemplate = cell;
+ priv->opts = *opts;
+ priv->err_msg.message = priv->msg;
+ priv->err_msg.msg_len = ERR_MSG_LEN;
+
+ struct config_persistent *cp = &priv->opts.cpers; // for convenientce
+
+ // allocate cell input space for one cell
+ priv->in.cell.x = (float *) calloc(3, sizeof(float));
+ priv->in.cell.y = (float *) calloc(3, sizeof(float));
+ priv->in.cell.z = (float *) calloc(3, sizeof(float));
+ priv->in.n_cells = 1;
+ cp->max_input_cells = 1; // make sure max is 1
+
+ // allocate spot input space for max_spots
+ priv->in.spot.x = (float *) calloc(cp->max_spots, sizeof(float));
+ priv->in.spot.y = (float *) calloc(cp->max_spots, sizeof(float));
+ priv->in.spot.z = (float *) calloc(cp->max_spots, sizeof(float));
+
+ priv->in.new_cells = true; // TODO: only set to true on first indexer run
+ priv->in.new_spots = true;
+
+ // allocate cell and score output space for max_output_cells
+ priv->out.x = (float *) calloc(3 * cp->max_output_cells, sizeof(float));
+ priv->out.y = (float *) calloc(3 * cp->max_output_cells, sizeof(float));
+ priv->out.z = (float *) calloc(3 * cp->max_output_cells, sizeof(float));
+ priv->out.score = (float *) calloc(cp->max_output_cells, sizeof(float));
+ priv->out.n_cells = cp->max_output_cells;
+
+ // convert given cell to indexer cell input format
+ double cell_internal_double[9];
+
+ cell_get_cartesian(priv->cellTemplate,
+ &cell_internal_double[0],&cell_internal_double[3],&cell_internal_double[6],
+ &cell_internal_double[1],&cell_internal_double[4],&cell_internal_double[7],
+ &cell_internal_double[2],&cell_internal_double[5],&cell_internal_double[8]);
+
+ for (int i = 0; i < 3; i++) {
+ priv->in.cell.x[i] = cell_internal_double[0 + i] * 1e10;
+ priv->in.cell.y[i] = cell_internal_double[3 + i] * 1e10;
+ priv->in.cell.z[i] = cell_internal_double[6 + i] * 1e10;
+ }
+
+ // create indexer state
+ priv->handle = create_indexer(&priv->opts.cpers, MEMORY_PIN_STATIC, &priv->err_msg, NULL);
+ if (priv->handle < 0) {
+ ffbidx_error_msg(&priv->err_msg, "create_indexer");
+ return NULL;
+ }
+
+ *indm &= INDEXING_METHOD_MASK | INDEXING_USE_CELL_PARAMETERS;
+ priv->state = STATE_CREATED;
+ return priv;
+}
+
+void ffbidx_cleanup(void *ipriv) {
+ struct ffbidx_private_data *priv = (struct ffbidx_private_data *) ipriv;
+ check_ffbidx_state(priv, STATE_CREATED);
+ priv->state = STATE_UNDEF;
+
+ // drop indexer state
+ if (drop_indexer(priv->handle) != 0) {
+ ERROR("ffbidx: Error dropping indexer\n");
+ }
+
+ // deallocate input spot space
+ free(priv->in.spot.x);
+ free(priv->in.spot.y);
+ free(priv->in.spot.z);
+
+ // deallocate input cell space
+ free(priv->in.cell.x);
+ free(priv->in.cell.y);
+ free(priv->in.cell.z);
+
+ // deallocate output cell and score space
+ free(priv->out.x);
+ free(priv->out.y);
+ free(priv->out.z);
+ free(priv->out.score);
+
+ // deallocate private data
+ free(priv);
+}
+
+const char *ffbidx_probe(UnitCell *cell) {
+ return "ffbidx";
+}
+
+// fill in default option values
+int ffbidx_default_options(struct ffbidx_options **opts_ptr)
+{
+ struct ffbidx_options *opts;
+
+ opts = malloc(sizeof(struct ffbidx_options));
+ if ( opts == NULL ) return ENOMEM;
+
+ set_defaults(&opts->cpers, &opts->cruntime, &opts->cifssr);
+ opts->cpers.max_input_cells = 1;
+
+ *opts_ptr = opts;
+ return 0;
+}
+
+// parse unsigned integer
+int sscanf_uint(const char *arg, unsigned *val) {
+ int tmp = 0;
+ int ret = sscanf(arg, "%d", &tmp);
+ if (ret != 1)
+ return -1;
+ if (tmp < 0)
+ return -1;
+ *val = tmp;
+ return 1;
+}
+
+#else // HAVE_FFBIDX is not set
+
+int run_ffbidx(struct image *image, void *ipriv)
+{
+ ERROR("This copy of CrystFEL was compiled without FFBIDX support.\n");
+ return 0;
+}
+
+
+void *ffbidx_prepare(IndexingMethod *indm, UnitCell *cell, struct ffbidx_options *opts)
+{
+ ERROR("This copy of CrystFEL was compiled without FFBIDX support.\n");
+ ERROR("To use FFBIDX indexing, recompile with FFBIDX.\n");
+ return NULL;
+}
+
+
+void ffbidx_cleanup(void *pp)
+{
+}
+
+
+const char *ffbidx_probe(UnitCell *cell)
+{
+ return NULL;
+}
+
+int ffbidx_default_options(struct ffbidx_options **opts_ptr)
+{
+ return 0;
+}
+
+#endif
+
+static void ffbidx_show_help(void)
+{
+#ifdef HAVE_FFBIDX // print ffbidx option descriptions
+
+ struct config_runtime r;
+ struct config_persistent p;
+ struct config_ifssr i;
+ set_defaults(&p, &r, &i);
+ printf("Parameters for the fast feedback indexing algorithm:\n");
+
+ printf(" --ffbidx-max-spots=\n");
+ printf(" Maximum number of peaks used for indexing.\n");
+ printf(" Default: %d\n", p.max_spots);
+
+ printf(" --ffbidx-output-cells=\n");
+ printf(" Number of output cells.\n");
+ printf(" Default: %d\n", p.max_output_cells);
+
+ printf(" --ffbidx-num-candidate-vectors=\n");
+ printf(" Number of candidate vectors (per input cell vector).\n");
+ printf(" Default: %d\n", p.num_candidate_vectors);
+
+ printf(" --ffbidx-redundant-computations\n");
+ printf(" --ffbidx-no-redundant-computations\n");
+ printf(" Compute candidates for all three cell vectors instead of just one\n");
+ printf(" Default: %s\n", p.redundant_computations
+ ? "--ffbidx-redundant-computations"
+ : "--ffbidx-no-redundant-computations");
+
+ printf(" --ffbidx-length-threshold=\n");
+ printf(" Threshold for determining equal vector length (|va| - threshold < |vb| < |va| + threshold)\n");
+ printf(" Default: %f\n", r.length_threshold);
+
+ printf(" --ffbidx-triml=\n");
+ printf(" Lower trim value for distance to nearest integer objective value - 0 < triml < trimh\n");
+ printf(" Default: %f\n", r.triml);
+
+ printf(" --ffbidx-trimh=\n");
+ printf(" Higher trim value for distance to nearest integer objective value - triml < trimh < 0.5\n");
+ printf(" Default: %f\n", r.trimh);
+
+ printf(" --ffbidx-delta=\n");
+ printf(" Log2 curve position: score = log2(trim(dist(x)) + delta)\n");
+ printf(" Default: %f\n", r.delta);
+
+ printf(" --ffbidx-dist1=\n");
+ printf(" Maximum distance to int for single coordinate\n");
+ printf(" Default: %f\n", r.dist1);
+
+ printf(" --ffbidx-dist3=\n");
+ printf(" Maximum distance to int for triple coordinates\n");
+ printf(" Default: %f\n", r.dist3);
+
+ printf(" --ffbidx-min-spots=\n");
+ printf(" Vector refinement: minimum number of peaks to fit against\n");
+ printf(" Default: %d\n", r.min_spots);
+
+ printf(" --ffbidx-num-halfsphere-points=\n");
+ printf(" Number of sample points on half sphere for finding vector candidates\n");
+ printf(" Default: %d\n", r.num_halfsphere_points);
+
+ printf(" --ffbidx-num-angle-points=\n");
+ printf(" Number of sample points in rotation space for finding cell candidates (0: auto)\n");
+ printf(" Default: %d\n", r.num_angle_points);
+
+ printf("\n");
+ printf(" --ffbidx-ifssr-min-spots=\n");
+ printf(" Cell refinement: minimum number of peaks to fit against\n");
+ printf(" Default: %d\n", i.min_spots);
+
+ printf(" --ffbidx-ifssr-max-dist=\n");
+ printf(" Cell refinement: max distance to reciprocal spots for inliers\n");
+ printf(" Default: %f\n", i.max_distance);
+
+ printf(" --ffbidx-ifssr-max-iter=\n");
+ printf(" Cell refinement: max number of iterations\n");
+ printf(" Default: %d\n", i.max_iter);
+
+ printf(" --ffbidx-ifssr-threshold-contraction=\n");
+ printf(" Cell refinement: contract error threshold by this value in every iteration\n");
+ printf(" Default: %f\n", i.threshold_contraction);
+
+#else // HAVE_FFBIDX is not set
+
+ printf("This copy of CrystFEL was compiled without FFBIDX support.\n");
+
+#endif
+}
+
+static error_t ffbidx_parse_arg(int key, char *arg, struct argp_state *state)
+{
+#ifdef HAVE_FFBIDX // parse all ffbidx options
+
+ struct ffbidx_options **opts_ptr = state->input;
+ int r;
+
+ switch ( key ) {
+ case ARGP_KEY_INIT :
+ r = ffbidx_default_options(opts_ptr);
+ if ( r ) return r;
+ break;
+
+ case 1:
+ ffbidx_show_help();
+ return EINVAL;
+
+ case 2:
+ if (sscanf_uint(arg, &(*opts_ptr)->cpers.max_spots) != 1) {
+ ERROR("Invalid value for --ffbidx-max-spots\n");
+ return EINVAL;
+ }
+ break;
+ case 3:
+ if (sscanf_uint(arg, &(*opts_ptr)->cifssr.min_spots) != 1) {
+ ERROR("Invalid value for --ffbidx-ifssr-min-spots\n");
+ return EINVAL;
+ }
+ break;
+ case 4:
+ if (sscanf_uint(arg, &(*opts_ptr)->cpers.max_output_cells) != 1) {
+ ERROR("Invalid value for --ffbidx-output-cells\n");
+ return EINVAL;
+ }
+ break;
+ case 5:
+ if (sscanf_uint(arg, &(*opts_ptr)->cpers.num_candidate_vectors) != 1) {
+ ERROR("Invalid value for --ffbidx-num-candidate-vectors\n");
+ return EINVAL;
+ }
+ break;
+ case 6:
+ (*opts_ptr)->cpers.redundant_computations = true;
+ break;
+ case 7:
+ (*opts_ptr)->cpers.redundant_computations = false;
+ break;
+ case 8:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cifssr.max_distance) != 1) {
+ ERROR("Invalid value for --ffbidx-ifssr-max-dist\n");
+ return EINVAL;
+ }
+ break;
+ case 9:
+ if (sscanf_uint(arg, &(*opts_ptr)->cifssr.max_iter) != 1) {
+ ERROR("Invalid value for --ffbidx-ifssr-max-iter\n");
+ return EINVAL;
+ }
+ break;
+ case 10:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cifssr.threshold_contraction) != 1) {
+ ERROR("Invalid value for --ffbidx-ifssr-threshold-contraction\n");
+ return EINVAL;
+ }
+ break;
+ case 11:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cruntime.length_threshold) != 1) {
+ ERROR("Invalid value for --ffbidx-length-threshold\n");
+ return EINVAL;
+ }
+ break;
+ case 12:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cruntime.triml) != 1) {
+ ERROR("Invalid value for --ffbidx-triml\n");
+ return EINVAL;
+ }
+ break;
+ case 13:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cruntime.trimh) != 1) {
+ ERROR("Invalid value for --ffbidx-trimh\n");
+ return EINVAL;
+ }
+ break;
+ case 14:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cruntime.delta) != 1) {
+ ERROR("Invalid value for --ffbidx-delta\n");
+ return EINVAL;
+ }
+ break;
+ case 15:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cruntime.dist1) != 1) {
+ ERROR("Invalid value for --ffbidx-dist1\n");
+ return EINVAL;
+ }
+ break;
+ case 16:
+ if (sscanf(arg, "%f", &(*opts_ptr)->cruntime.dist3) != 1) {
+ ERROR("Invalid value for --ffbidx-dist3\n");
+ return EINVAL;
+ }
+ break;
+
+ case 17:
+ if (sscanf_uint(arg, &(*opts_ptr)->cruntime.num_halfsphere_points) != 1) {
+ ERROR("Invalid value for --ffbidx-num-halfsphere-points\n");
+ return EINVAL;
+ }
+ break;
+ case 18:
+ if (sscanf_uint(arg, &(*opts_ptr)->cruntime.num_angle_points) != 1) {
+ ERROR("Invalid value for --ffbidx-num-angle-points\n");
+ return EINVAL;
+ }
+ break;
+ case 19:
+ if (sscanf_uint(arg, &(*opts_ptr)->cruntime.min_spots) != 1) {
+ ERROR("Invalid value for --ffbidx-min-spots\n");
+ return EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ char msg[256];
+ memset(msg, 0, 256);
+ struct error ffbidx_err;
+ ffbidx_err.msg_len = 255;
+ ffbidx_err.message = msg;
+ if (check_config(&(*opts_ptr)->cpers,
+ &(*opts_ptr)->cruntime,
+ &(*opts_ptr)->cifssr,
+ &ffbidx_err) != 0) {
+ ERROR(msg);
+ return EINVAL;
+ }
+
+#else // HAVE_FFBIDX is not set, parse only --help-ffbidx
+
+ switch ( key ) {
+ case 1:
+ ffbidx_show_help();
+ return EINVAL;
+
+ default:
+ break;
+ }
+
+#endif
+
+ return 0;
+}
+
+
+static struct argp_option ffbidx_options[] = {
+ {"help-ffbidx", 1, NULL, OPTION_NO_USAGE, "Show options for fast feedback indexing algorithm", 99},
+
+#ifdef HAVE_FFBIDX // these options are only relevant if ffbidx is present
+ {"ffbidx-max-spots", 2, "ffbidx_maxn", OPTION_HIDDEN, NULL},
+ {"ffbidx-ifssr-min-spots", 3, "ffbidx_cminn", OPTION_HIDDEN, NULL},
+ {"ffbidx-output-cells", 4, "ffbidx_out_cells", OPTION_HIDDEN, NULL},
+ {"ffbidx-num-candidate-vectors", 5, "ffbidx_num_candidate_vectors", OPTION_HIDDEN, NULL},
+ {"ffbidx-redundant-computations", 6, NULL, OPTION_HIDDEN, NULL},
+ {"ffbidx-no-redundant-computations", 7, NULL, OPTION_HIDDEN, NULL},
+ {"ffbidx-ifssr-max-dist", 8, "ffbidx_max_dist", OPTION_HIDDEN, NULL},
+ {"ffbidx-ifssr-max-iter", 9, "ffbidx_max_iter", OPTION_HIDDEN, NULL},
+ {"ffbidx-ifssr-threshold-contraction", 10, "ffbidx_thrc", OPTION_HIDDEN, NULL},
+ {"ffbidx-length-threshold", 11, "ffbidx_lt", OPTION_HIDDEN, NULL},
+ {"ffbidx-triml", 12, "ffbidx_lt", OPTION_HIDDEN, NULL},
+ {"ffbidx-trimh", 13, "ffbidx_lh", OPTION_HIDDEN, NULL},
+ {"ffbidx-delta", 14, "ffbidx_delta", OPTION_HIDDEN, NULL},
+ {"ffbidx-dist1", 15, "ffbidx_dist1", OPTION_HIDDEN, NULL},
+ {"ffbidx-dist3", 16, "ffbidx_dist3", OPTION_HIDDEN, NULL},
+ {"ffbidx-num-halfsphere-points", 17, "ffbidx_nhp", OPTION_HIDDEN, NULL},
+ {"ffbidx-num-angle-points", 18, "ffbidx_nap", OPTION_HIDDEN, NULL},
+ {"ffbidx-min-spots", 19, "ffbidx_vminn", OPTION_HIDDEN, NULL},
+#endif
+
+ {0}
+};
+
+
+struct argp ffbidx_argp = { ffbidx_options, ffbidx_parse_arg,
+ NULL, NULL, NULL, NULL, NULL };
diff --git a/libcrystfel/src/indexers/ffbidx.h b/libcrystfel/src/indexers/ffbidx.h
new file mode 100644
index 00000000..52df91e4
--- /dev/null
+++ b/libcrystfel/src/indexers/ffbidx.h
@@ -0,0 +1,55 @@
+/*
+ * ffbidx.h
+ *
+ * Invoke the Fast Feedback Indexer library
+ *
+ * Copyright © 2023 Paul Scherrer Institute
+ * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2023 Filip Leonarski <filip.leonarski@psi.ch>
+ *
+ * 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 CRYSTFEL_FFBIDX_H
+#define CRYSTFEL_FFBIDX_H
+
+#include "index.h"
+
+#include <argp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int ffbidx_default_options(struct ffbidx_options **opts_ptr);
+
+extern int run_ffbidx(struct image *image, void *ipriv);
+
+extern void *ffbidx_prepare(IndexingMethod *indm, UnitCell *cell, struct ffbidx_options *opts);
+
+extern void ffbidx_cleanup(void *pp);
+
+extern const char *ffbidx_probe(UnitCell *cell);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CRYSTFEL_FFBIDX_H