aboutsummaryrefslogtreecommitdiff
path: root/libcrystfel
diff options
context:
space:
mode:
authorThomas White <taw@physics.org>2020-02-21 17:02:26 +0100
committerThomas White <taw@physics.org>2020-07-29 18:39:50 +0200
commit8c63113ccd59c3b5498cbd68800c29536a52092d (patch)
treedd73b4b3d2340da7bb8db1cdb00b3adcf5348932 /libcrystfel
parent12fa6375acb536e61f41c006fc783e5e8f0c7a92 (diff)
Initial DataTemplate parser (copied from get_detector_geometry)
Diffstat (limited to 'libcrystfel')
-rw-r--r--libcrystfel/src/datatemplate.c1277
-rw-r--r--libcrystfel/src/datatemplate_priv.h189
2 files changed, 1367 insertions, 99 deletions
diff --git a/libcrystfel/src/datatemplate.c b/libcrystfel/src/datatemplate.c
index 22bad02c..bff8eccf 100644
--- a/libcrystfel/src/datatemplate.c
+++ b/libcrystfel/src/datatemplate.c
@@ -31,133 +31,1212 @@
#endif
#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "utils.h"
#include "datatemplate.h"
+#include "events.h"
+
+#include "datatemplate_priv.h"
/**
* \file datatemplate.h
*/
-enum adu_per_unit
-{
- ADU_PER_PHOTON,
- ADU_PER_EV
+struct rg_definition {
+ char *name;
+ char *pns;
};
-/**
- * Represents one panel of a detector
- */
-struct panel_template
-{
- /** Text name for panel (fixed length array) */
- char name[1024];
-
- /** \name Location of corner in units of the pixel size of this panel */
- /**@{*/
- double cnx;
- double cny;
- /**@}*/
-
- /** Location to get \ref cnz from, e.g. from HDF5 file */
- char *cnz_from;
-
- /** The offset to be applied from \ref clen */
- double coffset;
-
- /** Location of mask data */
- char *mask;
-
- /** Filename for mask data */
- char *mask_file;
-
- /** Location of per-pixel saturation map */
- char *satmap;
-
- /** Filename for saturation map */
- char *satmap_file;
-
- /** Resolution in pixels per metre */
- double pixel_pitch;
-
- /** Readout direction (for filtering out clusters of peaks)
- * ('x' or 'y') */
- char badrow;
-
- /** Number of detector intensity units per photon, or eV */
- double adu_scale;
- enum adu_per_unit adu_scale_unit;
-
- /** Treat pixel as unreliable if higher than this */
- double max_adu;
-
- /** Location of data in file */
- char *data;
-
- /** Dimension structure */
- struct dim_structure *dim_structure;
-
- /** \name Transformation matrix from pixel coordinates to lab frame */
- /*@{*/
- double fsx;
- double fsy;
- double fsz;
- double ssx;
- double ssy;
- double ssz;
- /*@}*/
-
- /** \name Rail direction */
- /*@{*/
- double rail_x;
- double rail_y;
- double rail_z;
- /*@}*/
-
- /* Value of clen (without coffset) at which beam is centered */
- double clen_for_centering;
-
- /** \name Position of the panel in the data block in the file. */
- /*@{*/
- int orig_min_fs;
- int orig_max_fs;
- int orig_min_ss;
- int orig_max_ss;
- /*@}*/
+struct rgc_definition {
+ char *name;
+ char *rgs;
};
-struct _datatemplate
+static struct panel_template *new_panel(DataTemplate *det, const char *name)
{
- struct panel_template *panels;
- int n_panels;
+ struct panel_template *new;
- struct badregion *bad;
- int n_bad;
+ det->n_panels++;
+ det->panels = realloc(det->panels,
+ det->n_panels*sizeof(struct panel_template));
- unsigned int mask_bad;
- unsigned int mask_good;
+ new = &det->panels[det->n_panels-1];
+ memcpy(new, &det->defaults, sizeof(struct panel_template));
- struct rigid_group **rigid_groups;
- int n_rigid_groups;
+ new->name = strdup(name);
- struct rg_collection **rigid_group_collections;
- int n_rg_collections;
+ /* Copy strings */
+ if ( new->cnz_from != NULL ) new->cnz_from = strdup(new->cnz_from);
+ if ( new->data != NULL ) new->data = strdup(new->data);
+ if ( new->mask != NULL ) new->mask = strdup(new->mask);
- int path_dim;
- int dim_dim;
+ /* Create a new copy of the dim_structure if needed */
+ if ( new->dim_structure != NULL ) {
- struct panel_template defaults;
-};
+ struct dim_structure *dim_copy;
+ int di;
+ dim_copy = initialize_dim_structure();
+ dim_copy->num_dims = new->dim_structure->num_dims;
+ dim_copy->dims = malloc(dim_copy->num_dims*sizeof(int));
+ for ( di=0; di<dim_copy->num_dims; di++ ) {
+ dim_copy->dims[di] = new->dim_structure->dims[di];
+ }
-DataTemplate *data_template_new_from_file(const char *filename)
+ new->dim_structure = dim_copy;
+ }
+
+ return new;
+}
+
+
+static struct dt_badregion *new_bad_region(DataTemplate *det, const char *name)
+{
+ struct dt_badregion *new;
+
+ det->n_bad++;
+ det->bad = realloc(det->bad, det->n_bad*sizeof(struct dt_badregion));
+
+ new = &det->bad[det->n_bad-1];
+ new->min_x = NAN;
+ new->max_x = NAN;
+ new->min_y = NAN;
+ new->max_y = NAN;
+ new->min_fs = 0;
+ new->max_fs = 0;
+ new->min_ss = 0;
+ new->max_ss = 0;
+ new->is_fsss = 99; /* Slightly nasty: means "unassigned" */
+ new->panel = NULL;
+ strcpy(new->name, name);
+
+ return new;
+}
+
+
+static struct panel_template *find_panel_by_name(DataTemplate *det, const char *name)
{
+ int i;
+
+ for ( i=0; i<det->n_panels; i++ ) {
+ if ( strcmp(det->panels[i].name, name) == 0 ) {
+ return &det->panels[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static struct dt_badregion *find_bad_region_by_name(DataTemplate *det,
+ const char *name)
+{
+ int i;
+
+ for ( i=0; i<det->n_bad; i++ ) {
+ if ( strcmp(det->bad[i].name, name) == 0 ) {
+ return &det->bad[i];
+ }
+ }
+
return NULL;
}
+static struct dt_rigid_group *find_or_add_rg(DataTemplate *det,
+ const char *name)
+{
+ int i;
+ struct dt_rigid_group **new;
+ struct dt_rigid_group *rg;
+
+ for ( i=0; i<det->n_rigid_groups; i++ ) {
+
+ if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) {
+ return det->rigid_groups[i];
+ }
+
+ }
+
+ new = realloc(det->rigid_groups,
+ (1+det->n_rigid_groups)*sizeof(struct dt_rigid_group *));
+ if ( new == NULL ) return NULL;
+
+ det->rigid_groups = new;
+
+ rg = malloc(sizeof(struct dt_rigid_group));
+ if ( rg == NULL ) return NULL;
+
+ det->rigid_groups[det->n_rigid_groups++] = rg;
+
+ rg->name = strdup(name);
+ rg->panels = NULL;
+ rg->n_panels = 0;
+
+ return rg;
+}
+
+
+static struct dt_rg_collection *find_or_add_rg_coll(DataTemplate *det,
+ const char *name)
+{
+ int i;
+ struct dt_rg_collection **new;
+ struct dt_rg_collection *rgc;
+
+ for ( i=0; i<det->n_rg_collections; i++ ) {
+ if ( strcmp(det->rigid_group_collections[i]->name, name) == 0 )
+ {
+ return det->rigid_group_collections[i];
+ }
+ }
+
+ new = realloc(det->rigid_group_collections,
+ (1+det->n_rg_collections)*sizeof(struct dt_rg_collection *));
+ if ( new == NULL ) return NULL;
+
+ det->rigid_group_collections = new;
+
+ rgc = malloc(sizeof(struct dt_rg_collection));
+ if ( rgc == NULL ) return NULL;
+
+ det->rigid_group_collections[det->n_rg_collections++] = rgc;
+
+ rgc->name = strdup(name);
+ rgc->rigid_groups = NULL;
+ rgc->n_rigid_groups = 0;
+
+ return rgc;
+}
+
+
+static void add_to_rigid_group(struct dt_rigid_group *rg, struct panel_template *p)
+{
+ struct panel_template **pn;
+
+ pn = realloc(rg->panels, (1+rg->n_panels)*sizeof(struct panel_template *));
+ if ( pn == NULL ) {
+ ERROR("Couldn't add panel to rigid group.\n");
+ return;
+ }
+
+ rg->panels = pn;
+ rg->panels[rg->n_panels++] = p;
+}
+
+
+static void add_to_rigid_group_coll(struct dt_rg_collection *rgc,
+ struct dt_rigid_group *rg)
+{
+ struct dt_rigid_group **r;
+
+ r = realloc(rgc->rigid_groups, (1+rgc->n_rigid_groups)*
+ sizeof(struct dt_rigid_group *));
+ if ( r == NULL ) {
+ ERROR("Couldn't add rigid group to collection.\n");
+ return;
+ }
+
+ rgc->rigid_groups = r;
+ rgc->rigid_groups[rgc->n_rigid_groups++] = rg;
+}
+
+
+/* Free all rigid groups in detector */
+static void free_all_rigid_groups(DataTemplate *det)
+{
+ int i;
+
+ if ( det->rigid_groups == NULL ) return;
+ for ( i=0; i<det->n_rigid_groups; i++ ) {
+ free(det->rigid_groups[i]->name);
+ free(det->rigid_groups[i]->panels);
+ free(det->rigid_groups[i]);
+ }
+ free(det->rigid_groups);
+}
+
+
+/* Free all rigid groups in detector */
+static void free_all_rigid_group_collections(DataTemplate *det)
+{
+ int i;
+
+ if ( det->rigid_group_collections == NULL ) return;
+ for ( i=0; i<det->n_rg_collections; i++ ) {
+ free(det->rigid_group_collections[i]->name);
+ free(det->rigid_group_collections[i]->rigid_groups);
+ free(det->rigid_group_collections[i]);
+ }
+ free(det->rigid_group_collections);
+}
+
+
+static struct dt_rigid_group *find_rigid_group_by_name(DataTemplate *det,
+ char *name)
+{
+ int i;
+
+ for ( i=0; i<det->n_rigid_groups; i++ ) {
+ if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) {
+ return det->rigid_groups[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static int atob(const char *a)
+{
+ if ( strcasecmp(a, "true") == 0 ) return 1;
+ if ( strcasecmp(a, "false") == 0 ) return 0;
+ return atoi(a);
+}
+
+
+static int assplode_algebraic(const char *a_orig, char ***pbits)
+{
+ int len, i;
+ int nexp;
+ char **bits;
+ char *a;
+ int idx, istr;
+
+ len = strlen(a_orig);
+
+ /* Add plus at start if no sign there already */
+ if ( (a_orig[0] != '+') && (a_orig[0] != '-') ) {
+ len += 1;
+ a = malloc(len+1);
+ snprintf(a, len+1, "+%s", a_orig);
+ a[len] = '\0';
+
+ } else {
+ a = strdup(a_orig);
+ }
+
+ /* Count the expressions */
+ nexp = 0;
+ for ( i=0; i<len; i++ ) {
+ if ( (a[i] == '+') || (a[i] == '-') ) nexp++;
+ }
+
+ bits = calloc(nexp, sizeof(char *));
+
+ /* Break the string up */
+ idx = -1;
+ istr = 0;
+ assert((a[0] == '+') || (a[0] == '-'));
+ for ( i=0; i<len; i++ ) {
+
+ char ch;
+
+ ch = a[i];
+
+ if ( (ch == '+') || (ch == '-') ) {
+ if ( idx >= 0 ) bits[idx][istr] = '\0';
+ idx++;
+ bits[idx] = malloc(len+1);
+ istr = 0;
+ }
+
+ if ( !isdigit(ch) && (ch != '.') && (ch != '+') && (ch != '-')
+ && (ch != 'x') && (ch != 'y') && (ch != 'z') )
+ {
+ ERROR("Invalid character '%c' found.\n", ch);
+ return 0;
+ }
+
+ assert(idx >= 0);
+ bits[idx][istr++] = ch;
+
+ }
+ if ( idx >= 0 ) bits[idx][istr] = '\0';
+
+ *pbits = bits;
+ free(a);
+
+ return nexp;
+}
+
+
+/* Parses the scan directions (accounting for possible rotation)
+ * Assumes all white spaces have been already removed */
+static int dir_conv(const char *a, double *sx, double *sy, double *sz)
+{
+ int n;
+ char **bits;
+ int i;
+
+ *sx = 0.0; *sy = 0.0; *sz = 0.0;
+
+ n = assplode_algebraic(a, &bits);
+
+ if ( n == 0 ) {
+ ERROR("Invalid direction '%s'\n", a);
+ return 1;
+ }
+
+ for ( i=0; i<n; i++ ) {
+
+ int len;
+ double val;
+ char axis;
+ int j;
+
+ len = strlen(bits[i]);
+ assert(len != 0);
+ axis = bits[i][len-1];
+ if ( (axis != 'x') && (axis != 'y') && (axis != 'z') ) {
+ ERROR("Invalid symbol '%c' - must be x, y or z.\n",
+ axis);
+ return 1;
+ }
+
+ /* Chop off the symbol now it's dealt with */
+ bits[i][len-1] = '\0';
+
+ /* Check for anything that isn't part of a number */
+ for ( j=0; j<strlen(bits[i]); j++ ) {
+ if ( isdigit(bits[i][j]) ) continue;
+ if ( bits[i][j] == '+' ) continue;
+ if ( bits[i][j] == '-' ) continue;
+ if ( bits[i][j] == '.' ) continue;
+ ERROR("Invalid coefficient '%s'\n", bits[i]);
+ }
+
+ if ( strlen(bits[i]) == 0 ) {
+ val = 1.0;
+ } else {
+ val = atof(bits[i]);
+ }
+ if ( strlen(bits[i]) == 1 ) {
+ if ( bits[i][0] == '+' ) val = 1.0;
+ if ( bits[i][0] == '-' ) val = -1.0;
+ }
+ switch ( axis ) {
+
+ case 'x' :
+ *sx += val;
+ break;
+
+ case 'y' :
+ *sy += val;
+ break;
+
+ case 'z' :
+ *sz += val;
+ break;
+ }
+
+ free(bits[i]);
+
+ }
+ free(bits);
+
+ return 0;
+}
+
+
+static int parse_field_for_panel(struct panel_template *panel, const char *key,
+ const char *val, DataTemplate *det)
+{
+ int reject = 0;
+
+ if ( strcmp(key, "min_fs") == 0 ) {
+ panel->orig_min_fs = atof(val);
+ } else if ( strcmp(key, "max_fs") == 0 ) {
+ panel->orig_max_fs = atof(val);
+ } else if ( strcmp(key, "min_ss") == 0 ) {
+ panel->orig_min_ss = atof(val);
+ } else if ( strcmp(key, "max_ss") == 0 ) {
+ panel->orig_max_ss = atof(val);
+ } else if ( strcmp(key, "corner_x") == 0 ) {
+ panel->cnx = atof(val);
+ } else if ( strcmp(key, "corner_y") == 0 ) {
+ panel->cny = atof(val);
+ } else if ( strcmp(key, "rail_direction") == 0 ) {
+ if ( dir_conv(val, &panel->rail_x,
+ &panel->rail_y,
+ &panel->rail_z) )
+ {
+ ERROR("Invalid rail direction '%s'\n", val);
+ reject = 1;
+ }
+ } else if ( strcmp(key, "clen_for_centering") == 0 ) {
+ panel->clen_for_centering = atof(val);
+ } else if ( strcmp(key, "adu_per_eV") == 0 ) {
+ panel->adu_per_eV = atof(val);
+ } else if ( strcmp(key, "adu_per_photon") == 0 ) {
+ panel->adu_per_photon = atof(val);
+ } else if ( strcmp(key, "rigid_group") == 0 ) {
+ add_to_rigid_group(find_or_add_rg(det, val), panel);
+ } else if ( strcmp(key, "clen") == 0 ) {
+ /* Gets expanded when image is loaded */
+ panel->cnz_from = strdup(val);
+
+ } else if ( strcmp(key, "data") == 0 ) {
+ if ( strncmp(val,"/",1) != 0 ) {
+ ERROR("Invalid data location '%s'\n", val);
+ reject = -1;
+ }
+ panel->data = strdup(val);
+
+ } else if ( strcmp(key, "mask") == 0 ) {
+ if ( strncmp(val,"/",1) != 0 ) {
+ ERROR("Invalid mask location '%s'\n", val);
+ reject = -1;
+ }
+ panel->mask = strdup(val);
+
+ } else if ( strcmp(key, "mask_file") == 0 ) {
+ panel->mask_file = strdup(val);
+
+ } else if ( strcmp(key, "saturation_map") == 0 ) {
+ panel->satmap = strdup(val);
+ } else if ( strcmp(key, "saturation_map_file") == 0 ) {
+ panel->satmap_file = strdup(val);
+
+ } else if ( strcmp(key, "coffset") == 0) {
+ panel->cnz_offset = atof(val);
+ } else if ( strcmp(key, "res") == 0 ) {
+ panel->pixel_pitch = 1.0/atof(val);
+ } else if ( strcmp(key, "max_adu") == 0 ) {
+ panel->max_adu = atof(val);
+ } else if ( strcmp(key, "badrow_direction") == 0 ) {
+ ERROR("WARNING 'badrow_direction' is ignored in this version.\n");
+ } else if ( strcmp(key, "no_index") == 0 ) {
+ panel->bad = atob(val);
+ } else if ( strcmp(key, "fs") == 0 ) {
+ if ( dir_conv(val, &panel->fsx, &panel->fsy, &panel->fsz) != 0 )
+ {
+ ERROR("Invalid fast scan direction '%s'\n", val);
+ reject = 1;
+ }
+ } else if ( strcmp(key, "ss") == 0 ) {
+ if ( dir_conv(val, &panel->ssx, &panel->ssy, &panel->ssz) != 0 )
+ {
+ ERROR("Invalid slow scan direction '%s'\n", val);
+ reject = 1;
+ }
+ } else if ( strncmp(key, "dim", 3) == 0) {
+ int dim_entry;
+ char *endptr;
+ if ( key[3] != '\0' ) {
+ if ( panel->dim_structure == NULL ) {
+ panel->dim_structure = initialize_dim_structure();
+ }
+ dim_entry = strtoul(key+3, &endptr, 10);
+ if ( endptr[0] != '\0' ) {
+ ERROR("Invalid dimension number %s\n", key+3);
+ } else {
+ if ( set_dim_structure_entry(panel->dim_structure,
+ dim_entry, val) )
+ {
+ ERROR("Failed to set dim structure entry\n");
+ }
+ }
+ } else {
+ ERROR("'dim' must be followed by a number, e.g. 'dim0'\n");
+ }
+ } else {
+ ERROR("Unrecognised field '%s'\n", key);
+ }
+
+ return reject;
+}
+
+
+static int check_badr_fsss(struct dt_badregion *badr, int is_fsss)
+{
+ /* First assignment? */
+ if ( badr->is_fsss == 99 ) {
+ badr->is_fsss = is_fsss;
+ return 0;
+ }
+
+ if ( is_fsss != badr->is_fsss ) {
+ ERROR("You can't mix x/y and fs/ss in a bad region.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int parse_field_bad(struct dt_badregion *badr, const char *key,
+ const char *val)
+{
+ int reject = 0;
+
+ if ( strcmp(key, "min_x") == 0 ) {
+ badr->min_x = atof(val);
+ reject = check_badr_fsss(badr, 0);
+ } else if ( strcmp(key, "max_x") == 0 ) {
+ badr->max_x = atof(val);
+ reject = check_badr_fsss(badr, 0);
+ } else if ( strcmp(key, "min_y") == 0 ) {
+ badr->min_y = atof(val);
+ reject = check_badr_fsss(badr, 0);
+ } else if ( strcmp(key, "max_y") == 0 ) {
+ badr->max_y = atof(val);
+ reject = check_badr_fsss(badr, 0);
+ } else if ( strcmp(key, "min_fs") == 0 ) {
+ badr->min_fs = atof(val);
+ reject = check_badr_fsss(badr, 1);
+ } else if ( strcmp(key, "max_fs") == 0 ) {
+ badr->max_fs = atof(val);
+ reject = check_badr_fsss(badr, 1);
+ } else if ( strcmp(key, "min_ss") == 0 ) {
+ badr->min_ss = atof(val);
+ reject = check_badr_fsss(badr, 1);
+ } else if ( strcmp(key, "max_ss") == 0 ) {
+ badr->max_ss = atof(val);
+ reject = check_badr_fsss(badr, 1);
+ } else if ( strcmp(key, "panel") == 0 ) {
+ badr->panel = strdup(val);
+ } else {
+ ERROR("Unrecognised field '%s'\n", key);
+ }
+
+ return reject;
+}
+
+static void parse_toplevel(DataTemplate *dt,
+ const char *key, const char *val,
+ struct rg_definition ***rg_defl,
+ struct rgc_definition ***rgc_defl, int *n_rg_defs,
+ int *n_rgc_defs)
+{
+
+ if ( strcmp(key, "mask_bad") == 0 ) {
+
+ char *end;
+ double v = strtod(val, &end);
+
+ if ( end != val ) {
+ dt->mask_bad = v;
+ }
+
+ } else if ( strcmp(key, "mask_good") == 0 ) {
+
+ char *end;
+ double v = strtod(val, &end);
+
+ if ( end != val ) {
+ dt->mask_good = v;
+ }
+
+ } else if ( strcmp(key, "coffset") == 0 ) {
+ dt->defaults.cnz_offset = atof(val);
+
+ } else if ( strcmp(key, "photon_energy") == 0 ) {
+ /* Will be expanded when image is loaded */
+ dt->wavelength_from = strdup(val);
+
+ } else if ( strcmp(key, "photon_energy_bandwidth") == 0 ) {
+ double v;
+ char *end;
+ v = strtod(val, &end);
+ if ( (val[0] != '\0') && (end[0] == '\0') ) {
+ dt->photon_energy_bandwidth = v;
+ } else {
+ ERROR("Invalid value for photon_energy_bandwidth\n");
+ }
+
+ } else if ( strcmp(key, "photon_energy_scale") == 0 ) {
+ dt->photon_energy_scale = atof(val);
+
+ } else if ( strcmp(key, "peak_info_location") == 0 ) {
+ dt->hdf5_peak_path = strdup(val);
+
+ } else if (strncmp(key, "rigid_group", 11) == 0
+ && strncmp(key, "rigid_group_collection", 22) != 0 ) {
+
+ struct rg_definition **new;
+
+ new = realloc(*rg_defl,
+ ((*n_rg_defs)+1)*sizeof(struct rg_definition*));
+ *rg_defl = new;
+
+ (*rg_defl)[*n_rg_defs] = malloc(sizeof(struct rg_definition));
+ (*rg_defl)[*n_rg_defs]->name = strdup(key+12);
+ (*rg_defl)[*n_rg_defs]->pns = strdup(val);
+ *n_rg_defs = *n_rg_defs+1;
+
+ } else if ( strncmp(key, "rigid_group_collection", 22) == 0 ) {
+
+ struct rgc_definition **new;
+
+ new = realloc(*rgc_defl, ((*n_rgc_defs)+1)*
+ sizeof(struct rgc_definition*));
+ *rgc_defl = new;
+
+ (*rgc_defl)[*n_rgc_defs] =
+ malloc(sizeof(struct rgc_definition));
+ (*rgc_defl)[*n_rgc_defs]->name = strdup(key+23);
+ (*rgc_defl)[*n_rgc_defs]->rgs = strdup(val);
+ *n_rgc_defs = *n_rgc_defs+1;
+
+ } else if ( parse_field_for_panel(&dt->defaults, key, val, dt) ) {
+ ERROR("Unrecognised top level field '%s'\n", key);
+ }
+}
+
+
+DataTemplate *data_template_new_from_string(const char *string_in)
+{
+ DataTemplate *dt;
+ char **bits;
+ int done = 0;
+ int i;
+ int rgi, rgci;
+ int reject = 0;
+ int path_dim, mask_path_dim;
+ int dim_dim;
+ int dim_reject = 0;
+ int dim_dim_reject = 0;
+ struct rg_definition **rg_defl = NULL;
+ struct rgc_definition **rgc_defl = NULL;
+ int n_rg_definitions = 0;
+ int n_rgc_definitions = 0;
+ char *string;
+ char *string_orig;
+ size_t len;
+
+ dt = calloc(1, sizeof(DataTemplate));
+ if ( dt == NULL ) return NULL;
+
+ dt->n_panels = 0;
+ dt->panels = NULL;
+ dt->n_bad = 0;
+ dt->bad = NULL;
+ dt->mask_good = 0;
+ dt->mask_bad = 0;
+ dt->n_rigid_groups = 0;
+ dt->rigid_groups = NULL;
+ dt->path_dim = 0;
+ dt->dim_dim = 0;
+ dt->n_rg_collections = 0;
+ dt->rigid_group_collections = NULL;
+ dt->photon_energy_bandwidth = -1.0;
+ dt->photon_energy_scale = -1.0;
+ dt->peak_info_location = NULL;
+
+ /* The default defaults... */
+ dt->defaults.orig_min_fs = -1;
+ dt->defaults.orig_min_ss = -1;
+ dt->defaults.orig_max_fs = -1;
+ dt->defaults.orig_max_ss = -1;
+ dt->defaults.cnx = NAN;
+ dt->defaults.cny = NAN;
+ dt->defaults.cnz_from = NULL;
+ dt->defaults.cnz_offset = 0.0;
+ dt->defaults.pixel_pitch = -1.0;
+ dt->defaults.bad = 0;
+ dt->defaults.fsx = 1.0;
+ dt->defaults.fsy = 0.0;
+ dt->defaults.fsz = 0.0;
+ dt->defaults.ssx = 0.0;
+ dt->defaults.ssy = 1.0;
+ dt->defaults.ssz = 0.0;
+ dt->defaults.rail_x = NAN; /* The actual default rail direction */
+ dt->defaults.rail_y = NAN; /* is below */
+ dt->defaults.rail_z = NAN;
+ dt->defaults.clen_for_centering = NAN;
+ dt->defaults.adu_scale = NAN;
+ dt->defaults.adu_scale_unit = ADU_PER_PHOTON;
+ dt->defaults.max_adu = +INFINITY;
+ dt->defaults.mask = NULL;
+ dt->defaults.mask_file = NULL;
+ dt->defaults.satmap = NULL;
+ dt->defaults.satmap_file = NULL;
+ dt->defaults.data = NULL;
+ dt->defaults.dim_structure = NULL;
+ dt->defaults.name = NULL;
+
+ string = strdup(string_in);
+ if ( string == NULL ) return NULL;
+ len = strlen(string);
+ for ( i=0; i<len; i++ ) {
+ if ( string_in[i] == '\r' ) string[i] = '\n';
+ }
+
+ /* Becaue 'string' will get modified */
+ string_orig = string;
+
+ do {
+
+ int n1, n2;
+ char **path;
+ char *line;
+ struct dt_badregion *badregion = NULL;
+ struct panel_template *panel = NULL;
+ char wholeval[1024];
+
+ const char *nl = strchr(string, '\n');
+ if ( nl != NULL ) {
+ size_t len = nl - string;
+ line = strndup(string, nl-string);
+ line[len] = '\0';
+ string += len+1;
+ } else {
+ line = strdup(string);
+ done = 1;
+ }
+
+ if ( line[0] == ';' ) {
+ free(line);
+ continue;
+ }
+
+ n1 = assplode(line, " \t", &bits, ASSPLODE_NONE);
+ if ( n1 < 3 ) {
+ for ( i=0; i<n1; i++ ) free(bits[i]);
+ free(bits);
+ free(line);
+ continue;
+ }
+
+ /* Stitch the pieces of the "value" back together */
+ wholeval[0] = '\0'; /* Empty string */
+ for ( i=2; i<n1; i++ ) {
+ if ( bits[i][0] == ';' ) break; /* Stop on comment */
+ strncat(wholeval, bits[i], 1023);
+ }
+
+ if ( bits[1][0] != '=' ) {
+ for ( i=0; i<n1; i++ ) free(bits[i]);
+ free(bits);
+ free(line);
+ continue;
+ }
+
+ n2 = assplode(bits[0], "/\\.", &path, ASSPLODE_NONE);
+ if ( n2 < 2 ) {
+
+ /* This was a top-level option, not handled above. */
+ parse_toplevel(dt, bits[0], wholeval, &rg_defl,
+ &rgc_defl, &n_rg_definitions,
+ &n_rgc_definitions);
+ for ( i=0; i<n1; i++ ) free(bits[i]);
+ free(bits);
+ for ( i=0; i<n2; i++ ) free(path[i]);
+ free(path);
+ free(line);
+ continue;
+ }
+
+ if ( strncmp(path[0], "bad", 3) == 0 ) {
+ badregion = find_bad_region_by_name(dt, path[0]);
+ if ( badregion == NULL ) {
+ badregion = new_bad_region(dt, path[0]);
+ }
+ } else {
+ panel = find_panel_by_name(dt, path[0]);
+ if ( panel == NULL ) {
+ panel = new_panel(dt, path[0]);
+ }
+ }
+
+ if ( panel != NULL ) {
+ if ( parse_field_for_panel(panel, path[1],
+ wholeval, dt) )
+ {
+ reject = 1;
+ }
+ } else {
+ if ( parse_field_bad(badregion, path[1], wholeval) ) {
+ reject = 1;
+ }
+ }
+
+ for ( i=0; i<n1; i++ ) free(bits[i]);
+ for ( i=0; i<n2; i++ ) free(path[i]);
+ free(bits);
+ free(path);
+ free(line);
+
+ } while ( !done );
+
+ if ( dt->n_panels == -1 ) {
+ ERROR("No panel descriptions in geometry file.\n");
+ free(dt);
+ return NULL;
+ }
+
+ path_dim = -1;
+ dim_reject = 0;
+
+ for ( i=0; i<dt->n_panels; i++ ) {
+
+ int panel_dim = 0;
+ char *next_instance;
+
+ next_instance = dt->panels[i].data;
+
+ while ( next_instance ) {
+ next_instance = strstr(next_instance, "%");
+ if ( next_instance != NULL ) {
+ next_instance += 1*sizeof(char);
+ panel_dim += 1;
+ }
+ }
+
+ if ( path_dim == -1 ) {
+ path_dim = panel_dim;
+ } else {
+ if ( panel_dim != path_dim ) {
+ dim_reject = 1;
+ }
+ }
+
+ }
+
+ mask_path_dim = -1;
+ for ( i=0; i<dt->n_panels; i++ ) {
+
+ int panel_mask_dim = 0;
+ char *next_instance;
+
+ if ( dt->panels[i].mask != NULL ) {
+
+ next_instance = dt->panels[i].mask;
+
+ while ( next_instance ) {
+ next_instance = strstr(next_instance, "%");
+ if ( next_instance != NULL ) {
+ next_instance += 1*sizeof(char);
+ panel_mask_dim += 1;
+ }
+ }
+
+ if ( mask_path_dim == -1 ) {
+ mask_path_dim = panel_mask_dim;
+ } else {
+ if ( panel_mask_dim != mask_path_dim ) {
+ dim_reject = 1;
+ }
+ }
+
+ }
+ }
+
+ if ( dim_reject == 1 ) {
+ ERROR("All panels' data and mask entries must have the same "
+ "number of placeholders\n");
+ reject = 1;
+ }
+
+ if ( mask_path_dim > path_dim ) {
+ ERROR("Number of placeholders in mask cannot be larger than "
+ "for data\n");
+ reject = 1;
+ }
+
+ dt->path_dim = path_dim;
+
+ dim_dim_reject = 0;
+ dim_dim = -1;
+
+ for ( i=0; i<dt->n_panels; i++ ) {
+
+ int di;
+ int found_ss = 0;
+ int found_fs = 0;
+ int panel_dim_dim = 0;
+
+ if ( dt->panels[i].dim_structure == NULL ) {
+ dt->panels[i].dim_structure = default_dim_structure();
+ }
+
+ for ( di=0; di<dt->panels[i].dim_structure->num_dims; di++ ) {
+
+ if ( dt->panels[i].dim_structure->dims[di] ==
+ HYSL_UNDEFINED ) {
+ dim_dim_reject = 1;
+ ERROR("Dimension %i for panel %s is undefined.\n",
+ di, dt->panels[i].name);
+ }
+ if ( dt->panels[i].dim_structure->dims[di] ==
+ HYSL_PLACEHOLDER ) {
+ panel_dim_dim += 1;
+ }
+ if ( dt->panels[i].dim_structure->dims[di] ==
+ HYSL_SS ) {
+ found_ss += 1;
+ }
+ if ( dt->panels[i].dim_structure->dims[di] ==
+ HYSL_FS ) {
+ found_fs += 1;
+ }
+
+ }
+
+ if ( found_ss != 1 ) {
+ ERROR("Exactly one slow scan dim coordinate is needed "
+ "(found %i for panel %s)\n", found_ss,
+ dt->panels[i].name);
+ dim_dim_reject = 1;
+ }
+
+ if ( found_fs != 1 ) {
+ ERROR("Exactly one fast scan dim coordinate is needed "
+ "(found %i for panel %s)\n", found_fs,
+ dt->panels[i].name);
+ dim_dim_reject = 1;
+ }
+
+ if ( panel_dim_dim > 1 ) {
+ ERROR("Maximum one placeholder dim coordinate is "
+ "allowed (found %i for panel %s)\n",
+ panel_dim_dim, dt->panels[i].name);
+ dim_dim_reject = 1;
+ }
+
+ if ( dim_dim == -1 ) {
+ dim_dim = panel_dim_dim;
+ } else {
+ if ( panel_dim_dim != dim_dim ) {
+ dim_dim_reject = 1;
+ }
+ }
+
+ }
+
+ if ( dim_dim_reject == 1) {
+ reject = 1;
+ }
+
+ dt->dim_dim = dim_dim;
+
+ for ( i=0; i<dt->n_panels; i++ ) {
+
+ struct panel_template *p = &dt->panels[i];
+
+ if ( p->orig_min_fs < 0 ) {
+ ERROR("Please specify the minimum FS coordinate for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( p->orig_max_fs < 0 ) {
+ ERROR("Please specify the maximum FS coordinate for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( p->orig_min_ss < 0 ) {
+ ERROR("Please specify the minimum SS coordinate for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( p->orig_max_ss < 0 ) {
+ ERROR("Please specify the maximum SS coordinate for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( isnan(p->cnx) ) {
+ ERROR("Please specify the corner X coordinate for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( isnan(p->cny) ) {
+ ERROR("Please specify the corner Y coordinate for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( p->cnz_from == NULL ) {
+ ERROR("Please specify the camera length for panel %s\n",
+ dt->panels[i].name);
+ reject = 1;
+ }
+ if ( p->pixel_pitch < 0 ) {
+ ERROR("Please specify the pixel size for"
+ " panel %s\n", dt->panels[i].name);
+ reject = 1;
+ }
+ if ( p->data == NULL ) {
+ ERROR("Please specify the data location for panel %s\n",
+ p->name);
+ reject = 1;
+ }
+ if ( isnan(p->adu_per_eV) && isnan(p->adu_per_photon) ) {
+ ERROR("Please specify either adu_per_eV or "
+ "adu_per_photon for panel %s\n",
+ dt->panels[i].name);
+ reject = 1;
+ }
+
+ if ( isnan(p->clen_for_centering) && !isnan(p->rail_x) )
+ {
+ ERROR("You must specify clen_for_centering if you "
+ "specify the rail direction (panel %s)\n",
+ p->name);
+ reject = 1;
+ }
+
+ if ( (p->mask_file != NULL) && (p->mask == NULL) ) {
+ ERROR("You have specified 'mask_file' but not 'mask'. "
+ "'mask_file' will therefore have no effect. "
+ "(panel %s)\n", p->name);
+ reject = 1;
+ }
+
+ /* The default rail direction */
+ if ( isnan(p->rail_x) ) {
+ p->rail_x = 0.0;
+ p->rail_y = 0.0;
+ p->rail_z = 1.0;
+ }
+ if ( isnan(p->clen_for_centering) ) p->clen_for_centering = 0.0;
+
+ }
+
+ for ( i=0; i<dt->n_bad; i++ ) {
+ if ( dt->bad[i].is_fsss == 99 ) {
+ ERROR("Please specify the coordinate ranges for"
+ " bad region %s\n", dt->bad[i].name);
+ reject = 1;
+ }
+ }
+
+ free(dt->defaults.cnz_from);
+ free(dt->defaults.data);
+ free(dt->defaults.mask);
+
+ for ( rgi=0; rgi<n_rg_definitions; rgi++) {
+
+ int pi, n1;
+ struct dt_rigid_group *rigidgroup = NULL;
+
+ rigidgroup = find_or_add_rg(dt, rg_defl[rgi]->name);
+
+ n1 = assplode(rg_defl[rgi]->pns, ",", &bits, ASSPLODE_NONE);
+
+ for ( pi=0; pi<n1; pi++ ) {
+
+ struct panel_template *p;
+
+ p = find_panel_by_name(dt, bits[pi]);
+ if ( p == NULL ) {
+ ERROR("Cannot add panel to rigid group\n");
+ ERROR("Panel not found: %s\n", bits[pi]);
+ return NULL;
+ }
+ add_to_rigid_group(rigidgroup, p);
+ free(bits[pi]);
+ }
+ free(bits);
+ free(rg_defl[rgi]->name);
+ free(rg_defl[rgi]->pns);
+ free(rg_defl[rgi]);
+ }
+ free(rg_defl);
+
+ for ( rgci=0; rgci<n_rgc_definitions; rgci++ ) {
+
+ int rgi, n2;
+ struct dt_rg_collection *rgcollection = NULL;
+
+ rgcollection = find_or_add_rg_coll(dt, rgc_defl[rgci]->name);
+
+ n2 = assplode(rgc_defl[rgci]->rgs, ",", &bits, ASSPLODE_NONE);
+
+ for ( rgi=0; rgi<n2; rgi++ ) {
+
+ struct dt_rigid_group *r;
+
+ r = find_rigid_group_by_name(dt, bits[rgi]);
+ if ( r == NULL ) {
+ ERROR("Cannot add rigid group to collection\n");
+ ERROR("Rigid group not found: %s\n", bits[rgi]);
+ return NULL;
+ }
+ add_to_rigid_group_coll(rgcollection, r);
+ free(bits[rgi]);
+ }
+ free(bits);
+ free(rgc_defl[rgci]->name);
+ free(rgc_defl[rgci]->rgs);
+ free(rgc_defl[rgci]);
+
+ }
+ free(rgc_defl);
+
+ if ( n_rg_definitions == 0 ) {
+
+ int pi;
+
+ for ( pi=0; pi<dt->n_panels; pi++ ) {
+
+ struct dt_rigid_group *rigidgroup = NULL;
+
+ rigidgroup = find_or_add_rg(dt, dt->panels[pi].name);
+ add_to_rigid_group(rigidgroup, &dt->panels[pi]);
+
+ }
+ }
+
+ if ( n_rgc_definitions == 0 ) {
+
+ int rgi;
+ struct dt_rg_collection *rgcollection = NULL;
+
+ rgcollection = find_or_add_rg_coll(dt, "default");
+
+ for ( rgi=0; rgi<dt->n_rigid_groups; rgi++ ) {
+
+ add_to_rigid_group_coll(rgcollection,
+ dt->rigid_groups[rgi]);
+
+ }
+ }
+
+ free(string_orig);
+
+ if ( reject ) return NULL;
+
+ return dt;
+}
+
+
+DataTemplate *data_template_new_from_file(const char *filename)
+{
+ char *contents;
+ DataTemplate *dt;
+
+ contents = load_entire_file(filename);
+ if ( contents == NULL ) {
+ ERROR("Failed to load geometry file '%s'\n", filename);
+ return NULL;
+ }
+
+ dt = data_template_new_from_string(contents);
+ free(contents);
+ return dt;
+}
+
+
void data_template_free(DataTemplate *dt)
{
+ int i;
+
+ free_all_rigid_groups(dt);
+ free_all_rigid_group_collections(dt);
+
+ for ( i=0; i<dt->n_panels; i++ ) {
+ free(dt->panels[i].cnz_from);
+ free_dim_structure(dt->panels[i].dim_structure);
+ }
+
+ free(dt->panels);
+ free(dt->bad);
+ free(dt);
}
diff --git a/libcrystfel/src/datatemplate_priv.h b/libcrystfel/src/datatemplate_priv.h
new file mode 100644
index 00000000..e8da4193
--- /dev/null
+++ b/libcrystfel/src/datatemplate_priv.h
@@ -0,0 +1,189 @@
+/*
+ * datatemplate_priv.h
+ *
+ * Data template structure (private parts)
+ *
+ * Copyright © 2019-2020 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019-2020 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/>.
+ *
+ */
+
+
+enum adu_per_unit
+{
+ ADU_PER_PHOTON,
+ ADU_PER_EV
+};
+
+
+struct dt_rigid_group
+{
+ char *name;
+ struct panel_template **panels;
+ int n_panels;
+};
+
+
+struct dt_rg_collection
+{
+ char *name;
+ struct dt_rigid_group **rigid_groups;
+ int n_rigid_groups;
+};
+
+
+/**
+ * Represents one panel of a detector
+ */
+struct panel_template
+{
+ /** Text name for panel */
+ char *name;
+
+ /** \name Location of corner in units of the pixel size of this panel */
+ /**@{*/
+ double cnx;
+ double cny;
+ /**@}*/
+
+ /** Location to get \ref cnz from, e.g. from HDF5 file */
+ char *cnz_from;
+
+ /** The offset to be applied from \ref clen */
+ double cnz_offset;
+
+ /** Location of mask data */
+ char *mask;
+
+ /** Filename for mask data */
+ char *mask_file;
+
+ /** Location of per-pixel saturation map */
+ char *satmap;
+
+ /** Filename for saturation map */
+ char *satmap_file;
+
+ /** Mark entire panel as bad if set */
+ int bad;
+
+ /** Resolution in pixels per metre */
+ double pixel_pitch;
+
+ /** Number of detector intensity units per photon, or eV */
+ double adu_scale;
+ enum adu_per_unit adu_scale_unit;
+
+ /** Treat pixel as unreliable if higher than this */
+ double max_adu;
+
+ /** Location of data in file */
+ char *data;
+
+ /** Dimension structure */
+ struct dim_structure *dim_structure;
+
+ /** \name Transformation matrix from pixel coordinates to lab frame */
+ /*@{*/
+ double fsx;
+ double fsy;
+ double fsz;
+ double ssx;
+ double ssy;
+ double ssz;
+ /*@}*/
+
+ /** \name Rail direction */
+ /*@{*/
+ double rail_x;
+ double rail_y;
+ double rail_z;
+ /*@}*/
+
+ /* Value of clen (without coffset) at which beam is centered */
+ double clen_for_centering;
+
+ /** \name Position of the panel in the data block in the file. */
+ /*@{*/
+ int orig_min_fs;
+ int orig_max_fs;
+ int orig_min_ss;
+ int orig_max_ss;
+ /*@}*/
+
+
+ /* FIXME: Should only be one */
+ double adu_per_eV;
+ double adu_per_photon;
+
+};
+
+
+struct dt_badregion
+{
+ char name[1024];
+ int is_fsss;
+ char *panel;
+
+ double min_x;
+ double max_x;
+ double min_y;
+ double max_y;
+
+ /* Specified INCLUSIVELY */
+ int min_fs;
+ int max_fs;
+ int min_ss;
+ int max_ss;
+
+};
+
+
+struct _datatemplate
+{
+ struct panel_template *panels;
+ int n_panels;
+
+ struct dt_badregion *bad;
+ int n_bad;
+
+ char *wavelength_from;
+ double photon_energy_bandwidth; /* Eww */
+ double photon_energy_scale; /* Eww */
+
+ char *peak_info_location;
+
+ unsigned int mask_bad;
+ unsigned int mask_good;
+
+ struct dt_rigid_group **rigid_groups;
+ int n_rigid_groups;
+
+ struct dt_rg_collection **rigid_group_collections;
+ int n_rg_collections;
+
+ int path_dim;
+ int dim_dim;
+
+ char *hdf5_peak_path;
+
+ struct panel_template defaults;
+};