diff options
Diffstat (limited to 'src/refine-lmder.c')
-rw-r--r-- | src/refine-lmder.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/src/refine-lmder.c b/src/refine-lmder.c new file mode 100644 index 0000000..ba99eaf --- /dev/null +++ b/src/refine-lmder.c @@ -0,0 +1,439 @@ +/* + * refine-lmder.c + * + * Refinement by GSL's Levenberg-Marquardt LSQ + * + * (c) 2006-2007 Thomas White <taw27@cam.ac.uk> + * + * synth2d - Two-Dimensional Crystallographic Fourier Synthesis + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gsl/gsl_errno.h> +#include <gsl/gsl_math.h> +#include <gsl/gsl_multifit_nlin.h> +#include <gsl/gsl_blas.h> + +#include "refine.h" +#include "model.h" +#include "reflist.h" +#include "statistics.h" + +static int refine_lmder_f(const gsl_vector *coordinates, void *params, gsl_vector *f) { + + RefinementPair *pair; + AtomicModel *model; + ReflectionList *calc; + unsigned int i, j, idx; + double scale; + ReflectionList *obs; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + scale = gsl_vector_get(coordinates, idx++); + for ( j=0; j<model->n_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + /* Calculate residuals */ + for ( i=1; i<calc->n_reflections; i++ ) { + double am_calc, residual; + am_calc = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + /* ( k|Icalc| - |Iobs| ) / error(|Iobs|) */ + residual = (scale*am_calc*am_calc - obs->refs[i].amplitude*obs->refs[i].amplitude)/(sqrt(2)*obs->refs[i].am_error); + } else { + residual = (scale*am_calc - obs->refs[i].amplitude)/obs->refs[i].am_error; /* ( k|Fcalc| - |Fobs| ) / error(|Fobs|) */ + } + gsl_vector_set(f, i-1, residual); + // printf("Residual for %3i %3i %3i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, residual); + } + + model_free(model); + free(calc); + + return GSL_SUCCESS; + +} + +static int refine_lmder_df(const gsl_vector *coordinates, void *params, gsl_matrix *J) { + + RefinementPair *pair; + AtomicModel *model_original; + AtomicModel *model; + ReflectionList *obs; + ReflectionList *calc; + unsigned int i, j, idx; + double scale; + + pair = params; + model = model_copy(pair->model); + obs = pair->reflections; + + idx = 0; + scale = gsl_vector_get(coordinates, idx++); + for ( j=0; j<model->n_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( pair->spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( pair->spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( pair->spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(coordinates, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( pair->spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( pair->spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(coordinates, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + /* Calculate reflections templated from the original list */ + calc = model_calculate_f(obs, model, 69); + + idx = 0; + + model_original = model_copy(model); + + /* Gradient of the scale factor */ + for ( i=1; i<calc->n_reflections; i++ ) { + double am; + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + gsl_matrix_set(J, i-1, idx, am*am); + } else { + gsl_matrix_set(J, i-1, idx, am); + } + } + idx++; + + /* Determine gradients of atomic coordinates and B-factors */ + for ( j=0; j<model->n_atoms; j++ ) { + + if ( !(model->atoms[j].refine) ) continue; + double x, y, z; + ReflectionList *reflections_new; + + x = model->atoms[j].x; + y = model->atoms[j].y; + z = model->atoms[j].z; + + if ( pair->spec & REFINE_SPEC_X ) { + model->atoms[j].x += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; i<obs->n_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt x%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_Y ) { + model->atoms[j].y += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; i<obs->n_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt y%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_Z ) { + model->atoms[j].z += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; i<obs->n_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt z%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_B ) { + model->atoms[j].B += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; i<obs->n_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt B%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + if ( pair->spec & REFINE_SPEC_OCC ) { + model->atoms[j].occ += LSQ_MSLS_SHIFT; + reflections_new = model_calculate_f(obs, model, 69); + for ( i=1; i<obs->n_reflections; i++ ) { + + double derivative, am, residual_old, residual_new; + + am = calc->refs[i].amplitude; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_old = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_old = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + am = reflections_new->refs[i].amplitude; + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + if ( pair->spec & REFINE_SPEC_INTENSITIES ) { + residual_new = (scale*am*am - (obs->refs[i].amplitude*obs->refs[i].amplitude))/(sqrt(2)*obs->refs[i].am_error); + } else { + residual_new = (scale*am - obs->refs[i].amplitude)/obs->refs[i].am_error; + } + + derivative = (residual_new - residual_old)/LSQ_MSLS_SHIFT; + gsl_matrix_set(J, i-1, idx, derivative); + // printf("J for %3i %3i %3i reflection wrt B%i = %f\n", obs->refs[i].h, obs->refs[i].k, obs->refs[i].l, j, derivative); + + } + idx++; + reflist_free(reflections_new); + model_move(model, model_original); + } + + } + + model_free(model_original); + model_free(model); + free(calc); + + return GSL_SUCCESS; + +} + +static int refine_lmder_fdf(const gsl_vector *coordinates, void *params, gsl_vector *f, gsl_matrix *J) { + + refine_lmder_f(coordinates, params, f); + refine_lmder_df(coordinates, params, J); + + return GSL_SUCCESS; + +} + +void refine_lmder(AtomicModel *model, ReflectionList *reflections, RefinementSpec spec) { + + gsl_multifit_fdfsolver *s; + gsl_multifit_function_fdf f; + unsigned int iter = 0; + gsl_vector *coordinates; + unsigned int j; + RefinementPair pair; + int iter_status, conv_status; + gsl_matrix *covar; + unsigned int n_params, n_atoms, idx; + double scale; + ReflectionList *calc; + + n_params = 0; + if ( spec & REFINE_SPEC_X ) n_params++; + if ( spec & REFINE_SPEC_Y ) n_params++; + if ( spec & REFINE_SPEC_Z ) n_params++; + if ( spec & REFINE_SPEC_B ) n_params++; + if ( spec & REFINE_SPEC_OCC ) n_params++; + + /* Prevent origin from floating */ + if ( (spec & REFINE_SPEC_X) || (spec & REFINE_SPEC_Y) || (spec & REFINE_SPEC_Z) ) { + model->atoms[0].refine = 0; + } + + n_atoms = 0; + for ( j=0; j<model->n_atoms; j++ ) { + if ( model->atoms[j].refine ) n_atoms++; + } + + f.n = reflections->n_reflections-1; f.p = 1 + n_params * n_atoms; + s = gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmsder, f.n, f.p); + f.f = &refine_lmder_f; f.df = &refine_lmder_df; f.fdf = &refine_lmder_fdf; + printf("Refining %i parameters against %i reflections\n", f.p, f.n); + coordinates = gsl_vector_alloc(f.p); + + /* Set initial estimates */ + idx = 0; + calc = model_calculate_f(reflections, NULL, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + scale = stat_scale_intensity(reflections, calc); + } else { + scale = stat_scale(reflections, calc); + } + reflist_free(calc); + gsl_vector_set(coordinates, idx++, scale); + for ( j=0; j<model->n_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) gsl_vector_set(coordinates, idx++, model->atoms[j].x); + if ( spec & REFINE_SPEC_Y ) gsl_vector_set(coordinates, idx++, model->atoms[j].y); + if ( spec & REFINE_SPEC_Z ) gsl_vector_set(coordinates, idx++, model->atoms[j].z); + if ( spec & REFINE_SPEC_B ) gsl_vector_set(coordinates, idx++, model->atoms[j].B); + if ( spec & REFINE_SPEC_OCC ) gsl_vector_set(coordinates, idx++, model->atoms[j].occ); + } + } + + pair.model = model; pair.reflections = reflections; pair.spec = spec; + f.params = &pair; + + covar = gsl_matrix_alloc(f.p, f.p); + gsl_multifit_fdfsolver_set(s, &f, coordinates); + + printf("initial: scale=%f, |f(x)|=%g\n", gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f)); + + do { + + ReflectionList *model_reflections; + + /* Iterate */ + printf("before iteration %i: scale=%f, |f(x)|=%g\n", iter, gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f)); + iter_status = gsl_multifit_fdfsolver_iterate(s); + printf("after iteration %i: scale=%f, |f(x)|=%g, status=%s, ", iter, gsl_vector_get(s->x, 0), gsl_blas_dnrm2(s->f), gsl_strerror(iter_status)); + iter++; + + /* Check for error */ + if ( iter_status ) { + printf("\nError in LSQ refinement"); + break; + } + + /* Test for convergence */ + conv_status = gsl_multifit_test_delta(s->dx, s->x, 1e-6, 1e-6); + //printf("status after convergence test = %s\n", gsl_strerror(conv_status)); + + idx = 0; + scale = gsl_vector_get(s->x, idx++); + for ( j=0; j<model->n_atoms; j++ ) { + if ( model->atoms[j].refine ) { + if ( spec & REFINE_SPEC_X ) model->atoms[j].x = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].x < 0 ) model->atoms[j].x = 1 + model->atoms[j].x; + if ( spec & REFINE_SPEC_Y ) model->atoms[j].y = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].y < 0 ) model->atoms[j].y = 1 + model->atoms[j].y; + if ( spec & REFINE_SPEC_Z ) model->atoms[j].z = fmod(gsl_vector_get(s->x, idx++), 1); + if ( model->atoms[j].z < 0 ) model->atoms[j].z = 1 + model->atoms[j].z; + if ( spec & REFINE_SPEC_B ) model->atoms[j].B = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].B < 0 ) model->atoms[j].B = 0; + if ( spec & REFINE_SPEC_OCC ) model->atoms[j].occ = gsl_vector_get(s->x, idx++); + if ( model->atoms[j].occ < 0 ) model->atoms[j].occ = 0; + } + } + + model_reflections = model_calculate_f(reflections, model, 69); + if ( spec & REFINE_SPEC_INTENSITIES ) { + printf("R2=%.2f%%\n\n", stat_r2(reflections, model_reflections)*100); /* stat_r(Fobs, Fcalc) */ + } else { + printf("R1=%.2f%%\n\n", stat_r(reflections, model_reflections)*100); /* stat_r(Fobs, Fcalc) */ + } + free(model_reflections); + + refine_schedule_update(model); + + } while ( (conv_status == GSL_CONTINUE) && (iter < MAX_REFINEMENT_ITERATIONS) && (model->refine_window->run_semaphore) ); + + printf("%i iterations performed\n", iter); + if ( iter == MAX_REFINEMENT_ITERATIONS ) printf("Reached maximum allowed number of iterations"); + + gsl_multifit_covar(s->J, 0.0, covar); + gsl_matrix_free(covar); + + gsl_multifit_fdfsolver_free(s); + gsl_vector_free(coordinates); + +} + |