/* * merge.c * * Parallel weighted merging of intensities * * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2010-2020 Thomas White * * This file is part of CrystFEL. * * CrystFEL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CrystFEL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CrystFEL. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "image.h" #include "peaks.h" #include "symmetry.h" #include "geometry.h" #include "cell.h" #include "utils.h" #include "reflist.h" #include "reflist-utils.h" #include "cell-utils.h" #include "merge.h" struct merge_queue_args { RefList *full; pthread_rwlock_t full_lock; struct crystal_refls *crystals; int n_started; double push_res; int use_weak; long long int n_reflections; int ln_merge; }; struct merge_worker_args { struct merge_queue_args *qargs; Crystal *crystal; RefList *refls; int crystal_number; int n_reflections; }; static void *create_merge_job(void *vqargs) { struct merge_worker_args *wargs; struct merge_queue_args *qargs = vqargs; wargs = malloc(sizeof(struct merge_worker_args)); wargs->qargs = qargs; wargs->crystal_number = qargs->n_started; wargs->crystal = qargs->crystals[qargs->n_started].cr; wargs->refls = qargs->crystals[qargs->n_started].refls; qargs->n_started++; return wargs; } static int alloc_contribs(struct reflection_contributions *c) { c->contribs = realloc(c->contribs, c->max_contrib*sizeof(Reflection *)); c->contrib_crystals = realloc(c->contrib_crystals, c->max_contrib*sizeof(Crystal *)); if ( c->contribs == NULL ) return 1; if ( c->contrib_crystals == NULL ) return 1; return 0; } /* Find reflection hkl in 'list', creating it if it's not there, under * protection of 'lock' and returning a locked reflection */ static Reflection *get_locked_reflection(RefList *list, pthread_rwlock_t *lock, signed int h, signed int k, signed int l) { Reflection *f; pthread_rwlock_rdlock(lock); f = find_refl(list, h, k, l); if ( f == NULL ) { /* Swap read lock for write lock */ pthread_rwlock_unlock(lock); pthread_rwlock_wrlock(lock); /* In the gap between the unlock and the wrlock, the * reflection might have been created by another thread. * So, we must check again */ f = find_refl(list, h, k, l); if ( f == NULL ) { struct reflection_contributions *c; f = add_refl(list, h, k, l); lock_reflection(f); pthread_rwlock_unlock(lock); set_intensity(f, 0.0); set_temp1(f, 0.0); set_temp2(f, 0.0); c = malloc(sizeof(struct reflection_contributions)); if ( c != NULL ) { c->n_contrib = 0; c->max_contrib = 32; c->contribs = NULL; c->contrib_crystals = NULL; if ( alloc_contribs(c) ) { set_contributions(f, NULL); } else { set_contributions(f, c); } } else { set_contributions(f, NULL); } } else { /* Someone else created it */ lock_reflection(f); pthread_rwlock_unlock(lock); } } else { lock_reflection(f); pthread_rwlock_unlock(lock); } return f; } static void run_merge_job(void *vwargs, int cookie) { struct merge_worker_args *wargs = vwargs; Crystal *cr = wargs->crystal; RefList *full = wargs->qargs->full; double push_res = wargs->qargs->push_res; int ln_merge = wargs->qargs->ln_merge; Reflection *refl; RefListIterator *iter; double G, B; wargs->n_reflections = 0; /* If this crystal's scaling was dodgy, it doesn't contribute to the * merged intensities */ if ( crystal_get_user_flag(cr) != 0 ) return; G = crystal_get_osf(cr); B = crystal_get_Bfac(cr); for ( refl = first_refl(wargs->refls, &iter); refl != NULL; refl = next_refl(refl, iter) ) { Reflection *f; signed int h, k, l; double mean, sumweight, M2, temp, delta, R; double res, w; struct reflection_contributions *c; if ( get_partiality(refl) < MIN_PART_MERGE ) continue; if ( isnan(get_esd_intensity(refl)) ) continue; if ( !wargs->qargs->use_weak || ln_merge ) { if (get_intensity(refl) < 3.0*fabs(get_esd_intensity(refl))) { continue; } if ( get_flag(refl) ) continue; } get_indices(refl, &h, &k, &l); f = get_locked_reflection(full, &wargs->qargs->full_lock, h, k, l); mean = get_intensity(f); sumweight = get_temp1(f); M2 = get_temp2(f); res = resolution(crystal_get_cell(cr), h, k, l); if ( 2.0*res > crystal_get_resolution_limit(cr)+push_res ) { unlock_reflection(f); continue; } /* Reflections count less the more they have to be scaled up */ w = get_partiality(refl) / correct_reflection_nopart(1.0, refl, G, B, res); /* Running mean and variance calculation */ temp = w + sumweight; if ( ln_merge ) { delta = log(correct_reflection(get_intensity(refl), refl, G, B, res)) - mean; } else { delta = correct_reflection(get_intensity(refl), refl, G, B, res) - mean; } R = delta * w / temp; set_intensity(f, mean + R); set_temp2(f, M2 + sumweight * delta * R); set_temp1(f, temp); set_redundancy(f, get_redundancy(f)+1); /* Record this contribution */ c = get_contributions(f); if ( c != NULL ) { c->contribs[c->n_contrib] = refl; c->contrib_crystals[c->n_contrib++] = cr; if ( c->n_contrib == c->max_contrib ) { c->max_contrib += 64; alloc_contribs(c); } } /* else, too bad! */ unlock_reflection(f); wargs->n_reflections++; } } static void finalise_merge_job(void *vqargs, void *vwargs) { struct merge_queue_args *qargs = vqargs; struct merge_worker_args *wargs = vwargs; qargs->n_reflections += wargs->n_reflections; free(vwargs); } RefList *merge_intensities(struct crystal_refls *crystals, int n, int n_threads, int min_meas, double push_res, int use_weak, int ln_merge) { RefList *full; RefList *full2; struct merge_queue_args qargs; Reflection *refl; RefListIterator *iter; if ( n == 0 ) return NULL; full = reflist_new(); qargs.full = full; qargs.n_started = 0; qargs.crystals = crystals; qargs.push_res = push_res; qargs.use_weak = use_weak; qargs.n_reflections = 0; qargs.ln_merge = ln_merge; pthread_rwlock_init(&qargs.full_lock, NULL); run_threads(n_threads, run_merge_job, create_merge_job, finalise_merge_job, &qargs, n, 0, 0, 0); pthread_rwlock_destroy(&qargs.full_lock); /* Calculate ESDs from variances, including only reflections with * enough measurements */ full2 = reflist_new(); if ( full2 == NULL ) return NULL; for ( refl = first_refl(full, &iter); refl != NULL; refl = next_refl(refl, iter) ) { double var; int red; /* Correct for averaging log of intensities*/ if ( ln_merge ) { double ln_I, ln_temp2; ln_temp2 = get_temp2(refl); set_temp2(refl, exp(ln_temp2)); ln_I = get_intensity(refl); set_intensity(refl, exp(ln_I)); } red = get_redundancy(refl); var = get_temp2(refl) / get_temp1(refl); set_esd_intensity(refl, sqrt(var)/sqrt(red)); if ( red >= min_meas ) { signed int h, k, l; Reflection *r2; get_indices(refl, &h, &k, &l); r2 = add_refl(full2, h, k, l); copy_data(r2, refl); } else { /* We do not need the contribution list any more */ struct reflection_contributions *c; c = get_contributions(refl); free(c->contribs); free(c->contrib_crystals); free(c); } } reflist_free(full); return full2; } /* Correct 'val' (probably an intensity from one pattern, maybe an e.s.d.) * for scaling and Lorentz factors but not partiality nor polarisation */ double correct_reflection_nopart(double val, Reflection *refl, double osf, double Bfac, double res) { double corr = osf * exp(-Bfac*res*res); return (val / corr) / get_lorentz(refl); } /* Correct 'val' (probably an intensity from one pattern, maybe an e.s.d.) * for scaling, partiality and Lorentz factors but not polarisation */ double correct_reflection(double val, Reflection *refl, double osf, double Bfac, double res) { double Ipart = correct_reflection_nopart(val, refl, osf, Bfac, res); return Ipart / get_partiality(refl); } double residual(RefList *list, 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(list, &iter); refl != NULL; refl = next_refl(refl, iter) ) { double w, res; signed int h, k, l; Reflection *match; double I_full; double int1, pobs, pcalc; 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; int1 = correct_reflection_nopart(get_intensity(refl), refl, G, B, res); pobs = int1 / I_full; if ( pobs > 1.0 ) pobs = 1.0; if ( pobs < 0.0 ) pobs = 0.0; pcalc = get_partiality(refl); w = 1.0 / correct_reflection_nopart(1.0, refl, G, B, res); if ( isnan(w) ) { w = 0.0; } num += w*fabs(pobs-pcalc); den += w; n_used++; } if ( pn_used != NULL ) *pn_used = n_used; return num/den; } double log_residual(RefList *list, 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(list, &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; 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; } /* Has to match run_merge_job to be useful */ void write_unmerged(const char *fn, struct crystal_refls *crystals, int n_crystals) { FILE *fh; int i; fh = fopen(fn, "w"); if ( fh == NULL ) { ERROR("Failed to open %s\n", fn); return; } for ( i=0; i