diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ambigator.c | 39 | ||||
-rw-r--r-- | src/geoptimiser.c | 384 | ||||
-rw-r--r-- | src/im-sandbox.c | 14 | ||||
-rw-r--r-- | src/indexamajig.c | 35 | ||||
-rw-r--r-- | src/merge.c | 128 | ||||
-rw-r--r-- | src/merge.h | 9 | ||||
-rw-r--r-- | src/partial_sim.c | 5 | ||||
-rw-r--r-- | src/partialator.c | 460 | ||||
-rw-r--r-- | src/post-refinement.c | 1146 | ||||
-rw-r--r-- | src/post-refinement.h | 20 | ||||
-rw-r--r-- | src/process_hkl.c | 8 | ||||
-rw-r--r-- | src/process_image.c | 2 | ||||
-rw-r--r-- | src/rejection.c | 2 | ||||
-rw-r--r-- | src/scaling.c | 461 | ||||
-rw-r--r-- | src/scaling.h | 16 |
15 files changed, 1769 insertions, 960 deletions
diff --git a/src/ambigator.c b/src/ambigator.c index ba7629ab..bbf1d06e 100644 --- a/src/ambigator.c +++ b/src/ambigator.c @@ -674,7 +674,8 @@ static void reindex_reflections(FILE *fh, FILE *ofh, int assignment, /* This is nasty, but means the output includes absolutely everything in the * input, even stuff ignored by read_chunk() */ static void write_reindexed_stream(const char *infile, const char *outfile, - int *assignments, SymOpList *amb) + int *assignments, SymOpList *amb, + int argc, char *argv[]) { FILE *fh; FILE *ofh; @@ -683,6 +684,7 @@ static void write_reindexed_stream(const char *infile, const char *outfile, int have_as = 0; int have_bs = 0; int have_cs = 0; + int done = 0; fh = fopen(infile, "r"); if ( fh == NULL ) { @@ -696,6 +698,38 @@ static void write_reindexed_stream(const char *infile, const char *outfile, return; } + /* Copy the header */ + do { + + char line[1024]; + char *rval; + + rval = fgets(line, 1023, fh); + if ( rval == NULL ) { + ERROR("Failed to read stream audit info.\n"); + return; + } + + if ( strncmp(line, "-----", 5) == 0 ) { + + done = 1; + + /* Add our own header */ + fprintf(ofh, "Re-indexed by ambigator "CRYSTFEL_VERSIONSTRING"\n"); + if ( argc > 0 ) { + for ( i=0; i<argc; i++ ) { + if ( i > 0 ) fprintf(ofh, " "); + fprintf(ofh, "%s", argv[i]); + } + fprintf(ofh, "\n"); + } + + } + + fputs(line, ofh); + + } while ( !done ); + i = 0; do { @@ -1359,7 +1393,8 @@ int main(int argc, char *argv[]) n_dif); if ( (outfile != NULL) && (amb != NULL) ) { - write_reindexed_stream(infile, outfile, assignments, amb); + write_reindexed_stream(infile, outfile, assignments, amb, + argc, argv); } else if ( outfile != NULL ) { ERROR("Can only write stream with known ambiguity operator.\n"); ERROR("Try again with -w or --operator.\n"); diff --git a/src/geoptimiser.c b/src/geoptimiser.c index f63ba81b..9b03d1b9 100644 --- a/src/geoptimiser.c +++ b/src/geoptimiser.c @@ -85,20 +85,25 @@ static void show_help(const char *s) " -c, --connected=<rg_coll> Rigid group collection for connected\n" " ASICs.\n" " --no-error-maps Do not generate error map PNGs.\n" +" --stretch-map Generate stretch map PNG (panels distance).\n" " -x, --min-num-peaks-per-pixel=<num> Minimum number of peaks per pixel.\n" " Default: 3. \n" +" -z, --max-num-peaks-per-pixel=<num> Maximum number of peaks per pixel.\n" +" Default is num_patterns / 10. \n" " -p, --min-num-pixels-per-conn-group=<num> Minimum number of useful pixels per\n" " connected group.\n" -" f Default: 100.\n" +" Default: 100.\n" " -l, --most-freq-clen Use only the most frequent camera\n" " length.\n" " -s, --individual-dist-offset Use a distance offset for each panel.\n" " Default: whole-detector offset.\n" " --no-stretch Do not optimize distance offset.\n" " Default: distance offset is optimized\n" +" --no-rotation Do not optimize rotation of connectedd groups.\n" +" Default: rotation is optimized\n" " -m --max-peak-dist=<num> Maximum distance between predicted and\n" " detected peaks (in pixels)\n" -" Default: half of minimal inter-Bragg distance\n" +" Default: half of minimal inter-Bragg distance\n" ); } @@ -108,11 +113,14 @@ struct geoptimiser_params char *outfile; char *geometry_filename; int min_num_peaks_per_pix; + int max_num_peaks_per_pix; int min_num_pix_per_conn_group; int only_best_distance; int nostretch; + int norotation; int individual_coffset; int error_maps; + int stretch_map; int enforce_cspad_layout; int no_cspad; double max_peak_dist; @@ -808,11 +816,13 @@ static int compute_pixel_displacements(struct image *images, int n_images, static int compute_avg_pix_displ(struct gpanel *gp, int idx, - int num_peaks_per_pixel) + int num_peaks_per_pixel, + int max_peaks_per_pixel) { int ret; - if ( gp->num_pix_displ[idx] >= num_peaks_per_pixel ) { + if ( gp->num_pix_displ[idx] >= num_peaks_per_pixel && + gp->num_pix_displ[idx] < max_peaks_per_pixel ) { ret = fill_avg_pixel_displ(gp, idx); if ( ret != 0 ) return ret; @@ -833,12 +843,14 @@ static int compute_avg_pix_displ(struct gpanel *gp, int idx, static int compute_avg_displacements(struct detector *det, struct rg_collection *connected, struct connected_data *conn_data, - struct gpanel *gpanels) + struct gpanel *gpanels, + struct geoptimiser_params *gparams) { int di, ip, ifs, iss; - int pix_index, ret; + int pix_index, ret, max_peaks; struct panel *p; + max_peaks = 0; for ( di=0; di<connected->n_rigid_groups; di++ ) { for ( ip=0; ip<connected->rigid_groups[di]->n_panels; ip++ ) { @@ -854,9 +866,12 @@ static int compute_avg_displacements(struct detector *det, for ( ifs=0; ifs<p->w; ifs++ ) { pix_index = ifs+p->w*iss; + if ( max_peaks < gp->num_pix_displ[pix_index] ) + max_peaks = gp->num_pix_displ[pix_index]; ret = compute_avg_pix_displ(gp, pix_index, - conn_data[di].num_peaks_per_pixel); + conn_data[di].num_peaks_per_pixel, + gparams->max_num_peaks_per_pix); if ( ret != 0 ) return ret; @@ -865,6 +880,7 @@ static int compute_avg_displacements(struct detector *det, } } + STATUS("Maximum peaks per pixel is: %d\n", max_peaks); return 0; } @@ -1085,20 +1101,23 @@ static void correct_rotation_and_stretch(struct rg_collection *connected, /* Calculate corner adjustment * NB All panels follow the first one */ - if ( ip > 0 && connected->rigid_groups[di]->n_panels == 2 && !gparams->no_cspad ) { + if ( ip > 0 ) { struct panel *p0; - double delta_x, delta_y, delta; + double dx_old, dy_old, dx_new, dy_new; p0 = connected->rigid_groups[di]->panels[0]; - delta_x = p->cnx - p0->cnx / cs; - delta_y = p->cny - p0->cny / cs; + dx_old = p->cnx - p0->cnx / cs; + dy_old = p->cny - p0->cny / cs; - delta = sqrt(delta_x*delta_x + delta_y*delta_y); + dx_new = dx_old*cos(conn_data[di].cang)- + dy_old*sin(conn_data[di].cang); + dy_new = dx_old*sin(conn_data[di].cang)+ + dy_old*cos(conn_data[di].cang); - new_cnx = p0->cnx + delta*p0->fsx; - new_cny = p0->cny + delta*p0->fsy; + new_cnx = p0->cnx + dx_new; + new_cny = p0->cny + dy_new; } else { @@ -1577,6 +1596,14 @@ static double compute_rotation_and_stretch(struct rg_collection *connected, free(stretches); } + if ( gparams->norotation ) { + STATUS("However, rotation will not be optimized, so the " + "rotation angle has been set to 0\n"); + for ( di=0; di<connected->n_rigid_groups; di++ ) { + conn_data[di].cang = 0.0; + } + } + stretch_cf = 1.0; printf("Computing overall stretch coefficient.\n"); @@ -1642,7 +1669,8 @@ static double compute_rotation_and_stretch(struct rg_collection *connected, #ifdef CAN_SAVE_TO_PNG static void draw_panel(struct image *image, cairo_t *cr, - cairo_matrix_t *basic_m, GdkPixbuf **pixbufs, int i) { + cairo_matrix_t *basic_m, GdkPixbuf **pixbufs, int i) +{ struct panel p = image->det->panels[i]; int w = gdk_pixbuf_get_width(pixbufs[i]); int h = gdk_pixbuf_get_height(pixbufs[i]); @@ -1816,8 +1844,119 @@ static int save_data_to_png(char *filename, struct detector *det, return 0; } + +static int save_stretch_to_png(char *filename, struct detector *det, + struct rg_collection *connected, + struct connected_data *conn_data) + +{ + struct image im; + int i, di, ip; + float min_str, max_str; + struct rectangle rect; + GdkPixbuf *col_scale; + cairo_t *cr; + + cairo_status_t r; + cairo_surface_t *surf; + + im.det = det; + im.bad = NULL; + im.dp = malloc(det->n_panels*sizeof(float *)); + if ( im.dp == NULL ) { + ERROR("Failed to allocate data\n"); + return 1; + } + + min_str = 10000; + max_str = 0; + for ( di=0; di<connected->n_rigid_groups; di++ ) { + if (conn_data[di].cstr > max_str) max_str = conn_data[di].cstr; + if (conn_data[di].cstr < min_str) min_str = conn_data[di].cstr; + } + + i = 0; + for ( di=0; di<connected->n_rigid_groups; di++ ) { + for ( ip=0; ip<connected->rigid_groups[di]->n_panels; ip++ ) { + + int fs, ss; + float val; + struct panel *p; + + p = connected->rigid_groups[di]->panels[ip]; + + im.dp[i] = calloc(p->w * p->h, sizeof(float)); + if ( im.dp[i] == NULL ) { + ERROR("Failed to allocate data\n"); + return 1; + } + + val = (conn_data[di].cstr - min_str) / (max_str - min_str); + val *= 10.0; /* render_panels sets this as max */ + + for ( ss=0; ss<p->h; ss++ ) { + for ( fs=0; fs<p->w; fs++ ) { + + im.dp[i][fs+p->w*ss] = val; + + } + } + i++; + } + } + + get_pixel_extents(im.det, &rect.min_x, &rect.min_y, &rect.max_x, + &rect.max_y); + + if (rect.min_x > 0.0) rect.min_x = 0.0; + if (rect.max_x < 0.0) rect.max_x = 0.0; + if (rect.min_y > 0.0) rect.min_y = 0.0; + if (rect.max_y < 0.0) rect.max_y = 0.0; + + rect.width = rect.max_x - rect.min_x; + rect.height = rect.max_y - rect.min_y; + + /* Add a thin border */ + rect.width += 2.0; + rect.height += 2.0; + surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect.width + 20, + rect.height); + + draw_detector(surf, &im, rect); + + col_scale = render_get_colour_scale(20, rect.height, SCALE_GEOPTIMISER); + + cr = cairo_create(surf); + cairo_identity_matrix(cr); + cairo_translate(cr, rect.width, 0.0); + cairo_rectangle(cr, 0.0, 0.0, 20.0, rect.height); + gdk_cairo_set_source_pixbuf(cr, col_scale, 0.0, 0.0); + cairo_fill(cr); + cairo_destroy(cr); + + for ( i=0; i<det->n_panels; i++ ) { + free(im.dp[i]); + } + free(im.dp); + + r = cairo_surface_write_to_png(surf, filename); + if ( r != CAIRO_STATUS_SUCCESS ) return 1; + + return 0; +} + #else /* CAN_SAVE_TO_PNG */ +static int save_stretch_to_png(char *filename, struct detector *det, + struct rg_collection *connected, + struct connected_data *conn_data) +{ + ERROR("WARNING: geoptimiser was compiled without gdk-pixbuf and cairo " + "support. Error maps will not be saved.\n"); + return 0; /* Return "success" so that program continues */ +} + + static int save_data_to_png(char *filename, struct detector *det, struct gpanel *gpanels) { @@ -1902,6 +2041,87 @@ int check_and_enforce_cspad_dist(struct geoptimiser_params *gparams, } } + return num_errors_found; +} + + +int check_and_enforce_agipd_dist(struct geoptimiser_params *gparams, + struct detector *det) +{ + int np = 0; + int npp = 0; + int num_errors_found = 0; + + double dist_to_check = 66.0; + double tol = 0.1; + + for ( np=0; np<det->n_panels; np = np+8 ) { + + double dist2; + double cnx2, cny2; + + struct panel *ep = &det->panels[np]; + + for ( npp=0; npp<8; npp++ ) { + + struct panel *op = &det->panels[np+npp]; + + cnx2 = ep->cnx + npp*dist_to_check*ep->ssx; + cny2 = ep->cny + npp*dist_to_check*ep->ssy; + + dist2 = (( cnx2 - op->cnx )*( cnx2 - op->cnx ) + + ( cny2 - op->cny )*( cny2 - op->cny )); + + if ( dist2 > (tol*tol)) { + + num_errors_found += 1; + + STATUS("Warning: distance between panels %s and %s " + "is outside acceptable margins (Corners are " + "more than 0.2 pixels away: %3.2f).\n", ep->name, + op->name, sqrt(dist2)); + + if ( gparams->enforce_cspad_layout ) { + + double new_op_cx, new_op_cy; + + new_op_cx = ep->cnx + ep->ssx*npp*dist_to_check; + new_op_cy = ep->cny + ep->ssy*npp*dist_to_check; + + op->cnx = new_op_cx; + op->cny = new_op_cy; + + STATUS("Enforcing distance....\n"); + } + + } + + if ( ep->fsx != op->fsx || ep->ssx != op->ssx || + ep->fsy != op->fsy || ep->ssx != op->ssx ) { + + num_errors_found += 1; + + STATUS("Warning: relative orientation between panels " + "%s and %s is incorrect.\n", ep->name, op->name); + + if ( gparams->enforce_cspad_layout ) { + + STATUS("Enforcing relative orientation....\n"); + + op->fsx = ep->fsx; + op->ssx = ep->ssx; + op->fsy = ep->fsy; + op->ssy = ep->ssy; + + op->xfs = ep->xfs; + op->xss = ep->xss; + op->yfs = ep->yfs; + op->yss = ep->yss; + } + + } + } + } return num_errors_found; } @@ -2076,9 +2296,9 @@ int optimize_geometry(struct geoptimiser_params *gparams, double avg_res; double clen_to_use; - // for angles and stretch calculation use - // only pixels which are distco*size_panel - // away + /* for angles and stretch calculation use + * only pixels which are distco*size_panel + * away */ double dist_coeff_for_rot_str = 0.2; double total_error; @@ -2099,6 +2319,7 @@ int optimize_geometry(struct geoptimiser_params *gparams, "accurate estimation of position/orientation: %i\n", gparams->min_num_pix_per_conn_group); + /* CS-PAD */ if ( (det->n_panels == 64) && !gparams->no_cspad ) { int num_errors = 0; @@ -2142,6 +2363,51 @@ int optimize_geometry(struct geoptimiser_params *gparams, } } + /* AGIPD */ + if ( (det->n_panels == 128) && det->panels[0].res > 4999 && + det->panels[0].res < 5001 && !gparams->no_cspad ) { + + int num_errors = 0; + + STATUS("It looks like the detector is an AGIPD. " + "Checking relative distance and orientation of " + "connected ASICS.\n"); + STATUS("If the detector is not an AGIPD , please rerun the " + "program with the --no-agipd option.\n"); + + STATUS("Enforcing AGIPD layout...\n"); + num_errors = check_and_enforce_agipd_dist(gparams, det); + + if ( gparams->enforce_cspad_layout ) { + + int geom_wr; + + STATUS("Saving geometry with enforced AGIPD layout.\n" + "Please restart geometry optimization using the " + "optimized geometry from this run as input " + "geometry file.\n"); + geom_wr = write_detector_geometry_2( + gparams->geometry_filename, + gparams->outfile, det, + gparams->command_line, 1); + if ( geom_wr != 0 ) { + ERROR("Error in writing output geometry file.\n"); + return 1; + } + STATUS("All done!\n"); + return 0; + } + + if ( !gparams->enforce_cspad_layout && num_errors > 0 ) { + ERROR("Relative distance and orientation of connected " + "ASICS do not respect the AGIPD layout.\n" + "Geometry optimization cannot continue.\n" + "Please rerun the program with the " + "--enforce-agipd-layout option.\n"); + return 1; + } + } + images = read_patterns_from_stream(gparams->infile, det, &n_images); if ( (n_images < 1) || (images == NULL) ) { ERROR("Error reading stream file\n"); @@ -2164,6 +2430,14 @@ int optimize_geometry(struct geoptimiser_params *gparams, avg_cell); if ( clen_to_use < 0.0 ) return 1; + if ( gparams->max_num_peaks_per_pix == 0 && n_images > 100 ) { + gparams->max_num_peaks_per_pix = n_images / 10; + } else if ( gparams->max_num_peaks_per_pix == 0 ) { + gparams->max_num_peaks_per_pix = n_images; + } + STATUS("Maximum number of measurements for a pixel to be included in " + "the refinement: %i\n", gparams->max_num_peaks_per_pix); + gpanels = calloc(det->n_panels, sizeof(struct gpanel)); if ( gpanels == NULL ) { ERROR("Failed to allocate panels.\n"); @@ -2206,7 +2480,7 @@ int optimize_geometry(struct geoptimiser_params *gparams, adjust_min_peaks_per_conn(connected, gpanels, det, gparams, conn_data); - if ( compute_avg_displacements(det, connected, conn_data, gpanels) ) { + if ( compute_avg_displacements(det, connected, conn_data, gpanels, gparams) ) { free(conn_data); free(images); return 1; @@ -2235,25 +2509,53 @@ int optimize_geometry(struct geoptimiser_params *gparams, } - stretch_coeff = compute_rotation_and_stretch(connected, conn_data, - det, gpanels, + if ( !gparams->nostretch || !gparams->norotation ) { + + stretch_coeff = compute_rotation_and_stretch(connected, + conn_data, det, gpanels, dist_coeff_for_rot_str, gparams); - if ( stretch_coeff < 0.0 ) { - free(conn_data); - return 1; - } + if ( stretch_coeff < 0.0 ) { + free(conn_data); + return 1; + } - ret = compute_rot_stretch_for_empty_panels(quadrants, connected, - gparams->min_num_pix_per_conn_group, conn_data); - if ( ret ) { - free(conn_data); - return 1; - } + ret = compute_rot_stretch_for_empty_panels(quadrants, connected, + gparams->min_num_pix_per_conn_group, + conn_data); + if ( ret ) { + free(conn_data); + return 1; + } + + if ( gparams->stretch_map ) { + +#ifdef HAVE_SAVE_TO_PNG + + STATUS("Saving stretch map - for out-of-plane rotations.\n"); - correct_rotation_and_stretch(connected, det, gpanels, conn_data, - clen_to_use, stretch_coeff, - gparams); + ret = save_stretch_to_png("stretch_co.png", det, + connected, conn_data); + + if ( ret ) { + ERROR("Error while writing data to file.\n"); + free(conn_data); + return 1; + } + +#else /* HAVE_SAVE_TO_PNG */ + + STATUS("ERROR: geoptimiser was compiled without GTK and " + "cairo support.\n Stretch map will not be saved.\n"); + +#endif /* HAVE_SAVE_TO_PNG */ + + } + + correct_rotation_and_stretch(connected, det, gpanels, + conn_data, clen_to_use, + stretch_coeff, gparams); + } ret = compute_shift(connected, conn_data, det, gparams, gpanels); if ( ret != 0 ) { @@ -2341,13 +2643,16 @@ int main(int argc, char *argv[]) gparams->infile = NULL; gparams->geometry_filename = NULL; gparams->min_num_peaks_per_pix = 3; + gparams->max_num_peaks_per_pix = 0; gparams->min_num_pix_per_conn_group = 100; gparams->only_best_distance = 0; gparams->enforce_cspad_layout = 0; gparams->nostretch = 0; + gparams->norotation = 0; gparams->individual_coffset = 0; gparams->no_cspad = 0; gparams->error_maps = 1; + gparams->stretch_map = 0; gparams->max_peak_dist = 0.0; const struct option longopts[] = { @@ -2361,6 +2666,7 @@ int main(int argc, char *argv[]) {"quadrants", 1, NULL, 'q'}, {"connected", 1, NULL, 'c'}, {"min-num-peaks-per-pixel", 1, NULL, 'x'}, + {"max-num-peaks-per-pixel", 0, NULL, 'z'}, {"min-num-pixels-per-conn-group", 1, NULL, 'p'}, {"most-few-clen", 0, NULL, 'l'}, {"max-peak-dist", 1, NULL, 'm'}, @@ -2368,9 +2674,13 @@ int main(int argc, char *argv[]) /* Long-only options with no arguments */ {"no-stretch", 0, &gparams->nostretch, 1}, + {"no-rotation", 0, &gparams->norotation, 1}, {"no-error-maps", 0, &gparams->error_maps, 0}, + {"stretch-map", 0, &gparams->stretch_map, 1}, {"enforce-cspad-layout", 0, &gparams->enforce_cspad_layout, 1}, + {"enforce-agipd-layout", 0, &gparams->enforce_cspad_layout, 1}, {"no-cspad", 0, &gparams->no_cspad, 1}, + {"no-agipd", 0, &gparams->no_cspad, 1}, /* Long-only options with arguments */ {"min-num-peaks-per-panel", 1, NULL, 11}, @@ -2380,7 +2690,7 @@ int main(int argc, char *argv[]) }; /* Short options */ - while ((c = getopt_long(argc, argv, "ho:i:g:q:c:o:x:p:lsm:", + while ((c = getopt_long(argc, argv, "ho:i:g:q:c:o:x:z:p:lsm:", longopts, NULL)) != -1) { switch (c) { @@ -2425,6 +2735,10 @@ int main(int argc, char *argv[]) gparams->min_num_peaks_per_pix = atoi(optarg); break; + case 'z' : + gparams->max_num_peaks_per_pix = atoi(optarg); + break; + case 'p' : gparams->min_num_pix_per_conn_group = atoi(optarg); break; diff --git a/src/im-sandbox.c b/src/im-sandbox.c index e01fcb74..74467529 100644 --- a/src/im-sandbox.c +++ b/src/im-sandbox.c @@ -196,7 +196,7 @@ static struct filename_plus_event *get_pattern(FILE *fh, int config_basename, line = tmp; } - scan_check = sscanf(line, "%s %s", filename_buf, event_buf ); + scan_check = sscanf(line, "%s %s", filename_buf, event_buf); len = strlen(prefix)+strlen(filename_buf)+1; @@ -261,9 +261,15 @@ static struct filename_plus_event *get_pattern(FILE *fh, int config_basename, struct event *ev_to_add; ev_to_add = get_event_from_event_string(event_buf); - append_event_to_event_list(ev_list, ev_to_add); - free_event(ev_to_add); - event_index = 0; + if ( ev_to_add == NULL ) { + ERROR("Bad event descriptor: '%s'\n", + event_buf); + } else { + append_event_to_event_list(ev_list, + ev_to_add); + free_event(ev_to_add); + event_index = 0; + } } diff --git a/src/indexamajig.c b/src/indexamajig.c index bac1ee04..38d83d58 100644 --- a/src/indexamajig.c +++ b/src/indexamajig.c @@ -123,11 +123,11 @@ static void show_help(const char *s) " --no-check-cell Don't check lattice parameters against input cell\n" " --no-cell-combinations\n" " Don't use axis combinations when checking cell\n" -" --no-multi Don't repeat indexing to index multiple hits\n" +" --multi Repeat indexing to index multiple hits\n" " --no-retry Don't repeat indexing to increase indexing rate\n" " --no-refine Skip the prediction refinement step\n" -" --check-peaks Check that most of the peaks can be accounted for\n" -" by the indexing solution\n" +" --no-check-peaks Don't check that most of the peaks can be accounted\n" +" for by the indexing solution\n" "\n" " --taketwo-member-threshold\n" " Minimum number of members in network\n" @@ -238,8 +238,8 @@ int main(int argc, char *argv[]) int if_refine = 1; int if_nocomb = 0; int if_nocheck = 0; - int if_peaks = 0; - int if_multi = 1; + int if_peaks = 1; + int if_multi = 0; int if_retry = 1; int serial_start = 1; @@ -342,8 +342,11 @@ int main(int argc, char *argv[]) {"no-check-cell", 0, &if_nocheck, 1}, {"no-cell-check", 0, &if_nocheck, 1}, {"check-peaks", 0, &if_peaks, 1}, + {"no-check-peaks", 0, &if_peaks, 0}, {"no-retry", 0, &if_retry, 0}, + {"retry", 0, &if_retry, 1}, {"no-multi", 0, &if_multi, 0}, + {"multi", 0, &if_multi, 1}, {"overpredict", 0, &iargs.overpredict, 1}, /* Long-only options which don't actually do anything */ @@ -851,6 +854,8 @@ int main(int argc, char *argv[]) if ( have_push_res && !(iargs.int_meth & INTEGRATION_RESCUT) ) { ERROR("WARNING: You used --push-res, but not -rescut, " "therefore --push-res will have no effect.\n"); + ERROR("WARNING: Add --integration=rings-rescut or " + "--integration=prof2d-rescut.\n"); } /* Parse unit cell tolerance */ @@ -955,15 +960,6 @@ int main(int argc, char *argv[]) } - /* Open output stream */ - st = open_stream_for_write_3(outfile, geom_filename, iargs.cell, - argc, argv); - if ( st == NULL ) { - ERROR("Failed to open stream '%s'\n", outfile); - return 1; - } - free(outfile); - if ( indm_str == NULL ) { STATUS("No indexing methods specified. I will try to "); @@ -1035,10 +1031,19 @@ int main(int argc, char *argv[]) ERROR("Failed to set up indexing system\n"); return 1; } - free(indm_str); } + /* Open output stream */ + st = open_stream_for_write_4(outfile, geom_filename, iargs.cell, + argc, argv, indm_str); + if ( st == NULL ) { + ERROR("Failed to open stream '%s'\n", outfile); + return 1; + } + free(outfile); + free(indm_str); + gsl_set_error_handler_off(); create_sandbox(&iargs, n_proc, prefix, config_basename, fh, diff --git a/src/merge.c b/src/merge.c index 17e57c90..14460953 100644 --- a/src/merge.c +++ b/src/merge.c @@ -50,7 +50,7 @@ /* Minimum partiality of a reflection for it to be merged */ -#define MIN_PART_MERGE (0.05) +#define MIN_PART_MERGE (0.3) struct merge_queue_args @@ -59,7 +59,6 @@ struct merge_queue_args pthread_rwlock_t full_lock; Crystal **crystals; int n_started; - PartialityModel pmodel; double push_res; int use_weak; long long int n_reflections; @@ -159,7 +158,6 @@ static void run_merge_job(void *vwargs, int cookie) signed int h, k, l; double mean, sumweight, M2, temp, delta, R; double corr, res, w; - //double esd; if ( get_partiality(refl) < MIN_PART_MERGE ) continue; @@ -189,7 +187,7 @@ static void run_merge_job(void *vwargs, int cookie) } /* Total (multiplicative) correction factor */ - corr = exp(-G) * exp(B*res*res) * get_lorentz(refl) + corr = 1.0/G * exp(B*res*res) * get_lorentz(refl) / get_partiality(refl); if ( isnan(corr) ) { ERROR("NaN corr:\n"); @@ -200,7 +198,7 @@ static void run_merge_job(void *vwargs, int cookie) continue; } - //esd = get_esd_intensity(refl) * corr; + /* Reflections count less the more they have to be scaled up */ w = 1.0; /* Running mean and variance calculation */ @@ -229,7 +227,7 @@ static void finalise_merge_job(void *vqargs, void *vwargs) RefList *merge_intensities(Crystal **crystals, int n, int n_threads, - PartialityModel pmodel, int min_meas, + int min_meas, double push_res, int use_weak) { RefList *full; @@ -245,7 +243,6 @@ RefList *merge_intensities(Crystal **crystals, int n, int n_threads, qargs.full = full; qargs.n_started = 0; qargs.crystals = crystals; - qargs.pmodel = pmodel; qargs.push_res = push_res; qargs.use_weak = use_weak; qargs.n_reflections = 0; @@ -285,3 +282,120 @@ RefList *merge_intensities(Crystal **crystals, int n, int n_threads, reflist_free(full); return full2; } + + +double residual(Crystal *cr, const RefList *full, int free, + int *pn_used, const char *filename) +{ + Reflection *refl; + RefListIterator *iter; + int n_used = 0; + double num = 0.0; + double den = 0.0; + double G = crystal_get_osf(cr); + double B = crystal_get_Bfac(cr); + UnitCell *cell = crystal_get_cell(cr); + + for ( refl = first_refl(crystal_get_reflections(cr), &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + double p, w, corr, res; + signed int h, k, l; + Reflection *match; + double I_full; + double int1, int2; + + if ( free && !get_flag(refl) ) continue; + + get_indices(refl, &h, &k, &l); + res = resolution(cell, h, k, l); + match = find_refl(full, h, k, l); + if ( match == NULL ) continue; + I_full = get_intensity(match); + + if ( get_redundancy(match) < 2 ) continue; + + p = get_partiality(refl); + //if ( p < 0.2 ) continue; + + corr = get_lorentz(refl) / (G * exp(-B*res*res)); + int1 = get_intensity(refl) * corr; + int2 = p*I_full; + w = p; + + num += fabs(int1 - int2) * w; + den += fabs(int1 + int2) * w/2.0; + + n_used++; + + } + + if ( pn_used != NULL ) *pn_used = n_used; + return num/(den*sqrt(2)); +} + + +double log_residual(Crystal *cr, const RefList *full, int free, + int *pn_used, const char *filename) +{ + double dev = 0.0; + double G, B; + Reflection *refl; + RefListIterator *iter; + int n_used = 0; + FILE *fh = NULL; + + G = crystal_get_osf(cr); + B = crystal_get_Bfac(cr); + if ( filename != NULL ) { + fh = fopen(filename, "a"); + if ( fh == NULL ) { + ERROR("Failed to open '%s'\n", filename); + } + } + + for ( refl = first_refl(crystal_get_reflections(cr), &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + double p, L, s, w; + signed int h, k, l; + Reflection *match; + double esd, I_full, I_partial; + double fx; + + if ( free && !get_flag(refl) ) continue; + + get_indices(refl, &h, &k, &l); + match = find_refl(full, h, k, l); + if ( match == NULL ) continue; + + p = get_partiality(refl); + L = get_lorentz(refl); + I_partial = get_intensity(refl); + I_full = get_intensity(match); + esd = get_esd_intensity(refl); + s = resolution(crystal_get_cell(cr), h, k, l); + + if ( I_partial <= 3.0*esd ) continue; /* Also because of log */ + if ( get_redundancy(match) < 2 ) continue; + if ( I_full <= 0 ) continue; /* Because log */ + if ( p <= 0.0 ) continue; /* Because of log */ + + fx = log(G) - B*s*s + log(p) + log(I_full) - log(I_partial) - log(L); + w = 1.0; + dev += w*fx*fx; + + if ( fh != NULL ) { + fprintf(fh, "%4i %4i %4i %e %e\n", + h, k, l, s, dev); + } + + } + + if ( fh != NULL ) fclose(fh); + + if ( pn_used != NULL ) *pn_used = n_used; + return dev; +} diff --git a/src/merge.h b/src/merge.h index 1911584f..b33afdea 100644 --- a/src/merge.h +++ b/src/merge.h @@ -40,7 +40,12 @@ #include "geometry.h" extern RefList *merge_intensities(Crystal **crystals, int n, int n_threads, - PartialityModel pmodel, int min_meas, - double push_res, int use_weak); + int min_meas, double push_res, int use_weak); + +extern double residual(Crystal *cr, const RefList *full, int free, + int *pn_used, const char *filename); + +extern double log_residual(Crystal *cr, const RefList *full, int free, + int *pn_used, const char *filename); #endif /* MERGE */ diff --git a/src/partial_sim.c b/src/partial_sim.c index 5926043f..218780fd 100644 --- a/src/partial_sim.c +++ b/src/partial_sim.c @@ -385,7 +385,7 @@ static void run_job(void *vwargs, int cookie) reflections = predict_to_res(cr, largest_q(&wargs->image)); crystal_set_reflections(cr, reflections); - calculate_partialities(cr, PMODEL_SCSPHERE); + calculate_partialities(cr, PMODEL_XSPHERE); for ( i=0; i<NBINS; i++ ) { wargs->n_ref[i] = 0; @@ -861,7 +861,7 @@ int main(int argc, char *argv[]) STATUS(" Background: none (no image " "output)\n"); } - STATUS(" Partiality model: scsphere (hardcoded)\n"); + STATUS(" Partiality model: xsphere (hardcoded)\n"); STATUS(" Noise standard deviation: %.2f detector units\n", noise_stddev); if ( random_intensities ) { @@ -873,6 +873,7 @@ int main(int argc, char *argv[]) input_file, sym_str); } STATUS(" Max error in cell components: %.2f %%\n", cnoise); + STATUS("Scale factor standard deviation: %.2f\n", osf_stddev); if ( random_intensities ) { full = reflist_new(); diff --git a/src/partialator.c b/src/partialator.c index 2540521d..e152db7c 100644 --- a/src/partialator.c +++ b/src/partialator.c @@ -3,11 +3,11 @@ * * Scaling and post refinement for coherent nanocrystallography * - * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2018 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -40,6 +40,7 @@ #include <assert.h> #include <pthread.h> #include <gsl/gsl_errno.h> +#include <sys/stat.h> #include <image.h> #include <utils.h> @@ -189,7 +190,7 @@ static void write_split(Crystal **crystals, int n_crystals, const char *outfile, } snprintf(tmp, 1024, "%s1", outfile); split = merge_intensities(crystals1, n_crystals1, nthreads, - pmodel, min_measurements, push_res, 1); + min_measurements, push_res, 1); if ( split == NULL ) { ERROR("Not enough crystals for two way split!\n"); @@ -201,7 +202,7 @@ static void write_split(Crystal **crystals, int n_crystals, const char *outfile, reflist_free(split); snprintf(tmp, 1024, "%s2", outfile); split = merge_intensities(crystals2, n_crystals2, nthreads, - pmodel, min_measurements, push_res, 1); + min_measurements, push_res, 1); STATUS("and %s\n", tmp); write_reflist_2(tmp, split, sym); reflist_free(split); @@ -288,7 +289,7 @@ static void write_custom_split(struct custom_split *csplit, int dsn, STATUS("Writing dataset '%s' to %s (%i crystals)\n", csplit->dataset_names[dsn], tmp, n_crystalsn); split = merge_intensities(crystalsn, n_crystalsn, nthreads, - pmodel, min_measurements, push_res, 1); + min_measurements, push_res, 1); write_reflist_2(tmp, split, sym); reflist_free(split); @@ -315,6 +316,7 @@ static void show_help(const char *s) " --stop-after=<n> Stop after merging <n> crystals.\n" " -n, --iterations=<n> Run <n> cycles of scaling and post-refinement.\n" " --no-scale Disable scale factor (G, B) refinement.\n" +" --no-Bscale Disable B factor scaling.\n" " --no-pr Disable orientation/physics refinement.\n" " -m, --model=<model> Specify partiality model.\n" " --min-measurements=<n> Minimum number of measurements to require.\n" @@ -324,7 +326,12 @@ static void show_help(const char *s) " -j <n> Run <n> analyses in parallel.\n" " --no-free Disable cross-validation (testing only).\n" " --custom-split List of files for custom dataset splitting.\n" -" --max-rel-B Maximum allowable relative |B| factor.\n"); +" --max-rel-B Maximum allowable relative |B| factor.\n" +" --no-logs Do not write extensive log files.\n" +" -w <pg> Apparent point group for resolving ambiguities.\n" +" --operator=<op> Indexing ambiguity operator for resolving.\n" +" --force-bandwidth=<n> Set all bandwidths to <n> (fraction).\n" +" --force-radius=<n> Set all profile radii to <n> nm^-1.\n"); } @@ -578,7 +585,7 @@ static int set_initial_params(Crystal *cr, FILE *fh) } else { - crystal_set_osf(cr, 0.0); + crystal_set_osf(cr, 1.0); crystal_set_Bfac(cr, 0.0); } @@ -603,13 +610,22 @@ static void select_free_reflections(RefList *list, gsl_rng *rng) static void write_to_pgraph(FILE *fh, RefList *list, RefList *full, Crystal *cr, - int fr) + int fr, signed int inum) { Reflection *refl; RefListIterator *iter; double G = crystal_get_osf(cr); double B = crystal_get_Bfac(cr); UnitCell *cell = crystal_get_cell(cr); + char ins[5]; + + if ( inum >= 0 ) { + snprintf(ins, 4, "%i", inum); + } else { + ins[0] = 'F'; + ins[1] = '\0'; + } + for ( refl = first_refl(list, &iter); refl != NULL; @@ -622,6 +638,9 @@ static void write_to_pgraph(FILE *fh, RefList *list, RefList *full, Crystal *cr, if ( !get_flag(refl) ) continue; /* Not free-flagged */ + /* Strong reflections only */ + if ( get_intensity(refl) < 3.0*get_esd_intensity(refl) ) continue; + get_indices(refl, &h, &k, &l); res = resolution(cell, h, k, l); if ( 2.0*res > crystal_get_resolution_limit(cr) ) continue; @@ -629,46 +648,55 @@ static void write_to_pgraph(FILE *fh, RefList *list, RefList *full, Crystal *cr, match = find_refl(full, h, k, l); if ( match == NULL ) continue; + /* Don't calculate pobs if reference reflection is weak */ + if ( fabs(get_intensity(match)) / get_esd_intensity(match) < 3.0 ) continue; + /* Calculated partiality */ pcalc = get_partiality(refl); /* Observed partiality */ - corr = exp(-G) * exp(B*res*res) * get_lorentz(refl); + corr = G * exp(B*res*res) * get_lorentz(refl); Ipart = get_intensity(refl) * corr; pobs = Ipart / get_intensity(match); - fprintf(fh, "%5i %4i %4i %4i %8.4f %8.3f %8.3f\n", - fr, h, k, l, 2*res/1e9, pcalc, pobs); + fprintf(fh, "%5i %4i %4i %4i %e %e %8.3f %8.3f %s\n", + fr, h, k, l, 2*res, Ipart, pcalc, pobs, ins); } } static void write_pgraph(RefList *full, Crystal **crystals, int n_crystals, - int iter) + signed int iter, const char *suff) { FILE *fh; char tmp[256]; int i; - snprintf(tmp, 256, "pgraph-iter%i.dat", iter); + snprintf(tmp, 256, "pr-logs/pgraph%s.dat", suff); + + if ( iter == 0 ) { + fh = fopen(tmp, "w"); + } else { + fh = fopen(tmp, "a"); + } - fh = fopen(tmp, "w"); if ( fh == NULL ) { ERROR("Failed to open '%s'\n", tmp); return; } - fprintf(fh, " fr h k l 1/d(nm) pcalc pobs\n"); + if ( iter == 0 ) { + fprintf(fh, " Crystal h k l 1/d(m) Ipart pcalc pobs iteration\n"); + } for ( i=0; i<n_crystals; i++ ) { if ( crystal_get_user_flag(crystals[i]) != 0 ) continue; write_to_pgraph(fh, crystal_get_reflections(crystals[i]), full, - crystals[i], i); + crystals[i], i, iter); } fclose(fh); - } @@ -689,6 +717,7 @@ static void all_residuals(Crystal **crystals, int n_crystals, RefList *full, if ( crystal_get_user_flag(crystals[i]) ) continue; + /* Scaling should have been done right before calling this */ r = residual(crystals[i], full, 0, NULL, NULL); free_r = residual(crystals[i], full, 1, NULL, NULL); log_r = log_residual(crystals[i], full, 0, NULL, NULL); @@ -719,32 +748,82 @@ static void show_all_residuals(Crystal **crystals, int n_crystals, } -static void dump_parameters(const char *filename, Crystal **crystals, - int n_crystals) +struct log_qargs { - FILE *fh; - fh = fopen(filename, "w"); - if ( fh == NULL ) { - ERROR("Couldn't open partialator.params!\n"); - } else { - fprintf(fh, " cr OSF relB div" - " flag filename event\n"); - int i; - for ( i=0; i<n_crystals; i++ ) { - struct image *img; - char *evt_str; - img = crystal_get_image(crystals[i]); - evt_str = get_event_string(img->event); - fprintf(fh, "%4i %10.5f %10.2f %8.5e %-25s %s %s\n", - i, crystal_get_osf(crystals[i]), - crystal_get_Bfac(crystals[i])*1e20, - crystal_get_image(crystals[i])->div, - str_prflag(crystal_get_user_flag(crystals[i])), - img->filename, evt_str); - free(evt_str); - } - fclose(fh); - } + int iter; + int next; + Crystal **crystals; + int n_crystals; + RefList *full; + int scaleflags; +}; + + +struct log_args +{ + Crystal *cr; + RefList *full; + int scaleflags; + int iter; + int cnum; +}; + + +static void *get_log_task(void *vp) +{ + struct log_qargs *qargs = vp; + struct log_args *task; + + if ( qargs->next >= qargs->n_crystals ) return NULL; + + task = malloc(sizeof(struct log_args)); + if ( task == NULL ) return NULL; + + task->cr = qargs->crystals[qargs->next]; + task->full = qargs->full; + task->iter = qargs->iter; + task->cnum = qargs->next; + task->scaleflags = qargs->scaleflags; + + qargs->next += 20; + return task; +} + + +static void write_logs(void *vp, int cookie) +{ + struct log_args *args = vp; + write_specgraph(args->cr, args->full, args->iter, args->cnum); + write_gridscan(args->cr, args->full, args->iter, args->cnum, + args->scaleflags); + write_test_logs(args->cr, args->full, args->iter, args->cnum); +} + + +static void done_log(void *qargs, void *vp) +{ + struct log_args *task = vp; + free(task); +} + + +static void write_logs_parallel(Crystal **crystals, int n_crystals, + RefList *full, int iter, int n_threads, + int scaleflags) +{ + struct log_qargs qargs; + + qargs.iter = iter; + qargs.next = 0; + qargs.full = full; + qargs.crystals = crystals; + qargs.n_crystals = n_crystals; + qargs.scaleflags = scaleflags; + + STATUS("Writing logs...\n"); + run_threads(n_threads, write_logs, get_log_task, done_log, &qargs, + n_crystals/20, 0, 0, 0); + STATUS("Done.\n"); } @@ -755,9 +834,10 @@ int main(int argc, char *argv[]) char *outfile = NULL; char *sym_str = NULL; SymOpList *sym; + SymOpList *amb; + SymOpList *w_sym; int nthreads = 1; int i; - struct image *images; int n_iter = 10; RefList *full; int n_images = 0; @@ -765,11 +845,12 @@ int main(int argc, char *argv[]) int n_crystals_seen = 0; char cmdline[1024]; int no_scale = 0; + int no_Bscale = 0; int no_pr = 0; Stream *st; Crystal **crystals; char *pmodel_str = NULL; - PartialityModel pmodel = PMODEL_SCSPHERE; + PartialityModel pmodel = PMODEL_XSPHERE; int min_measurements = 2; char *rval; int polarisation = 1; @@ -785,6 +866,15 @@ int main(int argc, char *argv[]) char *csplit_fn = NULL; struct custom_split *csplit = NULL; double max_B = 1e-18; + char *rfile = NULL; + RefList *reference = NULL; + int no_logs = 0; + char *w_sym_str = NULL; + char *operator = NULL; + double force_bandwidth = -1.0; + double force_radius = -1.0; + char *audit_info; + int scaleflags = 0; /* Long options */ const struct option longopts[] = { @@ -805,10 +895,15 @@ int main(int argc, char *argv[]) {"push-res", 1, NULL, 5}, {"res-push", 1, NULL, 5}, /* compat */ {"custom-split", 1, NULL, 6}, - {"max-rel-B" , 1, NULL, 7}, - {"max-rel-b" , 1, NULL, 7}, /* compat */ + {"max-rel-B", 1, NULL, 7}, + {"max-rel-b", 1, NULL, 7}, /* compat */ + {"reference", 1, NULL, 8}, /* ssshhh! */ + {"operator", 1, NULL, 9}, + {"force-bandwidth", 1, NULL, 10}, + {"force-radius", 1, NULL, 11}, {"no-scale", 0, &no_scale, 1}, + {"no-Bscale", 0, &no_Bscale, 1}, {"no-pr", 0, &no_pr, 1}, {"no-polarisation", 0, &polarisation, 0}, {"no-polarization", 0, &polarisation, 0}, /* compat */ @@ -816,6 +911,7 @@ int main(int argc, char *argv[]) {"polarization", 0, &polarisation, 1}, /* compat */ {"no-free", 0, &no_free, 1}, {"output-every-cycle", 0, &output_everycycle, 1}, + {"no-logs", 0, &no_logs, 1}, {0, 0, NULL, 0} }; @@ -827,7 +923,7 @@ int main(int argc, char *argv[]) } /* Short options */ - while ((c = getopt_long(argc, argv, "hi:o:g:b:y:n:j:m:", + while ((c = getopt_long(argc, argv, "hi:o:g:b:y:n:j:m:w:", longopts, NULL)) != -1) { @@ -886,6 +982,10 @@ int main(int argc, char *argv[]) pmodel_str = strdup(optarg); break; + case 'w' : + w_sym_str = strdup(optarg); + break; + case 2 : errno = 0; min_measurements = strtod(optarg, &rval); @@ -932,6 +1032,33 @@ int main(int argc, char *argv[]) max_B = max_B * 1e-20; break; + case 8 : + rfile = strdup(optarg); + break; + + case 9 : + operator = strdup(optarg); + break; + + case 10 : + errno = 0; + force_bandwidth = strtod(optarg, &rval); + if ( *rval != '\0' ) { + ERROR("Invalid value for --force-bandwidth.\n"); + return 1; + } + break; + + case 11 : + errno = 0; + force_radius = strtod(optarg, &rval); + if ( *rval != '\0' ) { + ERROR("Invalid value for --force-radius.\n"); + return 1; + } + force_radius *= 1e9; + break; + case 0 : break; @@ -971,13 +1098,49 @@ int main(int argc, char *argv[]) free(sym_str); if ( sym == NULL ) return 1; + if ( (w_sym_str != NULL) && (operator != NULL) ) { + ERROR("Specify the apparent symmetry (-w) or the operator, " + "not both.\n"); + return 1; + } + + if ( w_sym_str == NULL ) { + w_sym = NULL; + amb = NULL; + } else { + pointgroup_warning(w_sym_str); + w_sym = get_pointgroup(w_sym_str); + free(w_sym_str); + if ( w_sym == NULL ) return 1; + amb = get_ambiguities(w_sym, sym); + if ( amb == NULL ) { + ERROR("Couldn't find ambiguity operator.\n"); + ERROR("Check that your values for -y and -w are " + "correct.\n"); + return 1; + } + + } + + if ( operator ) { + amb = parse_symmetry_operations(operator); + if ( amb == NULL ) return 1; + set_symmetry_name(amb, "Ambiguity"); + } + + if ( amb != NULL ) { + STATUS("Indexing ambiguity resolution enabled. " + "The ambiguity operation(s) are:\n"); + describe_symmetry(amb); + /* In contrast to ambigator, partialator can deal with multiple + * ambiguities at once */ + } + if ( pmodel_str != NULL ) { if ( strcmp(pmodel_str, "unity") == 0 ) { pmodel = PMODEL_UNITY; - } else if ( strcmp(pmodel_str, "scgaussian") == 0 ) { - pmodel = PMODEL_SCGAUSSIAN; - } else if ( strcmp(pmodel_str, "scsphere") == 0 ) { - pmodel = PMODEL_SCSPHERE; + } else if ( strcmp(pmodel_str, "xsphere") == 0 ) { + pmodel = PMODEL_XSPHERE; } else if ( strcmp(pmodel_str, "random") == 0 ) { pmodel = PMODEL_RANDOM; } else { @@ -988,7 +1151,25 @@ int main(int argc, char *argv[]) if ( (pmodel == PMODEL_UNITY) && !no_pr ) { no_pr = 1; - STATUS("Setting --no-pr because we are not modelling partialities (--model=unity).\n"); + STATUS("Setting --no-pr because we are not modelling " + "partialities (--model=unity).\n"); + } + + if ( no_Bscale ) { + scaleflags |= SCALE_NO_B; + } + + if ( !no_logs ) { + int r = mkdir("pr-logs", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if ( r ) { + if ( errno == EEXIST ) { + ERROR("WARNING: pr-logs folder already exists. " + "Beware of mixing old and new log files!\n"); + } else { + ERROR("Failed to create pr-logs folder.\n"); + return 1; + } + } } /* Read the custom split list (if applicable) */ @@ -1001,6 +1182,20 @@ int main(int argc, char *argv[]) free(csplit_fn); } + if ( rfile != NULL ) { + RefList *rread; + rread = read_reflections(rfile); + if ( rread == NULL ) { + ERROR("Failed to read reference reflections\n"); + return 1; + } + reference = asymmetric_indices(rread, sym); + reflist_free(rread); + ERROR("WARNING: Using an external reference.\n"); + ERROR("WARNING: If you publish a structure based on the result," + " expect to have to retract your paper!\n"); + } + gsl_set_error_handler_off(); rng = gsl_rng_alloc(gsl_rng_mt19937); @@ -1008,7 +1203,6 @@ int main(int argc, char *argv[]) n_images = 0; n_crystals = 0; n_crystals_seen = 0; - images = NULL; crystals = NULL; if ( sparams_fn != NULL ) { char line[1024]; @@ -1032,35 +1226,27 @@ int main(int argc, char *argv[]) RefList *as; int i; - struct image *images_new; - struct image *cur; + struct image cur; - images_new = realloc(images, (n_images+1)*sizeof(struct image)); - if ( images_new == NULL ) { - ERROR("Failed to allocate memory for image list.\n"); - return 1; - } - images = images_new; - cur = &images[n_images]; - - cur->div = NAN; - cur->bw = NAN; - cur->det = NULL; - if ( read_chunk_2(st, cur, STREAM_READ_REFLECTIONS - | STREAM_READ_UNITCELL) != 0 ) { + cur.div = NAN; + cur.bw = NAN; + cur.det = NULL; + if ( read_chunk_2(st, &cur, STREAM_READ_REFLECTIONS + | STREAM_READ_UNITCELL) != 0 ) { break; } - if ( isnan(cur->div) || isnan(cur->bw) ) { + if ( isnan(cur.div) || isnan(cur.bw) ) { ERROR("Chunk doesn't contain beam parameters.\n"); return 1; } - for ( i=0; i<cur->n_crystals; i++ ) { + for ( i=0; i<cur.n_crystals; i++ ) { Crystal *cr; Crystal **crystals_new; RefList *cr_refl; + struct image *image; n_crystals_seen++; if ( n_crystals_seen <= start_after ) continue; @@ -1073,11 +1259,19 @@ int main(int argc, char *argv[]) return 1; } crystals = crystals_new; - crystals[n_crystals] = cur->crystals[i]; + crystals[n_crystals] = cur.crystals[i]; cr = crystals[n_crystals]; - /* Image pointer will change due to later reallocs */ - crystal_set_image(cr, NULL); + image = malloc(sizeof(struct image)); + if ( image == NULL ) { + ERROR("Failed to allocatea memory for image.\n"); + return 1; + } + + crystal_set_image(cr, image); + *image = cur; + image->n_crystals = 1; + image->crystals = &crystals[n_crystals]; /* This is the raw list of reflections */ cr_refl = crystal_get_reflections(cr); @@ -1087,7 +1281,7 @@ int main(int argc, char *argv[]) if ( polarisation ) { polarisation_correction(cr_refl, crystal_get_cell(cr), - cur); + image); } if ( !no_free ) select_free_reflections(cr_refl, rng); @@ -1120,68 +1314,77 @@ int main(int argc, char *argv[]) display_progress(n_images, n_crystals); fprintf(stderr, "\n"); if ( sparams_fh != NULL ) fclose(sparams_fh); - + audit_info = stream_audit_info(st); close_stream(st); - /* Fill in image pointers */ - for ( i=0; i<n_images; i++ ) { - int j; - for ( j=0; j<images[i].n_crystals; j++ ) { - - Crystal *cryst; - - cryst = images[i].crystals[j]; - crystal_set_image(cryst, &images[i]); - - /* Now it's safe to do the following */ - update_predictions(cryst); - calculate_partialities(cryst, pmodel); - + STATUS("Initial partiality calculation...\n"); + for ( i=0; i<n_crystals; i++ ) { + Crystal *cr = crystals[i]; + if ( force_bandwidth > 0.0 ) { + crystal_get_image(cr)->bw = force_bandwidth; } + if ( force_radius > 0.0 ) { + crystal_set_profile_radius(cr, force_radius); + } + update_predictions(cr); + calculate_partialities(cr, pmodel); } if (csplit != NULL) check_csplit(crystals, n_crystals, csplit); /* Make a first pass at cutting out crap */ - STATUS("Checking patterns.\n"); + //STATUS("Early rejection...\n"); //early_rejection(crystals, n_crystals); - /* Initial rejection, figures of merit etc */ - full = merge_intensities(crystals, n_crystals, nthreads, pmodel, - min_measurements, push_res, 1); + /* Create reference data set if we don't already have one */ + if ( reference == NULL ) { + if ( !no_scale ) { + STATUS("Initial scaling...\n"); + scale_all(crystals, n_crystals, nthreads, scaleflags); + } + full = merge_intensities(crystals, n_crystals, nthreads, + min_measurements, push_res, 1); + } else { + full = reference; + } + + /* Check rejection and write figures of merit */ check_rejection(crystals, n_crystals, full, max_B); show_all_residuals(crystals, n_crystals, full); - write_pgraph(full, crystals, n_crystals, 0); + if ( !no_pr && !no_logs ) { + write_pgraph(full, crystals, n_crystals, 0, ""); + write_logs_parallel(crystals, n_crystals, full, 0, nthreads, + scaleflags); + } /* Iterate */ for ( i=0; i<n_iter; i++ ) { STATUS("Scaling and refinement cycle %i of %i\n", i+1, n_iter); - if ( !no_scale ) { - scale_all(crystals, n_crystals, nthreads, pmodel); - reflist_free(full); - full = merge_intensities(crystals, n_crystals, nthreads, - pmodel, min_measurements, - push_res, 1); + if ( !no_pr ) { + refine_all(crystals, n_crystals, full, nthreads, pmodel, + 0, i+1, no_logs, sym, amb, scaleflags); } - if ( !no_pr ) { - refine_all(crystals, n_crystals, full, nthreads, - pmodel); + /* Create new reference if needed */ + if ( reference == NULL ) { reflist_free(full); + if ( !no_scale ) { + scale_all(crystals, n_crystals, nthreads, + scaleflags); + } full = merge_intensities(crystals, n_crystals, nthreads, - pmodel, min_measurements, + min_measurements, push_res, 1); - } + } /* else full still equals reference */ check_rejection(crystals, n_crystals, full, max_B); - reflist_free(full); - full = merge_intensities(crystals, n_crystals, nthreads, - pmodel, min_measurements, - push_res, 1); show_all_residuals(crystals, n_crystals, full); - write_pgraph(full, crystals, n_crystals, i+1); + + if ( !no_logs ) { + write_pgraph(full, crystals, n_crystals, i+1, ""); + } if ( output_everycycle ) { @@ -1208,21 +1411,37 @@ int main(int argc, char *argv[]) } } - /* Dump parameters */ - snprintf(tmp, 1024, "iter%.2d_partialator.params", i+1); - dump_parameters(tmp, crystals, n_crystals); + } + } + /* Final merge */ + STATUS("Final merge...\n"); + if ( reference == NULL ) { + reflist_free(full); + if ( !no_scale ) { + scale_all(crystals, n_crystals, nthreads, scaleflags); } + full = merge_intensities(crystals, n_crystals, nthreads, + min_measurements, + push_res, 1); + } else { + full = merge_intensities(crystals, n_crystals, nthreads, + min_measurements, push_res, 1); } - full = merge_intensities(crystals, n_crystals, nthreads, pmodel, - min_measurements, push_res, 1); + /* Write final figures of merit (no rejection any more) */ show_all_residuals(crystals, n_crystals, full); - write_pgraph(full, crystals, n_crystals, n_iter+1); + if ( !no_pr && !no_logs ) { + write_pgraph(full, crystals, n_crystals, -1, ""); + write_logs_parallel(crystals, n_crystals, full, -1, nthreads, + scaleflags); + } /* Output results */ STATUS("Writing overall results to %s\n", outfile); reflist_add_command_and_version(full, argc, argv); + reflist_add_notes(full, "Audit information from stream:"); + reflist_add_notes(full, audit_info); write_reflist_2(outfile, full, sym); /* Output split results */ @@ -1238,9 +1457,6 @@ int main(int argc, char *argv[]) } } - /* Dump parameters */ - dump_parameters("partialator.params", crystals, n_crystals); - /* Clean up */ gsl_rng_free(rng); for ( i=0; i<n_crystals; i++ ) { @@ -1248,13 +1464,9 @@ int main(int argc, char *argv[]) crystal_free(crystals[i]); } reflist_free(full); - free(sym); + free_symoplist(sym); free(outfile); free(crystals); - for ( i=0; i<n_images; i++ ) { - free(images[i].filename); - } - free(images); free(infile); return 0; diff --git a/src/post-refinement.c b/src/post-refinement.c index bf30d299..bde9c02c 100644 --- a/src/post-refinement.c +++ b/src/post-refinement.c @@ -3,11 +3,11 @@ * * Post refinement * - * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2018 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -33,11 +33,8 @@ #include <stdlib.h> #include <assert.h> -#include <gsl/gsl_matrix.h> -#include <gsl/gsl_vector.h> -#include <gsl/gsl_linalg.h> -#include <gsl/gsl_eigen.h> -#include <gsl/gsl_blas.h> +#include <gsl/gsl_multimin.h> +#include <gsl/gsl_fit.h> #include "image.h" #include "post-refinement.h" @@ -46,15 +43,14 @@ #include "geometry.h" #include "cell.h" #include "cell-utils.h" +#include "reflist-utils.h" +#include "scaling.h" +#include "merge.h" -/* Maximum number of iterations of NLSq to do for each image per macrocycle. */ -#define MAX_CYCLES (10) - struct prdata { int refined; - int n_filtered; }; const char *str_prflag(enum prflag flag) @@ -79,625 +75,820 @@ const char *str_prflag(enum prflag flag) case PRFLAG_BIGB : return "B too big"; + case PRFLAG_SCALEBAD : + return "bad scaling"; + default : return "Unknown flag"; } } -/* Returns dp(gauss)/dr at "r" */ -static double gaussian_fraction_gradient(double r, double R) +static UnitCell *rotate_cell_xy(const UnitCell *cell, double ang1, double ang2) { - const double ng = 2.6; - const double sig = R/ng; - - /* If the Ewald sphere isn't within the profile, the gradient is zero */ - if ( r < -R ) return 0.0; - if ( r > +R ) return 0.0; - - return exp(-pow(r/sig, 2.0)/2.0) / (sig*sqrt(2.0*M_PI)); + UnitCell *o; + double asx, asy, asz; + double bsx, bsy, bsz; + double csx, csy, csz; + double xnew, ynew, znew; + + o = cell_new_from_cell(cell); + + cell_get_reciprocal(o, &asx, &asy, &asz, + &bsx, &bsy, &bsz, + &csx, &csy, &csz); + + /* "a" around x */ + xnew = asx; + ynew = asy*cos(ang1) + asz*sin(ang1); + znew = -asy*sin(ang1) + asz*cos(ang1); + asx = xnew; asy = ynew; asz = znew; + + /* "b" around x */ + xnew = bsx; + ynew = bsy*cos(ang1) + bsz*sin(ang1); + znew = -bsy*sin(ang1) + bsz*cos(ang1); + bsx = xnew; bsy = ynew; bsz = znew; + + /* "c" around x */ + xnew = csx; + ynew = csy*cos(ang1) + csz*sin(ang1); + znew = -csy*sin(ang1) + csz*cos(ang1); + csx = xnew; csy = ynew; csz = znew; + + /* "a" around y */ + xnew = asx*cos(ang2) + asz*sin(ang2); + ynew = asy; + znew = -asx*sin(ang2) + asz*cos(ang2); + asx = xnew; asy = ynew; asz = znew; + + /* "b" around y */ + xnew = bsx*cos(ang2) + bsz*sin(ang2); + ynew = bsy; + znew = -bsx*sin(ang2) + bsz*cos(ang2); + bsx = xnew; bsy = ynew; bsz = znew; + + /* "c" around y */ + xnew = csx*cos(ang2) + csz*sin(ang2); + ynew = csy; + znew = -csx*sin(ang2) + csz*cos(ang2); + csx = xnew; csy = ynew; csz = znew; + + cell_set_reciprocal(o, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); + + return o; } -/* Returns dp(sph)/dr at "r" */ -static double sphere_fraction_gradient(double r, double pr) +static const char *get_label(enum gparam p) { - double q, dpdq, dqdr; - - /* If the Ewald sphere isn't within the profile, the gradient is zero */ - if ( r < -pr ) return 0.0; - if ( r > +pr ) return 0.0; - - q = (r + pr)/(2.0*pr); - dpdq = 6.0*(q - q*q); - dqdr = 1.0 / (2.0*pr); - return dpdq * dqdr; + switch ( p ) { + case GPARAM_ANG1 : return "First angle (radians)"; + case GPARAM_ANG2 : return "Second angle (radians)"; + case GPARAM_R : return "Profile radius (m^-1)"; + case GPARAM_WAVELENGTH : return "Wavelength (Angstroms)"; + default : return "unknown"; + } } -/* Returns dp/dr at "r" */ -static double partiality_gradient(double r, double pr, - PartialityModel pmodel, - double rlow, double rhigh) +/* We set all the step sizes to 1, then scale them. + * This way, the size of the simplex stays meaningful and we possibly also + * avoid some roundoff errors */ +static double get_scale(enum gparam p) { - double A, D; - - D = rlow - rhigh; - - switch ( pmodel ) { + switch ( p ) { - default: - case PMODEL_UNITY: - return 0.0; + case GPARAM_ANG1 : return deg2rad(0.05); + case GPARAM_ANG2 : return deg2rad(0.05); + case GPARAM_R : return 0.0005e9; + case GPARAM_WAVELENGTH : return 0.001e-10; - case PMODEL_SCSPHERE: - A = sphere_fraction_gradient(r, pr)/D; - return 4.0*pr*A/3.0; - - case PMODEL_SCGAUSSIAN: - A = gaussian_fraction_gradient(r, pr)/D; - return 4.0*pr*A/3.0; + default : return 0.0; } } -static double sphere_fraction_rgradient(double r, double R) -{ - /* If the Ewald sphere isn't within the profile, the gradient is zero */ - if ( r < -R ) return 0.0; - if ( r > +R ) return 0.0; - - return -(3.0*r/(4.0*R*R)) * (1.0 - r*r/(R*R)); -} +struct rf_priv { + const Crystal *cr; + const RefList *full; + enum gparam rv[32]; + int verbose; + int serial; + gsl_vector *initial; + int scaleflags; + /* For freeing later */ + gsl_vector *vals; + gsl_vector *step; -static double gaussian_fraction_rgradient(double r, double R) -{ - const double ng = 2.6; - const double sig = R/ng; + /* So that it stays around until the end of minimisation */ + gsl_multimin_function f; +}; - /* If the Ewald sphere isn't within the profile, the gradient is zero */ - if ( r < -R ) return 0.0; - if ( r > +R ) return 0.0; - return -(ng*r/(sqrt(2.0*M_PI)*R*R))*exp(-r*r/(2.0*sig*sig)); +static double get_actual_val(const gsl_vector *v, const gsl_vector *initial, + enum gparam *rv, int i) +{ + return gsl_vector_get(v, i) * get_scale(rv[i]) + + gsl_vector_get(initial, i); } -static double volume_fraction_rgradient(double r, double pr, - PartialityModel pmodel) +static void apply_parameters(const gsl_vector *v, const gsl_vector *initial, + enum gparam *rv, Crystal *cr) { - switch ( pmodel ) - { - case PMODEL_UNITY : - return 1.0; - - case PMODEL_SCSPHERE : - return sphere_fraction_rgradient(r, pr); + int i; + double ang1, ang2, R, lambda; + UnitCell *cell; - case PMODEL_SCGAUSSIAN : - return gaussian_fraction_rgradient(r, pr); + /* Default parameters if not used in refinement */ + ang1 = 0.0; + ang2 = 0.0; + R = crystal_get_profile_radius(cr); + lambda = crystal_get_image(cr)->lambda; - default : - ERROR("No pmodel in volume_fraction_rgradient!\n"); - return 1.0; - } -} + for ( i=0; i<v->size; i++ ) { + double val; -static double volume_fraction(double rlow, double rhigh, double pr, - PartialityModel pmodel) -{ - switch ( pmodel ) - { - case PMODEL_UNITY : - return 1.0; + val = get_actual_val(v, initial, rv, i); - case PMODEL_SCSPHERE : - return sphere_fraction(rlow, rhigh, pr); + switch ( rv[i] ) { - case PMODEL_SCGAUSSIAN : - return gaussian_fraction(rlow, rhigh, pr); + case GPARAM_ANG1 : + ang1 = val; + break; - default : - ERROR("No pmodel in volume_fraction!\n"); - return 1.0; - } -} + case GPARAM_ANG2 : + ang2 = val; + break; + case GPARAM_R : + R = val; + break; -/* Return the gradient of "fx" wrt parameter 'k' given the current - * status of the crystal. */ -double gradient(Crystal *cr, int k, Reflection *refl, PartialityModel pmodel) -{ - double glow, ghigh; - double rlow, rhigh, p; - struct image *image = crystal_get_image(cr); - double R = crystal_get_profile_radius(cr); - double gr; + case GPARAM_WAVELENGTH : + lambda = val; + break; - get_partial(refl, &rlow, &rhigh, &p); + default : + ERROR("Don't understand parameter %i\n", rv[i]); + break; - if ( k == GPARAM_R ) { + } + } - double Rglow, Rghigh; - double D, psph; + cell = rotate_cell_xy(crystal_get_cell_const(cr), ang1, ang2); + crystal_set_cell(cr, cell); - D = rlow - rhigh; - psph = volume_fraction(rlow, rhigh, R, pmodel); + crystal_set_profile_radius(cr, R); + crystal_get_image(cr)->lambda = lambda; +} - Rglow = volume_fraction_rgradient(rlow, R, pmodel); - Rghigh = volume_fraction_rgradient(rhigh, R, pmodel); - gr = 4.0*psph/(3.0*D) + (4.0*R/(3.0*D))*(Rglow - Rghigh); - return gr; +static double residual_f(const gsl_vector *v, void *pp) +{ + struct rf_priv *pv = pp; + RefList *list; + struct image im; + Crystal *cr; + UnitCell *cell; + double res; + int i; + for ( i=0; i<v->size; i++ ) { + if ( gsl_vector_get(v, i) > 100.0 ) return GSL_NAN; } - /* Calculate the gradient of partiality wrt excitation error. */ - glow = partiality_gradient(rlow, R, pmodel, rlow, rhigh); - ghigh = partiality_gradient(rhigh, R, pmodel, rlow, rhigh); + cr = crystal_copy(pv->cr); + cell = cell_new_from_cell(crystal_get_cell(cr)); + if ( cell == NULL ) return GSL_NAN; + crystal_set_cell(cr, cell); + im = *crystal_get_image(cr); + crystal_set_image(cr, &im); + apply_parameters(v, pv->initial, pv->rv, cr); + + if ( fabs(crystal_get_profile_radius(cr)) > 5e9 ) { + crystal_free(cr); + if ( pv->verbose ) STATUS("radius > 5e9\n"); + return GSL_NAN; + } - if ( k == GPARAM_DIV ) { + /* Can happen with grid scans and certain --force-radius values */ + if ( fabs(crystal_get_profile_radius(cr)) < 0.0000001e9 ) { + crystal_free(cr); + if ( pv->verbose ) STATUS("radius very small\n"); + return GSL_NAN; + } - double D, psph, ds; - signed int hs, ks, ls; + if ( im.lambda <= 0.0 ) { + crystal_free(cr); + if ( pv->verbose ) STATUS("lambda < 0\n"); + return GSL_NAN; + } - D = rlow - rhigh; - psph = volume_fraction(rlow, rhigh, R, pmodel); - get_symmetric_indices(refl, &hs, &ks, &ls); - ds = 2.0 * resolution(crystal_get_cell(cr), hs, ks, ls); + list = copy_reflist(crystal_get_reflections(cr)); + crystal_set_reflections(cr, list); - gr = (ds/2.0)*(glow+ghigh) - 4.0*R*psph*ds/(3.0*D*D); - return gr; + update_predictions(cr); + calculate_partialities(cr, PMODEL_XSPHERE); + if ( scale_one_crystal(cr, pv->full, pv->scaleflags) ) { + crystal_free(cr); + if ( pv->verbose ) STATUS("Bad scaling\n"); + return GSL_NAN; } + res = residual(cr, pv->full, 0, NULL, NULL); + + cell_free(crystal_get_cell(cr)); + reflist_free(crystal_get_reflections(cr)); + crystal_free(cr); - gr = r_gradient(crystal_get_cell(cr), k, refl, image) * (glow-ghigh); - return gr; + return res; } -static void apply_cell_shift(UnitCell *cell, int k, double shift) +static double get_initial_param(Crystal *cr, enum gparam p) { - double asx, asy, asz; - double bsx, bsy, bsz; - double csx, csy, csz; - double as, bs, cs; + switch ( p ) { - cell_get_reciprocal(cell, &asx, &asy, &asz, - &bsx, &bsy, &bsz, - &csx, &csy, &csz); - as = modulus(asx, asy, asz); - bs = modulus(bsx, bsy, bsz); - cs = modulus(csx, csy, csz); + case GPARAM_ANG1 : return 0.0; + case GPARAM_ANG2 : return 0.0; + case GPARAM_R : return crystal_get_profile_radius(cr); + case GPARAM_WAVELENGTH : return crystal_get_image(cr)->lambda; - /* Forbid any step which looks too large */ - switch ( k ) - { - case GPARAM_ASX : - case GPARAM_ASY : - case GPARAM_ASZ : - if ( fabs(shift) > 0.1*as ) return; - break; - - case GPARAM_BSX : - case GPARAM_BSY : - case GPARAM_BSZ : - if ( fabs(shift) > 0.1*bs ) return; - break; - - case GPARAM_CSX : - case GPARAM_CSY : - case GPARAM_CSZ : - if ( fabs(shift) > 0.1*cs ) return; - break; - } - - switch ( k ) - { - case GPARAM_ASX : asx += shift; break; - case GPARAM_ASY : asy += shift; break; - case GPARAM_ASZ : asz += shift; break; - case GPARAM_BSX : bsx += shift; break; - case GPARAM_BSY : bsy += shift; break; - case GPARAM_BSZ : bsz += shift; break; - case GPARAM_CSX : csx += shift; break; - case GPARAM_CSY : csy += shift; break; - case GPARAM_CSZ : csz += shift; break; - } - - cell_set_reciprocal(cell, asx, asy, asz, - bsx, bsy, bsz, - csx, csy, csz); + default: return 0.0; + + } } -/* Apply the given shift to the 'k'th parameter of 'image'. */ -static void apply_shift(Crystal *cr, int k, double shift) +static int check_angle_shifts(gsl_vector *cur, const gsl_vector *initial, + enum gparam *rv, struct rf_priv *residual_f_priv) { - double t; - struct image *image = crystal_get_image(cr); - - switch ( k ) { - - case GPARAM_DIV : - if ( shift > 0.1*image->div ) return; - image->div += shift; - if ( image->div < 0.0 ) image->div = 0.0; - break; - - case GPARAM_R : - t = crystal_get_profile_radius(cr); - if ( shift > 0.1*t ) return; - t += shift; - crystal_set_profile_radius(cr, t); - break; - - case GPARAM_ASX : - case GPARAM_ASY : - case GPARAM_ASZ : - case GPARAM_BSX : - case GPARAM_BSY : - case GPARAM_BSZ : - case GPARAM_CSX : - case GPARAM_CSY : - case GPARAM_CSZ : - apply_cell_shift(crystal_get_cell(cr), k, shift); - break; + int i = 0; + double ang = 0.0; - default : - ERROR("No shift defined for parameter %i\n", k); - abort(); + while ( rv[i] != GPARAM_EOL ) { + if ( (rv[i] == GPARAM_ANG1) || (rv[i] == GPARAM_ANG2) ) { + ang += fabs(get_actual_val(cur, initial, rv, i)); + } + rv++; + } + if ( rad2deg(ang) > 5.0 ) { + ERROR("More than 5 degrees total rotation!\n"); + residual_f_priv->verbose = 1; + double res = residual_f(cur, residual_f_priv); + STATUS("residual after rotation = %e\n", res); + residual_f_priv->verbose = 2; + res = residual_f(initial, residual_f_priv); + STATUS("residual before rotation = %e\n", res); + return 1; } + return 0; } -/* Perform one cycle of post refinement on 'image' against 'full' */ -static double pr_iterate(Crystal *cr, const RefList *full, - PartialityModel pmodel, - int *n_filtered, int verbose) +static RefList *reindex_reflections(RefList *input, SymOpList *amb, + SymOpList *sym, int idx) { - gsl_matrix *M; - gsl_vector *v; - gsl_vector *shifts; - int param; + RefList *n; Reflection *refl; RefListIterator *iter; - RefList *reflections; - double max_shift; - int nref = 0; - int num_params = 0; - enum gparam rv[32]; - double G, B; - - if ( n_filtered != NULL ) *n_filtered = 0; - - rv[num_params++] = GPARAM_ASX; - rv[num_params++] = GPARAM_ASY; - rv[num_params++] = GPARAM_ASZ; - rv[num_params++] = GPARAM_BSX; - rv[num_params++] = GPARAM_BSY; - rv[num_params++] = GPARAM_BSZ; - rv[num_params++] = GPARAM_CSX; - rv[num_params++] = GPARAM_CSY; - rv[num_params++] = GPARAM_CSZ; -// rv[num_params++] = GPARAM_R; -// rv[num_params++] = GPARAM_DIV; - - M = gsl_matrix_calloc(num_params, num_params); - v = gsl_vector_calloc(num_params); - - reflections = crystal_get_reflections(cr); - G = crystal_get_osf(cr); - B = crystal_get_Bfac(cr); - - /* Post-refinement terms */ - for ( refl = first_refl(reflections, &iter); + + n = reflist_new(); + + for ( refl = first_refl(input, &iter); refl != NULL; refl = next_refl(refl, iter) ) { - signed int ha, ka, la; - double I_full, delta_I, esd; - double w; - double I_partial; - int k; - double p, L, s; - double fx; - Reflection *match; - double gradients[num_params]; + signed int h, k, l; + Reflection *rn; - if ( get_flag(refl) ) continue; + get_indices(refl, &h, &k, &l); + get_equiv(amb, NULL, idx, h, k, l, &h, &k, &l); + get_asymm(sym, h, k, l, &h, &k, &l); + rn = add_refl(n, h, k, l); + copy_data(rn, refl); + + get_symmetric_indices(rn, &h, &k, &l); + get_equiv(amb, NULL, idx, h, k, l, &h, &k, &l); + set_symmetric_indices(rn, h, k, l); + } - get_indices(refl, &ha, &ka, &la); - match = find_refl(full, ha, ka, la); - if ( match == NULL ) continue; - I_full = get_intensity(match); + return n; +} - if ( get_redundancy(match) < 2 ) continue; - p = get_partiality(refl); - L = get_lorentz(refl); - I_partial = get_intensity(refl); - esd = get_esd_intensity(refl); - s = resolution(crystal_get_cell(cr), ha, ka, la); +static void reindex_cell(UnitCell *cell, SymOpList *amb, int idx) +{ + signed int h, k, l; + struct rvec na, nb, nc; + struct rvec as, bs, cs; + + cell_get_reciprocal(cell, &as.u, &as.v, &as.w, + &bs.u, &bs.v, &bs.w, + &cs.u, &cs.v, &cs.w); + + get_equiv(amb, NULL, idx, 1, 0, 0, &h, &k, &l); + na.u = as.u*h + bs.u*k + cs.u*l; + na.v = as.v*h + bs.v*k + cs.v*l; + na.w = as.w*h + bs.w*k + cs.w*l; + + get_equiv(amb, NULL, idx, 0, 1, 0, &h, &k, &l); + nb.u = as.u*h + bs.u*k + cs.u*l; + nb.v = as.v*h + bs.v*k + cs.v*l; + nb.w = as.w*h + bs.w*k + cs.w*l; + + get_equiv(amb, NULL, idx, 0, 0, 1, &h, &k, &l); + nc.u = as.u*h + bs.u*k + cs.u*l; + nc.v = as.v*h + bs.v*k + cs.v*l; + nc.w = as.w*h + bs.w*k + cs.w*l; + + cell_set_reciprocal(cell, na.u, na.v, na.w, + nb.u, nb.v, nb.w, + nc.u, nc.v, nc.w); +} - if ( I_partial < 3.0*esd ) continue; - /* Calculate the weight for this reflection */ - w = (s/1e9)*(s/1e9) / (esd*esd); +static void try_reindex(Crystal *crin, const RefList *full, + SymOpList *sym, SymOpList *amb, int scaleflags) +{ + RefList *list; + Crystal *cr; + UnitCell *cell; + double residual_original, residual_flipped; + int idx, n; - /* Calculate all gradients for this reflection */ - for ( k=0; k<num_params; k++ ) { - gradients[k] = gradient(cr, rv[k], refl, pmodel); - gradients[k] *= exp(G)*exp(-B*s*s)*I_full/L; - } + if ( sym == NULL || amb == NULL ) return; - for ( k=0; k<num_params; k++ ) { + if ( scale_one_crystal(crin, full, scaleflags) ) return; + residual_original = residual(crin, full, 0, NULL, NULL); - int g; - double v_c, v_curr; + cr = crystal_copy(crin); - for ( g=0; g<num_params; g++ ) { + n = num_equivs(amb, NULL); - double M_c, M_curr; + for ( idx=0; idx<n; idx++ ) { - /* Matrix is symmetric */ - if ( g > k ) continue; + cell = cell_new_from_cell(crystal_get_cell(crin)); + if ( cell == NULL ) return; + reindex_cell(cell, amb, idx); + crystal_set_cell(cr, cell); - M_c = w * gradients[g] * gradients[k]; + list = reindex_reflections(crystal_get_reflections(crin), + amb, sym, idx); + crystal_set_reflections(cr, list); - M_curr = gsl_matrix_get(M, k, g); - gsl_matrix_set(M, k, g, M_curr + M_c); - gsl_matrix_set(M, g, k, M_curr + M_c); + update_predictions(cr); + calculate_partialities(cr, PMODEL_XSPHERE); + + if ( scale_one_crystal(cr, full, scaleflags) ) return; + residual_flipped = residual(cr, full, 0, NULL, NULL); + + if ( residual_flipped < residual_original ) { + crystal_set_cell(crin, cell); + crystal_set_reflections(crin, list); + residual_original = residual_flipped; + } else { + cell_free(crystal_get_cell(cr)); + reflist_free(crystal_get_reflections(cr)); + } + } - } + crystal_free(cr); +} - fx = exp(G)*p*exp(-B*s*s)*I_full/L; - delta_I = I_partial - fx; - v_c = w * delta_I * gradients[k]; - v_curr = gsl_vector_get(v, k); - gsl_vector_set(v, k, v_curr + v_c); - } +void write_test_logs(Crystal *crystal, const RefList *full, + signed int cycle, int serial) +{ + FILE *fh; + struct image *image = crystal_get_image(crystal); + char tmp[256]; + char ins[5]; - nref++; - } - if ( verbose ) { - //STATUS("The original equation:\n"); - //show_matrix_eqn(M, v); - STATUS("%i reflections went into the equations.\n", nref); - } - - if ( nref < num_params ) { - crystal_set_user_flag(cr, PRFLAG_FEWREFL); - gsl_matrix_free(M); - gsl_vector_free(v); - return 0.0; - } - - max_shift = 0.0; - shifts = solve_svd(v, M, n_filtered, 0); - if ( shifts != NULL ) { - - for ( param=0; param<num_params; param++ ) { - double shift = gsl_vector_get(shifts, param); - if ( verbose ) STATUS("Shift %i: %e\n", param, shift); - if ( isnan(shift) ) { - //ERROR("NaN shift parameter %i (image ser %i), " - // "%i reflections.\n", rv[param], - // crystal_get_image(cr)->serial, - // nref); - } else { - apply_shift(cr, rv[param], shift); - } - if ( fabs(shift) > max_shift ) max_shift = fabs(shift); - } + snprintf(tmp, 256, "pr-logs/parameters-crystal%i.dat", serial); + if ( cycle == 0 ) { + fh = fopen(tmp, "w"); } else { - crystal_set_user_flag(cr, PRFLAG_SOLVEFAIL); + fh = fopen(tmp, "a"); } - gsl_matrix_free(M); - gsl_vector_free(v); - gsl_vector_free(shifts); + if ( fh == NULL ) { + ERROR("Failed to open '%s'\n", tmp); + return; + } + + if ( cycle == 0 ) { + fprintf(fh, "Image: %s %s\n", + image->filename, get_event_string(image->event)); + } + + if ( cycle >= 0 ) { + snprintf(ins, 4, "%i", cycle); + } else { + ins[0] = 'F'; + ins[1] = '\0'; + } - return max_shift; + fprintf(fh, "%s rlp_size = %e\n", ins, crystal_get_profile_radius(crystal)/1e10); + fprintf(fh, "%s mosaicity = %e\n", ins, crystal_get_mosaicity(crystal)); + fprintf(fh, "%s wavelength = %f A\n", ins, image->lambda*1e10); + fprintf(fh, "%s bandwidth = %f\n", ins, image->bw); + fprintf(fh, "%s my scale factor = %e\n", ins, crystal_get_osf(crystal)); + + double asx, asy, asz, bsx, bsy, bsz, csx, csy, csz; + cell_get_reciprocal(crystal_get_cell(crystal), &asx, &asy, &asz, + &bsx, &bsy, &bsz, + &csx, &csy, &csz); + fprintf(fh, "%s %f, %f, %f, %f, %f, %f, %f, %f, %f\n", ins, + asx/1e10, bsx/1e10, csx/1e10, + asy/1e10, bsy/1e10, csy/1e10, + asz/1e10, bsz/1e10, csz/1e10); + fclose(fh); } -double residual(Crystal *cr, const RefList *full, int free, - int *pn_used, const char *filename) +void write_specgraph(Crystal *crystal, const RefList *full, + signed int cycle, int serial) { - double dev = 0.0; - double G, B; + FILE *fh; + char tmp[256]; Reflection *refl; RefListIterator *iter; - FILE *fh = NULL; - int n_used = 0; + double G = crystal_get_osf(crystal); + double B = crystal_get_Bfac(crystal); + UnitCell *cell; + struct image *image = crystal_get_image(crystal); + char ins[5]; - if ( filename != NULL ) { - fh = fopen(filename, "a"); - if ( fh == NULL ) { - ERROR("Failed to open '%s'\n", filename); - } + snprintf(tmp, 256, "pr-logs/specgraph-crystal%i.dat", serial); + + if ( cycle == 0 ) { + fh = fopen(tmp, "w"); + } else { + fh = fopen(tmp, "a"); + } + + if ( fh == NULL ) { + ERROR("Failed to open '%s'\n", tmp); + return; + } + + if ( cycle == 0 ) { + fprintf(fh, "Image: %s %s\n", + image->filename, get_event_string(image->event)); + fprintf(fh, "khalf/m 1/d(m) pcalc pobs iteration h k l\n"); } - G = crystal_get_osf(cr); - B = crystal_get_Bfac(cr); + cell = crystal_get_cell(crystal); - for ( refl = first_refl(crystal_get_reflections(cr), &iter); + if ( cycle >= 0 ) { + snprintf(ins, 4, "%i", cycle); + } else { + ins[0] = 'F'; + ins[1] = '\0'; + } + + for ( refl = first_refl(crystal_get_reflections(crystal), &iter); refl != NULL; refl = next_refl(refl, iter) ) { - double p, L, s, w; + double corr, Ipart, Ifull, pobs, pcalc; + double res; signed int h, k, l; Reflection *match; - double esd, I_full, I_partial; - double fx, dc; - - if ( free && !get_flag(refl) ) continue; get_indices(refl, &h, &k, &l); + res = resolution(cell, h, k, l); + match = find_refl(full, h, k, l); if ( match == NULL ) continue; - I_full = get_intensity(match); - if ( get_redundancy(match) < 2 ) continue; + /* Don't calculate pobs if reference reflection is weak */ + if ( fabs(get_intensity(match)) / get_esd_intensity(match) < 3.0 ) continue; - p = get_partiality(refl); - L = get_lorentz(refl); - I_partial = get_intensity(refl); - esd = get_esd_intensity(refl); - s = resolution(crystal_get_cell(cr), h, k, l); + corr = G * exp(B*res*res) * get_lorentz(refl); + Ipart = get_intensity(refl) * corr; + Ifull = get_intensity(match); + pobs = Ipart / Ifull; + pcalc = get_partiality(refl); - if ( I_partial < 3.0*esd ) continue; + fprintf(fh, "%e %e %f %f %s %4i %4i %4i\n", + get_khalf(refl), 2.0*res, pcalc, pobs, ins, h, k, l); - fx = exp(G)*p*exp(-B*s*s)*I_full/L; - dc = I_partial - fx; - w = (s/1e9)*(s/1e9)/(esd*esd); - dev += w*dc*dc; - n_used++; - - if ( fh != NULL ) { - fprintf(fh, "%4i %4i %4i %e %e\n", - h, k, l, s, dev); - } } - if ( fh != NULL ) fclose(fh); - - if ( pn_used != NULL ) *pn_used = n_used; - return dev; + fclose(fh); } -static void write_residual_graph(Crystal *cr, const RefList *full) +static gsl_multimin_fminimizer *setup_minimiser(Crystal *cr, const RefList *full, + int verbose, int serial, + int scaleflags, + struct rf_priv *priv) { + gsl_multimin_fminimizer *min; + int n_params = 0; int i; - double asx, asy, asz; - double bsx, bsy, bsz; - double csx, csy, csz; - double a; - double step; - double orig_asx; - FILE *fh; - UnitCell *cell; - cell = crystal_get_cell(cr); + /* The parameters to be refined */ + priv->rv[n_params++] = GPARAM_ANG1; + priv->rv[n_params++] = GPARAM_ANG2; + priv->rv[n_params++] = GPARAM_R; + priv->rv[n_params++] = GPARAM_WAVELENGTH; + priv->rv[n_params] = GPARAM_EOL; /* End of list */ + + priv->cr = cr; + priv->full = full; + priv->verbose = verbose; + priv->serial = serial; + priv->scaleflags = scaleflags; + + priv->f.f = residual_f; + priv->f.n = n_params; + priv->f.params = priv; + + priv->initial = gsl_vector_alloc(n_params); + priv->vals = gsl_vector_alloc(n_params); + priv->step = gsl_vector_alloc(n_params); + + for ( i=0; i<n_params; i++ ) { + gsl_vector_set(priv->initial, i, get_initial_param(cr, priv->rv[i])); + gsl_vector_set(priv->vals, i, 0.0); + gsl_vector_set(priv->step, i, 1.0); + } - fh = fopen("residual-plot.dat", "w"); + min = gsl_multimin_fminimizer_alloc(gsl_multimin_fminimizer_nmsimplex2, + n_params); + gsl_multimin_fminimizer_set(min, &priv->f, priv->vals, priv->step); - cell_get_reciprocal(cell, &asx, &asy, &asz, - &bsx, &bsy, &bsz, - &csx, &csy, &csz); - a = modulus(asx, asy, asz); - orig_asx = asx; + return min; +} - step = a/100.0; - for ( i=-50; i<=50; i++ ) { +static void write_grid(Crystal *cr, const RefList *full, + signed int cycle, int serial, int scaleflags, + const enum gparam par1, const enum gparam par2, + const char *name) +{ + FILE *fh; + char fn[64]; + char ins[5]; + gsl_multimin_fminimizer *min; + struct rf_priv priv; + int idx1, idx2; + int i; - double res; - int n; - asx = orig_asx + (i*step); - cell_set_reciprocal(cell, asx, asy, asz, - bsx, bsy, bsz, - csx, csy, csz); - update_predictions(cr); - calculate_partialities(cr, PMODEL_SCSPHERE); - res = residual(cr, full, 0, &n, NULL); - fprintf(fh, "%i %e %e %i\n", i, asx, res, n); + min = setup_minimiser(cr, full, 0, serial, scaleflags, &priv); + + idx1 = 99; + idx2 = 99; + for ( i=0; i<priv.f.n; i++ ) { + if ( priv.rv[i] == par1 ) idx1 = i; + if ( priv.rv[i] == par2 ) idx2 = i; } + assert(idx1 != 99); + assert(idx2 != 99); - cell_set_reciprocal(cell, orig_asx, asy, asz, - bsx, bsy, bsz, - csx, csy, csz); - update_predictions(cr); - calculate_partialities(cr, PMODEL_SCSPHERE); + if ( cycle >= 0 ) { + snprintf(ins, 4, "%i", cycle); + } else { + ins[0] = 'F'; + ins[1] = '\0'; + } + + snprintf(fn, 63, "pr-logs/grid-crystal%i-cycle%s-%s.dat", + serial, ins, name); + fh = fopen(fn, "w"); + if ( fh != NULL ) { + double v1, v2; + fprintf(fh, "%e %e %e %s\n", + -5.0*get_scale(par1)+get_initial_param(cr, par1), + 5.0*get_scale(par1)+get_initial_param(cr, par1), + get_initial_param(cr, par1), + get_label(par1)); + fprintf(fh, "%e %e %e %s\n", + -5.0*get_scale(par2)+get_initial_param(cr, par2), + 5.0*get_scale(par2)+get_initial_param(cr, par2), + get_initial_param(cr, par2), + get_label(par2)); + for ( v2=-5.0; v2<=5.0; v2+=0.25 ) { + int first=1; + for ( v1=-5.0; v1<=5.0; v1+=0.25 ) { + double res; + gsl_vector_set(min->x, idx1, v1); + gsl_vector_set(min->x, idx2, v2); + res = residual_f(min->x, &priv); + if ( !first ) fprintf(fh, " "); + first = 0; + fprintf(fh, "%e", res); + } + fprintf(fh, "\n"); + } + } fclose(fh); + + gsl_multimin_fminimizer_free(min); + gsl_vector_free(priv.initial); + gsl_vector_free(priv.vals); + gsl_vector_free(priv.step); +} + + +void write_gridscan(Crystal *cr, const RefList *full, + signed int cycle, int serial, int scaleflags) +{ + write_grid(cr, full, cycle, serial, scaleflags, + GPARAM_ANG1, GPARAM_ANG2, "ang1-ang2"); + write_grid(cr, full, cycle, serial, scaleflags, + GPARAM_ANG1, GPARAM_WAVELENGTH, "ang1-wave"); + write_grid(cr, full, cycle, serial, scaleflags, + GPARAM_R, GPARAM_WAVELENGTH, "R-wave"); } static void do_pr_refine(Crystal *cr, const RefList *full, - PartialityModel pmodel, int verbose) + PartialityModel pmodel, int verbose, int serial, + int cycle, int write_logs, + SymOpList *sym, SymOpList *amb, int scaleflags) { - int i, done; - double old_dev; - UnitCell *cell = crystal_get_cell(cr); + gsl_multimin_fminimizer *min; + struct rf_priv priv; + int n_iter = 0; + int status; + double residual_start, residual_free_start; + FILE *fh = NULL; + + try_reindex(cr, full, sym, amb, scaleflags); + + if ( scale_one_crystal(cr, full, scaleflags | SCALE_VERBOSE_ERRORS) ) { + ERROR("Bad scaling at start of refinement.\n"); + return; + } + residual_start = residual(cr, full, 0, NULL, NULL); + residual_free_start = residual(cr, full, 1, NULL, NULL); + + if ( verbose ) { + STATUS("\nPR initial: dev = %10.5e, free dev = %10.5e\n", + residual_start, residual_free_start); + } - old_dev = residual(cr, full, 0, 0, NULL); + min = setup_minimiser(cr, full, verbose, serial, scaleflags, &priv); if ( verbose ) { - double asx, asy, asz; - double bsx, bsy, bsz; - double csx, csy, csz; - cell_get_reciprocal(cell, &asx, &asy, &asz, - &bsx, &bsy, &bsz, - &csx, &csy, &csz); - STATUS("Initial asx = %e\n", asx); - STATUS("PR initial dev = %10.5e, free dev = %10.5e\n", - old_dev, residual(cr, full, 1, NULL, NULL)); - } - - i = 0; - done = 0; - do { + double res = residual_f(min->x, &priv); + double size = gsl_multimin_fminimizer_size(min); + STATUS("At start: %f %f %f %f ----> %f %f %e %f residual = %e size %f\n", + gsl_vector_get(min->x, 0), + gsl_vector_get(min->x, 1), + gsl_vector_get(min->x, 2), + gsl_vector_get(min->x, 3), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)), + get_actual_val(min->x, priv.initial, priv.rv, 2), + get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10, + res, size); + } - double dev; + if ( write_logs ) { - pr_iterate(cr, full, pmodel, NULL, verbose); + char fn[64]; - update_predictions(cr); - calculate_partialities(cr, pmodel); + snprintf(fn, 63, "pr-logs/crystal%i-cycle%i.log", serial, cycle); + fh = fopen(fn, "w"); + if ( fh != NULL ) { + fprintf(fh, "iteration RtoReference CCtoReference nref " + "ang1 ang2 radius wavelength"); + double res = residual_f(min->x, &priv); + fprintf(fh, "%5i %10.8f %10.8f %5i %10.8f %10.8f %e %e\n", + n_iter, res, 0.0, 0, + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)), + get_actual_val(min->x, priv.initial, priv.rv, 2), + get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10); + } - dev = residual(cr, full, 0, 0, NULL); - if ( fabs(dev - old_dev) < dev*0.0001 ) done = 1; + } + + do { + double res; + + n_iter++; + + status = gsl_multimin_fminimizer_iterate(min); + if ( status ) break; + + res = residual_f(min->x, &priv); + if ( isnan(res) ) { + status = GSL_ENOPROG; + break; + } if ( verbose ) { - STATUS("PR iter %2i: dev = %10.5e, free dev = %10.5e\n", - i+1, dev, residual(cr, full, 1, NULL, NULL)); + double res = residual_f(min->x, &priv); + double size = gsl_multimin_fminimizer_size(min); + STATUS("%f %f %f %f ----> %f %f %e %f residual = %e size %f\n", + gsl_vector_get(min->x, 0), + gsl_vector_get(min->x, 1), + gsl_vector_get(min->x, 2), + gsl_vector_get(min->x, 3), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)), + get_actual_val(min->x, priv.initial, priv.rv, 2), + get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10, + res, size); } - i++; - old_dev = dev; + if ( fh != NULL ) { + fprintf(fh, "%5i %10.8f %10.8f %5i %10.8f %10.8f %e %e\n", + n_iter, res, 0.0, 0, + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)), + get_actual_val(min->x, priv.initial, priv.rv, 2), + get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10); + } + + status = gsl_multimin_test_size(min->size, 0.005); - } while ( i < 30 && !done ); + } while ( status == GSL_CONTINUE && n_iter < 1000 ); if ( verbose ) { - double asx, asy, asz; - double bsx, bsy, bsz; - double csx, csy, csz; - cell_get_reciprocal(cell, &asx, &asy, &asz, - &bsx, &bsy, &bsz, - &csx, &csy, &csz); - STATUS("Final asx = %e\n", asx); + STATUS("Done with refinement after %i iter\n", n_iter); + STATUS("status = %i (%s)\n", status, gsl_strerror(status)); } -} + if ( status == GSL_SUCCESS ) { -static struct prdata pr_refine(Crystal *cr, const RefList *full, - PartialityModel pmodel) -{ - int verbose = 0; - struct prdata prdata; + if ( check_angle_shifts(min->x, priv.initial, priv.rv, &priv) ) return; - prdata.refined = 0; - prdata.n_filtered = 0; + if ( verbose ) { - if ( verbose ) { - write_residual_graph(cr, full); + double res = residual_f(min->x, &priv); + double size = gsl_multimin_fminimizer_size(min); + STATUS("At end: %f %f %f %f ----> %f %f %e %f residual = %e size %f\n", + gsl_vector_get(min->x, 0), + gsl_vector_get(min->x, 1), + gsl_vector_get(min->x, 2), + gsl_vector_get(min->x, 3), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)), + get_actual_val(min->x, priv.initial, priv.rv, 2), + get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10, + res, size); + + } + + if ( fh != NULL ) { + double res = residual_f(min->x, &priv); + fprintf(fh, "%5i %10.8f %10.8f %5i %10.8f %10.8f %e %e\n", + n_iter, res, 0.0, 0, + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)), + rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)), + get_actual_val(min->x, priv.initial, priv.rv, 2), + get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10); + fclose(fh); + } + + /* Apply the final shifts */ + apply_parameters(min->x, priv.initial, priv.rv, cr); + update_predictions(cr); + calculate_partialities(cr, PMODEL_XSPHERE); + scale_one_crystal(cr, full, scaleflags); + + if ( verbose ) { + + STATUS("After applying final shifts:\n"); + STATUS("PR final: dev = %10.5e, free dev = %10.5e\n", + residual(cr, full, 0, NULL, NULL), + residual(cr, full, 1, NULL, NULL)); + STATUS("Final R = %e m^-1\n", crystal_get_profile_radius(cr)); + + } + + } else { + ERROR("Bad refinement: crystal %i\n", serial); } - do_pr_refine(cr, full, pmodel, verbose); + if ( write_logs ) { + write_gridscan(cr, full, cycle, serial, scaleflags); + write_specgraph(cr, full, cycle, serial); + write_test_logs(cr, full, cycle, serial); + } - if ( crystal_get_user_flag(cr) == 0 ) { - prdata.refined = 1; + if ( crystal_get_profile_radius(cr) > 5e9 ) { + ERROR("Very large radius: crystal %i\n", serial); } - return prdata; + gsl_multimin_fminimizer_free(min); + gsl_vector_free(priv.initial); + gsl_vector_free(priv.vals); + gsl_vector_free(priv.step); } @@ -706,7 +897,14 @@ struct refine_args RefList *full; Crystal *crystal; PartialityModel pmodel; + int serial; struct prdata prdata; + int verbose; + int cycle; + int no_logs; + SymOpList *sym; + SymOpList *amb; + int scaleflags; }; @@ -724,8 +922,18 @@ static void refine_image(void *task, int id) { struct refine_args *pargs = task; Crystal *cr = pargs->crystal; + int write_logs = 0; + + write_logs = !pargs->no_logs && (pargs->serial % 20 == 0); + pargs->prdata.refined = 0; + + do_pr_refine(cr, pargs->full, pargs->pmodel, pargs->verbose, + pargs->serial, pargs->cycle, write_logs, + pargs->sym, pargs->amb, pargs->scaleflags); - pargs->prdata = pr_refine(cr, pargs->full, pargs->pmodel); + if ( crystal_get_user_flag(cr) == 0 ) { + pargs->prdata.refined = 1; + } } @@ -738,6 +946,7 @@ static void *get_image(void *vqargs) memcpy(task, &qargs->task_defaults, sizeof(struct refine_args)); task->crystal = qargs->crystals[qargs->n_started]; + task->serial = qargs->n_started; qargs->n_started++; @@ -757,7 +966,9 @@ static void done_image(void *vqargs, void *task) void refine_all(Crystal **crystals, int n_crystals, - RefList *full, int nthreads, PartialityModel pmodel) + RefList *full, int nthreads, PartialityModel pmodel, + int verbose, int cycle, int no_logs, + SymOpList *sym, SymOpList *amb, int scaleflags) { struct refine_args task_defaults; struct queue_args qargs; @@ -766,7 +977,12 @@ void refine_all(Crystal **crystals, int n_crystals, task_defaults.crystal = NULL; task_defaults.pmodel = pmodel; task_defaults.prdata.refined = 0; - task_defaults.prdata.n_filtered = 0; + task_defaults.verbose = verbose; + task_defaults.cycle = cycle; + task_defaults.no_logs = no_logs; + task_defaults.sym = sym; + task_defaults.amb = amb; + task_defaults.scaleflags = scaleflags; qargs.task_defaults = task_defaults; qargs.n_started = 0; diff --git a/src/post-refinement.h b/src/post-refinement.h index 7f395013..71a6d7f3 100644 --- a/src/post-refinement.h +++ b/src/post-refinement.h @@ -3,11 +3,11 @@ * * Post refinement * - * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2018 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -41,6 +41,7 @@ #include "utils.h" #include "crystal.h" #include "geometry.h" +#include "symmetry.h" enum prflag @@ -51,19 +52,28 @@ enum prflag PRFLAG_EARLY = 18, PRFLAG_CC = 19, PRFLAG_BIGB = 20, + PRFLAG_SCALEBAD = 21, }; extern const char *str_prflag(enum prflag flag); extern void refine_all(Crystal **crystals, int n_crystals, - RefList *full, int nthreads, PartialityModel pmodel); + RefList *full, int nthreads, PartialityModel pmodel, + int verbose, int cycle, int no_logs, + SymOpList *sym, SymOpList *amb, int scaleflags); + +extern void write_gridscan(Crystal *cr, const RefList *full, + int cycle, int serial, int scaleflags); + +extern void write_specgraph(Crystal *crystal, const RefList *full, + signed int cycle, int serial); /* Exported so it can be poked by tests/pr_p_gradient_check */ extern double gradient(Crystal *cr, int k, Reflection *refl, PartialityModel pmodel); -extern double residual(Crystal *cr, const RefList *full, int free, - int *pn_used, const char *filename); +extern void write_test_logs(Crystal *crystal, const RefList *full, + signed int cycle, int serial); #endif /* POST_REFINEMENT_H */ diff --git a/src/process_hkl.c b/src/process_hkl.c index 5930c189..ab5b9af1 100644 --- a/src/process_hkl.c +++ b/src/process_hkl.c @@ -519,6 +519,7 @@ int main(int argc, char *argv[]) double push_res = +INFINITY; double min_cc = -INFINITY; int twopass = 0; + char *audit_info; /* Long options */ const struct option longopts[] = { @@ -822,11 +823,14 @@ int main(int argc, char *argv[]) hist_nbins); } + audit_info = stream_audit_info(st); + close_stream(st); + reflist_add_command_and_version(model, argc, argv); + reflist_add_notes(model, "Audit information from stream:"); + reflist_add_notes(model, audit_info); write_reflist_2(output, model, sym); - close_stream(st); - free_symoplist(sym); reflist_free(model); free(output); diff --git a/src/process_image.c b/src/process_image.c index 87089289..4b02e694 100644 --- a/src/process_image.c +++ b/src/process_image.c @@ -301,7 +301,7 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, /* Integrate! */ time_accounts_set(taccs, TACC_INTEGRATION); sb_shared->pings[cookie]++; - integrate_all_5(&image, iargs->int_meth, PMODEL_SCSPHERE, + integrate_all_5(&image, iargs->int_meth, PMODEL_XSPHERE, iargs->push_res, iargs->ir_inn, iargs->ir_mid, iargs->ir_out, iargs->int_diag, iargs->int_diag_h, diff --git a/src/rejection.c b/src/rejection.c index 10f13f9c..9d41f9b4 100644 --- a/src/rejection.c +++ b/src/rejection.c @@ -137,7 +137,7 @@ static void check_cc(Crystal *cr, RefList *full) pcalc = get_partiality(refl); /* Observed partiality */ - corr = exp(-G) * exp(B*res*res) * get_lorentz(refl); + corr = G * exp(B*res*res) * get_lorentz(refl); Ipart = get_intensity(refl) * corr; pobs = Ipart / get_intensity(match); diff --git a/src/scaling.c b/src/scaling.c index 77a64384..cabc5952 100644 --- a/src/scaling.c +++ b/src/scaling.c @@ -3,11 +3,11 @@ * * Scaling * - * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2017 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2017 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -38,290 +38,22 @@ #include <gsl/gsl_linalg.h> #include <gsl/gsl_eigen.h> #include <gsl/gsl_blas.h> +#include <gsl/gsl_fit.h> +#include <gsl/gsl_statistics_double.h> #include "merge.h" #include "post-refinement.h" #include "symmetry.h" #include "cell.h" #include "cell-utils.h" - - -/* Maximum number of iterations of NLSq to do for each image per macrocycle. */ -#define MAX_CYCLES (10) - - -/* Apply the given shift to the 'k'th parameter of 'image'. */ -static void apply_shift(Crystal *cr, int k, double shift) -{ - double t; - - switch ( k ) { - - case GPARAM_BFAC : - t = crystal_get_Bfac(cr); - t += shift; - crystal_set_Bfac(cr, t); - break; - - case GPARAM_OSF : - t = crystal_get_osf(cr); - t += shift; - crystal_set_osf(cr, t); - break; - - default : - ERROR("No shift defined for parameter %i\n", k); - abort(); - - } -} - - -/* Perform one cycle of scaling of 'cr' against 'full' */ -static double scale_iterate(Crystal *cr, const RefList *full, - PartialityModel pmodel, int *nr) -{ - gsl_matrix *M; - gsl_vector *v; - gsl_vector *shifts; - int param; - Reflection *refl; - RefListIterator *iter; - RefList *reflections; - double max_shift; - int nref = 0; - int num_params = 0; - enum gparam rv[32]; - double G, B; - - *nr = 0; - - rv[num_params++] = GPARAM_OSF; - rv[num_params++] = GPARAM_BFAC; - - M = gsl_matrix_calloc(num_params, num_params); - v = gsl_vector_calloc(num_params); - - reflections = crystal_get_reflections(cr); - G = crystal_get_osf(cr); - B = crystal_get_Bfac(cr); - - /* Scaling terms */ - for ( refl = first_refl(reflections, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - signed int ha, ka, la; - double I_full, delta_I, esd; - double w; - double I_partial; - int k; - double p, L, s; - double fx; - Reflection *match; - double gradients[num_params]; - - /* If reflection is free-flagged, don't use it here */ - if ( get_flag(refl) ) continue; - - /* Find the full version */ - get_indices(refl, &ha, &ka, &la); - match = find_refl(full, ha, ka, la); - if ( match == NULL ) continue; - - /* Merged intensitty */ - I_full = get_intensity(match); - - /* Actual measurement of this reflection from this pattern */ - I_partial = get_intensity(refl); - esd = get_esd_intensity(refl); - p = get_partiality(refl); - - /* Scale only using strong reflections */ - if ( I_partial <= 3.0*esd ) continue; /* Also because of log */ - if ( get_redundancy(match) < 2 ) continue; - if ( I_full <= 0 ) continue; /* Because log */ - if ( p <= 0.0 ) continue; /* Because of log */ - - L = get_lorentz(refl); - s = resolution(crystal_get_cell(cr), ha, ka, la); - - /* Calculate the weight for this reflection */ - w = 1.0; - - /* Calculate all gradients for this reflection */ - for ( k=0; k<num_params; k++ ) { - - if ( rv[k] == GPARAM_OSF ) { - gradients[k] = 1.0; - } else if ( rv[k] == GPARAM_BFAC ) { - gradients[k] = -s*s; - } else { - ERROR("Unrecognised scaling gradient.\n"); - abort(); - } - } - - for ( k=0; k<num_params; k++ ) { - - int g; - double v_c, v_curr; - - for ( g=0; g<num_params; g++ ) { - - double M_c, M_curr; - - /* Matrix is symmetric */ - if ( g > k ) continue; - - M_c = w * gradients[g] * gradients[k]; - - M_curr = gsl_matrix_get(M, k, g); - gsl_matrix_set(M, k, g, M_curr + M_c); - gsl_matrix_set(M, g, k, M_curr + M_c); - - } - - fx = G + log(p) - log(L) - B*s*s + log(I_full); - delta_I = log(I_partial) - fx; - v_c = w * delta_I * gradients[k]; - v_curr = gsl_vector_get(v, k); - gsl_vector_set(v, k, v_curr + v_c); - - } - - nref++; - } - - *nr = nref; - - if ( nref < num_params ) { - crystal_set_user_flag(cr, PRFLAG_FEWREFL); - gsl_matrix_free(M); - gsl_vector_free(v); - return 0.0; - } - - max_shift = 0.0; - shifts = solve_svd(v, M, NULL, 0); - if ( shifts != NULL ) { - - for ( param=0; param<num_params; param++ ) { - double shift = gsl_vector_get(shifts, param); - apply_shift(cr, rv[param], shift); - if ( fabs(shift) > max_shift ) max_shift = fabs(shift); - } - - } else { - crystal_set_user_flag(cr, PRFLAG_SOLVEFAIL); - } - - gsl_matrix_free(M); - gsl_vector_free(v); - gsl_vector_free(shifts); - - return max_shift; -} - - -double log_residual(Crystal *cr, const RefList *full, int free, - int *pn_used, const char *filename) -{ - double dev = 0.0; - double G, B; - Reflection *refl; - RefListIterator *iter; - int n_used = 0; - FILE *fh = NULL; - - G = crystal_get_osf(cr); - B = crystal_get_Bfac(cr); - if ( filename != NULL ) { - fh = fopen(filename, "a"); - if ( fh == NULL ) { - ERROR("Failed to open '%s'\n", filename); - } - } - - for ( refl = first_refl(crystal_get_reflections(cr), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double p, L, s, w; - signed int h, k, l; - Reflection *match; - double esd, I_full, I_partial; - double fx, dc; - - if ( free && !get_flag(refl) ) continue; - - get_indices(refl, &h, &k, &l); - match = find_refl(full, h, k, l); - if ( match == NULL ) continue; - - p = get_partiality(refl); - L = get_lorentz(refl); - I_partial = get_intensity(refl); - I_full = get_intensity(match); - esd = get_esd_intensity(refl); - s = resolution(crystal_get_cell(cr), h, k, l); - - if ( I_partial <= 3.0*esd ) continue; /* Also because of log */ - if ( get_redundancy(match) < 2 ) continue; - if ( I_full <= 0 ) continue; /* Because log */ - if ( p <= 0.0 ) continue; /* Because of log */ - - fx = G + log(p) - log(L) - B*s*s + log(I_full); - dc = log(I_partial) - fx; - w = 1.0; - dev += w*dc*dc; - - if ( fh != NULL ) { - fprintf(fh, "%4i %4i %4i %e %e\n", - h, k, l, s, dev); - } - - } - - if ( fh != NULL ) fclose(fh); - - if ( pn_used != NULL ) *pn_used = n_used; - return dev; -} - - -static void do_scale_refine(Crystal *cr, const RefList *full, - PartialityModel pmodel, int *nr) -{ - int i, done; - double old_dev; - - old_dev = log_residual(cr, full, 0, NULL, NULL); - - i = 0; - done = 0; - do { - - double dev; - - scale_iterate(cr, full, pmodel, nr); - - dev = log_residual(cr, full, 0, 0, NULL); - if ( fabs(dev - old_dev) < dev*0.01 ) done = 1; - - i++; - old_dev = dev; - - } while ( i < MAX_CYCLES && !done ); -} +#include "scaling.h" struct scale_args { RefList *full; Crystal *crystal; - PartialityModel pmodel; - int n_reflections; + int flags; }; @@ -331,7 +63,6 @@ struct queue_args int n_done; Crystal **crystals; int n_crystals; - long long int n_reflections; struct scale_args task_defaults; }; @@ -339,8 +70,7 @@ struct queue_args static void scale_crystal(void *task, int id) { struct scale_args *pargs = task; - do_scale_refine(pargs->crystal, pargs->full, pargs->pmodel, - &pargs->n_reflections); + scale_one_crystal(pargs->crystal, pargs->full, pargs->flags); } @@ -363,11 +93,7 @@ static void *get_crystal(void *vqargs) static void done_crystal(void *vqargs, void *task) { struct queue_args *qa = vqargs; - struct scale_args *wargs = task; - qa->n_done++; - qa->n_reflections += wargs->n_reflections; - progress_bar(qa->n_done, qa->n_crystals, "Scaling"); free(task); } @@ -381,14 +107,12 @@ static double total_log_r(Crystal **crystals, int n_crystals, RefList *full, int n = 0; for ( i=0; i<n_crystals; i++ ) { - double r; if ( crystal_get_user_flag(crystals[i]) ) continue; r = log_residual(crystals[i], full, 0, NULL, NULL); if ( isnan(r) ) continue; total += r; n++; - } if ( ninc != NULL ) *ninc = n; @@ -397,8 +121,7 @@ static double total_log_r(Crystal **crystals, int n_crystals, RefList *full, /* Perform iterative scaling, all the way to convergence */ -void scale_all(Crystal **crystals, int n_crystals, int nthreads, - PartialityModel pmodel) +void scale_all(Crystal **crystals, int n_crystals, int nthreads, int scaleflags) { struct scale_args task_defaults; struct queue_args qargs; @@ -406,7 +129,7 @@ void scale_all(Crystal **crystals, int n_crystals, int nthreads, int niter = 0; task_defaults.crystal = NULL; - task_defaults.pmodel = pmodel; + task_defaults.flags = scaleflags; qargs.task_defaults = task_defaults; qargs.n_crystals = n_crystals; @@ -422,18 +145,15 @@ void scale_all(Crystal **crystals, int n_crystals, int nthreads, double bef_res; full = merge_intensities(crystals, n_crystals, nthreads, - pmodel, 2, INFINITY, 0); + 2, INFINITY, 0); old_res = new_res; bef_res = total_log_r(crystals, n_crystals, full, NULL); qargs.task_defaults.full = full; qargs.n_started = 0; qargs.n_done = 0; - qargs.n_reflections = 0; run_threads(nthreads, scale_crystal, get_crystal, done_crystal, &qargs, n_crystals, 0, 0, 0); - STATUS("%lli reflections went into the scaling.\n", - qargs.n_reflections); new_res = total_log_r(crystals, n_crystals, full, &ninc); STATUS("Log residual went from %e to %e, %i crystals\n", @@ -456,3 +176,164 @@ void scale_all(Crystal **crystals, int n_crystals, int nthreads, ERROR("Too many iterations - giving up!\n"); } } + + +/* Calculates G and B, by which cr's reflections should be multiplied to fit reference */ +int scale_one_crystal(Crystal *cr, const RefList *listR, int flags) +{ + const Reflection *reflS; + RefListIterator *iter; + int max_n = 256; + int n = 0; + double *x; + double *y; + double *w; + int r; + double cov00, cov01, cov11, chisq; + int n_esdS = 0; + int n_esdR = 0; + int n_ihS = 0; + int n_ihR = 0; + int n_nanS = 0; + int n_nanR = 0; + int n_infS = 0; + int n_infR = 0; + int n_part = 0; + int n_nom = 0; + int n_red = 0; + RefList *listS = crystal_get_reflections(cr); + UnitCell *cell = crystal_get_cell(cr); + double G, B; + + assert(cell != NULL); + assert(listR != NULL); + assert(listS != NULL); + + x = malloc(max_n*sizeof(double)); + w = malloc(max_n*sizeof(double)); + y = malloc(max_n*sizeof(double)); + if ( (x==NULL) || (y==NULL) || (w==NULL) ) { + ERROR("Failed to allocate memory for scaling.\n"); + return 1; + } + + int nb = 0; + for ( reflS = first_refl_const(listS, &iter); + reflS != NULL; + reflS = next_refl_const(reflS, iter) ) + { + signed int h, k, l; + const Reflection *reflR; + double IhR, IhS, esdS, pS, LS; + double s; + + nb++; + + get_indices(reflS, &h, &k, &l); + reflR = find_refl(listR, h, k, l); + if ( reflR == NULL ) { + n_nom++; + continue; + } + + s = resolution(cell, h, k, l); + + IhR = get_intensity(reflR); + IhS = get_intensity(reflS); + esdS = get_esd_intensity(reflS); + pS = get_partiality(reflS); + LS = get_lorentz(reflS); + + /* Problem cases in approximate descending order of severity */ + if ( isnan(IhR) ) { n_nanR++; continue; } + if ( isinf(IhR) ) { n_infR++; continue; } + if ( isnan(IhS) ) { n_nanS++; continue; } + if ( isinf(IhS) ) { n_infS++; continue; } + if ( pS < 0.3 ) { n_part++; continue; } + if ( IhS <= 0.0 ) { n_ihS++; continue; } + if ( IhS <= 3.0*esdS ) { n_esdS++; continue; } + if ( IhR <= 0.0 ) { n_ihR++; continue; } + if ( get_redundancy(reflR) < 2 ) { n_red++; continue; } + + if ( n == max_n ) { + max_n *= 2; + x = realloc(x, max_n*sizeof(double)); + y = realloc(y, max_n*sizeof(double)); + w = realloc(w, max_n*sizeof(double)); + if ( (x==NULL) || (y==NULL) || (w==NULL) ) { + ERROR("Failed to allocate memory for scaling.\n"); + return 1; + } + } + + x[n] = s*s; + y[n] = log(LS) + log(IhS) -log(pS) - log(IhR); + w[n] = pS; + n++; + + } + + if ( n < 2 ) { + if ( flags & SCALE_VERBOSE_ERRORS ) { + ERROR("Not enough reflections for scaling (had %i, but %i remain)\n", nb, n); + if ( n_esdR ) ERROR("%i reference reflection esd\n", n_esdR); + if ( n_esdS ) ERROR("%i subject reflection esd\n", n_esdS); + if ( n_ihR ) ERROR("%i reference reflection intensity\n", n_ihR); + if ( n_red ) ERROR("%i reference reflection redundancy\n", n_red); + if ( n_ihS ) ERROR("%i subject reflection intensity\n", n_ihS); + if ( n_nanR ) ERROR("%i reference reflection nan\n", n_nanR); + if ( n_nanS ) ERROR("%i subject reflection nan\n", n_nanS); + if ( n_infR ) ERROR("%i reference reflection inf\n", n_infR); + if ( n_infS ) ERROR("%i subject reflection inf\n", n_infS); + if ( n_part ) ERROR("%i subject reflection partiality\n", n_part); + if ( n_nom ) ERROR("%i no match in reference list\n", n_nom); + } + free(x); + free(y); + free(w); + return 1; + } + + if ( flags & SCALE_NO_B ) { + G = gsl_stats_wmean(w, 1, y, 1, n); + B = 0.0; + r = 0; + } else { + r = gsl_fit_wlinear(x, 1, w, 1, y, 1, n, &G, &B, &cov00, &cov01, &cov11, &chisq); + } + + if ( r ) { + ERROR("Scaling failed.\n"); + free(x); + free(y); + free(w); + return 1; + } + + if ( isnan(G) ) { + + if ( flags & SCALE_VERBOSE_ERRORS ) { + ERROR("Scaling gave NaN (%i pairs)\n", n); + if ( n < 10 ) { + int i; + for ( i=0; i<n; i++ ) { + STATUS("%3i %e %e %e\n", i, x[i], y[i], w[i]); + } + } + } + + free(x); + free(y); + free(w); + return 1; + } + + crystal_set_osf(cr, exp(G)); + crystal_set_Bfac(cr, -B); + + free(x); + free(y); + free(w); + + return 0; +} diff --git a/src/scaling.h b/src/scaling.h index 534043d9..8c2e7ef2 100644 --- a/src/scaling.h +++ b/src/scaling.h @@ -3,11 +3,11 @@ * * Scaling * - * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2018 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -38,10 +38,16 @@ #include "crystal.h" #include "geometry.h" -extern double log_residual(Crystal *cr, const RefList *full, int free, - int *pn_used, const char *filename); +enum ScaleFlags +{ + SCALE_NONE = 0, + SCALE_NO_B = 1<<0, /* Don't use Debye-Waller part */ + SCALE_VERBOSE_ERRORS = 1<<1, +}; + +extern int scale_one_crystal(Crystal *cr, const RefList *reference, int flags); extern void scale_all(Crystal **crystals, int n_crystals, int nthreads, - PartialityModel pmodel); + int flags); #endif /* SCALING_H */ |