compare_hkl.c 12.4 KB
Newer Older
1
2
3
4
5
/*
 * compare_hkl.c
 *
 * Compare reflection lists
 *
6
7
8
9
 * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
 *                  a research centre of the Helmholtz Association.
 *
 * Authors:
10
 *   2010-2012 Thomas White <taw@physics.org>
11
 *
Thomas White's avatar
Thomas White committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * This file is part of CrystFEL.
 *
 * CrystFEL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CrystFEL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CrystFEL.  If not, see <http://www.gnu.org/licenses/>.
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 *
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
Thomas White's avatar
Thomas White committed
40
#include <assert.h>
41
#include <gsl/gsl_errno.h>
42
43

#include "utils.h"
Thomas White's avatar
Thomas White committed
44
#include "statistics.h"
45
#include "symmetry.h"
Thomas White's avatar
Thomas White committed
46
#include "reflist-utils.h"
47
48


Thomas White's avatar
Thomas White committed
49
/* Number of bins for plot of resolution shells */
50
#define NBINS (10)
Thomas White's avatar
Thomas White committed
51
52


53
54
55
56
57
58
59
60
61
62
63
enum r_shell
{
	R_SHELL_NONE,
	R_SHELL_R1I,
	R_SHELL_R1F,
	R_SHELL_RSPLIT,
};


static enum r_shell get_r_shell(const char *s)
{
64
65
66
	if ( strcasecmp(s, "r1i") == 0 ) return R_SHELL_R1I;
	if ( strcasecmp(s, "r1f") == 0 ) return R_SHELL_R1F;
	if ( strcasecmp(s, "rsplit") == 0 ) return R_SHELL_RSPLIT;
67
68
69
70
71
72
73

	ERROR("Unknown R-factor '%s' - try '--shells=rsplit', or --help for"
	      " more possibilities.\n", s);
	exit(1);
}


74
75
static void show_help(const char *s)
{
76
	printf("Syntax: %s [options] <file1.hkl> <file2.hkl>\n\n", s);
77
78
79
80
	printf(
"Compare intensity lists.\n"
"\n"
"  -h, --help                 Display this help message.\n"
Thomas White's avatar
Thomas White committed
81
"  -o, --ratio=<filename>     Specify output filename for ratios.\n"
82
"  -y, --symmetry=<sym>       The symmetry of both the input files.\n"
Thomas White's avatar
Thomas White committed
83
"  -p, --pdb=<filename>       PDB file to use.\n"
84
85
"      --shells=<FoM>         Plot this figure of merit in resolution shells.\n"
"                              Choose from: 'Rsplit', 'R1f' and 'R1i'.\n"
86
87
"      --rmin=<res>           Fix lower resolution limit for --shells (m^-1).\n"
"      --rmax=<res>           Fix upper resolution limit for --shells (m^-1).\n"
88
"      --sigma-cutoff=<n>     Discard reflections with I/sigma(I) < n.\n"
89
90
91
92
"\n");
}


93
static void plot_shells(RefList *list1, RefList *list2, double scale,
94
95
                        UnitCell *cell, double rmin_fix, double rmax_fix,
                        enum r_shell config_shells)
Thomas White's avatar
Thomas White committed
96
97
{
	double num[NBINS];
Thomas White's avatar
Thomas White committed
98
	int cts[NBINS];
99
100
101
102
	double total_vol, vol_per_shell;
	double rmins[NBINS];
	double rmaxs[NBINS];
	double rmin, rmax;
Thomas White's avatar
Thomas White committed
103
	int i;
Thomas White's avatar
Thomas White committed
104
105
	Reflection *refl1;
	RefListIterator *iter;
106
	FILE *fh;
107
	double den[NBINS];
108
	int ctot, nout;
Thomas White's avatar
Thomas White committed
109
110

	if ( cell == NULL ) {
Thomas White's avatar
Thomas White committed
111
		ERROR("Need the unit cell to plot resolution shells.\n");
Thomas White's avatar
Thomas White committed
112
113
114
115
116
		return;
	}

	for ( i=0; i<NBINS; i++ ) {
		num[i] = 0.0;
117
		den[i] = 0.0;
Thomas White's avatar
Thomas White committed
118
		cts[i] = 0;
Thomas White's avatar
Thomas White committed
119
120
	}

121
122
	/* Find resolution limits */
	resolution_limits(list1, cell, &rmin, &rmax);
123
	STATUS("1/d goes from %f to %f nm^-1\n", rmin/1e9, rmax/1e9);
124

125
126
	/* Widen the range just a little bit */
	rmin -= 0.001e9;
127
128
	rmax += 0.001e9;

129
130
131
	/* Fixed resolution shells if needed */
	if ( rmin_fix > 0.0 ) rmin = rmin_fix;
	if ( rmax_fix > 0.0 ) rmax = rmax_fix;
132

Thomas White's avatar
Thomas White committed
133
	/* Calculate the resolution bins */
134
135
136
137
138
139
140
141
142
143
	total_vol = pow(rmax, 3.0) - pow(rmin, 3.0);
	vol_per_shell = total_vol / NBINS;
	rmins[0] = rmin;
	for ( i=1; i<NBINS; i++ ) {

		double r;

		r = vol_per_shell + pow(rmins[i-1], 3.0);
		r = pow(r, 1.0/3.0);

144
		/* Shells of constant volume */
145
146
147
		rmaxs[i-1] = r;
		rmins[i] = r;

148
149
150
151
		/* Shells of constant thickness */
		//rmins[i] = rmins[i-1] + (rmax-rmin)/NBINS;
		//rmaxs[i-1] = rmins[i-1] + (rmax-rmin)/NBINS;

152
153
154
155
156
157
158
159
		STATUS("Shell %i: %f to %f\n", i-1,
		       rmins[i-1]/1e9, rmaxs[i-1]/1e9);

	}
	rmaxs[NBINS-1] = rmax;
	STATUS("Shell %i: %f to %f\n", NBINS-1,
	       rmins[NBINS-1]/1e9, rmaxs[NBINS-1]/1e9);

160
	ctot = 0;  nout = 0;
Thomas White's avatar
Thomas White committed
161
162
	for ( refl1 = first_refl(list1, &iter);
	      refl1 != NULL;
163
164
	      refl1 = next_refl(refl1, iter) )
	{
Thomas White's avatar
Thomas White committed
165
		signed int h, k, l;
166
		double d;
Thomas White's avatar
Thomas White committed
167
		int bin;
168
		double i1, i2, f1, f2;
169
		int j;
170
		Reflection *refl2;
Thomas White's avatar
Thomas White committed
171

Thomas White's avatar
Thomas White committed
172
		get_indices(refl1, &h, &k, &l);
173
		d = 2.0 * resolution(cell, h, k, l);
Thomas White's avatar
Thomas White committed
174

175
176
177
178
179
180
181
		bin = -1;
		for ( j=0; j<NBINS; j++ ) {
			if ( (d>rmins[j]) && (d<=rmaxs[j]) ) {
				bin = j;
				break;
			}
		}
182
183

		/* Outside resolution range? */
184
185
186
187
		if ( bin == -1 ) {
			nout++;
			continue;
		}
Thomas White's avatar
Thomas White committed
188

189
190
191
		refl2 = find_refl(list2, h, k, l);
		if ( refl2 == NULL ) continue;

Thomas White's avatar
Thomas White committed
192
		i1 = get_intensity(refl1);
193
194
195
196
197
198
199
		i2 = get_intensity(refl2);
		f1 = i1 > 0.0 ? sqrt(i1) : 0.0;
		f2 = i2 > 0.0 ? sqrt(i2) : 0.0;

		switch ( config_shells ) {

			case R_SHELL_RSPLIT :
200
201
			num[bin] += fabs(i1 - i2);
			den[bin] += i1 + i2;
202
203
204
205
			break;

			case R_SHELL_R1I :
			num[bin] += fabs(i1 - scale*i2);
206
			den[bin] += i1;
207
208
209
210
			break;

			case R_SHELL_R1F :
			num[bin] += fabs(f1 - scale*f2);
211
			den[bin] += f1;
212
213
214
215
216
			break;

			default : break;

		}
Thomas White's avatar
Thomas White committed
217

Thomas White's avatar
Thomas White committed
218
		ctot++;
219
		cts[bin]++;
Thomas White's avatar
Thomas White committed
220
221
	}

222
223
224
225
226
	if ( nout ) {
		STATUS("Warning; %i reflections outside resolution range.\n",
		       nout);
	}

227
228
229
230
231
232
	fh = fopen("shells.dat", "w");
	if ( fh == NULL ) {
		ERROR("Couldn't open 'shells.dat'\n");
		return;
	}

233
234
235
	switch ( config_shells ) {

		case R_SHELL_RSPLIT :
236
		fprintf(fh, "1/d centre   Rsplit / %%     nref       d (A)\n");
237
238
239
		break;

		case R_SHELL_R1I :
240
		fprintf(fh, "1/d centre   R1(I) / %%      nref       d (A)\n");
241
242
243
		break;

		case R_SHELL_R1F :
244
		fprintf(fh, "1/d centre   R1(F) ign -/%%  nref       d (A)\n");
245
246
247
		break;

		default :
248
		fprintf(fh, "1/d centre   0.0             nref       d (A)\n");
249
250
251
		break;

	}
252

Thomas White's avatar
Thomas White committed
253
254
	for ( i=0; i<NBINS; i++ ) {

255
		double r, cen;
256
		cen = rmins[i] + (rmaxs[i] - rmins[i])/2.0;
257
258
259
260

		switch ( config_shells ) {

			case R_SHELL_RSPLIT :
261
			r = 2.0*(num[i]/den[i]) / sqrt(2.0);
262
263
264
265
			break;

			case R_SHELL_R1I :
			case R_SHELL_R1F :
266
			r = num[i]/den[i];
267
268
269
270
271
272
273
274
			break;

			default :
			r = 0.0;
			break;

		}

275
276
		fprintf(fh, "%10.3f %10.2f %10i %10.2f\n",
		        cen*1.0e-9, r*100.0, cts[i], (1.0/cen)*1e10);
Thomas White's avatar
Thomas White committed
277
278
279
280

	}

	fclose(fh);
Thomas White's avatar
Thomas White committed
281
282

	STATUS("Resolution shell information written to shells.dat.\n");
Thomas White's avatar
Thomas White committed
283
284
285
}


286
287
288
int main(int argc, char *argv[])
{
	int c;
289
	UnitCell *cell;
Thomas White's avatar
Thomas White committed
290
	char *ratiofile = NULL;
291
292
	char *afile = NULL;
	char *bfile = NULL;
Thomas White's avatar
Thomas White committed
293
294
	char *sym_str = NULL;
	SymOpList *sym;
295
	double scale, scale_r2, scale_rdig, R1, R2, R1i, Rdiff, pearson;
296
	double scale_rintint, scale_r1i, scale_r1, scale_r1fi;
297
	int ncom, nrej;
Thomas White's avatar
Thomas White committed
298
299
	RefList *list1;
	RefList *list2;
300
301
	RefList *list1_raw;
	RefList *list2_raw;
Thomas White's avatar
Thomas White committed
302
	RefList *ratio;
303
	enum r_shell config_shells = R_SHELL_NONE;
304
	char *pdb = NULL;
305
306
	float rmin_fix = -1.0;
	float rmax_fix = -1.0;
Thomas White's avatar
Thomas White committed
307
308
	Reflection *refl1;
	RefListIterator *iter;
309
	int config_unity = 0;
310
	double scale_for_shells;
311
	float sigma_cutoff = -INFINITY;
312
313
314
315

	/* Long options */
	const struct option longopts[] = {
		{"help",               0, NULL,               'h'},
Thomas White's avatar
Thomas White committed
316
		{"ratio" ,             1, NULL,               'o'},
317
		{"symmetry",           1, NULL,               'y'},
318
		{"pdb",                1, NULL,               'p'},
319
320
321
322
		{"rmin",               1, NULL,                2},
		{"rmax",               1, NULL,                3},
		{"shells",             1, NULL,                4},
		{"sigma-cutoff",       1, NULL,                5},
323
324
325
326
		{0, 0, NULL, 0}
	};

	/* Short options */
327
328
329
	while ((c = getopt_long(argc, argv, "ho:y:p:u",
	                        longopts, NULL)) != -1)
	{
330
331

		switch (c) {
Thomas White's avatar
Thomas White committed
332
333

			case 'h' :
334
335
336
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
337
			case 'o' :
Thomas White's avatar
Thomas White committed
338
			ratiofile = strdup(optarg);
339
340
			break;

Thomas White's avatar
Thomas White committed
341
			case 'y' :
Thomas White's avatar
Thomas White committed
342
			sym_str = strdup(optarg);
343
344
			break;

Thomas White's avatar
Thomas White committed
345
			case 'p' :
346
347
348
			pdb = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
349
			case 'u' :
350
351
352
			config_unity = 1;
			break;

Thomas White's avatar
Thomas White committed
353
			case 0 :
354
355
			break;

Thomas White's avatar
Thomas White committed
356
			case 2 :
357
358
359
360
361
362
			if ( sscanf(optarg, "%e", &rmin_fix) != 1 ) {
				ERROR("Invalid value for --rmin\n");
				return 1;
			}
			break;

Thomas White's avatar
Thomas White committed
363
			case 3 :
364
365
366
367
368
369
			if ( sscanf(optarg, "%e", &rmax_fix) != 1 ) {
				ERROR("Invalid value for --rmax\n");
				return 1;
			}
			break;

Thomas White's avatar
Thomas White committed
370
			case 4 :
371
372
373
			config_shells = get_r_shell(optarg);
			break;

374
375
376
377
378
379
380
			case 5 :
			if ( sscanf(optarg, "%f", &sigma_cutoff) != 1 ) {
				ERROR("Invalid value for --sigma-cutoff\n");
				return 1;
			}
			break;

Thomas White's avatar
Thomas White committed
381
			default :
382
383
			ERROR("Unhandled option '%c'\n", c);
			break;
Thomas White's avatar
Thomas White committed
384

385
386
387
388
		}

	}

389
390
391
392
393
	if ( argc != (optind+2) ) {
		ERROR("Please provide exactly two HKL files to compare.\n");
		return 1;
	}

Thomas White's avatar
Thomas White committed
394
395
	if ( sym_str == NULL ) {
		sym_str = strdup("1");
396
	}
Thomas White's avatar
Thomas White committed
397
398
	sym = get_pointgroup(sym_str);
	free(sym_str);
399

400
401
402
	afile = strdup(argv[optind++]);
	bfile = strdup(argv[optind]);

403
404
405
406
407
408
409
	if ( pdb == NULL ) {
		pdb = strdup("molecule.pdb");
	}

	cell = load_cell_from_pdb(pdb);
	free(pdb);

410
411
	list1_raw = read_reflections(afile);
	if ( list1_raw == NULL ) {
Thomas White's avatar
Thomas White committed
412
		ERROR("Couldn't read file '%s'\n", afile);
413
414
		return 1;
	}
Thomas White's avatar
Thomas White committed
415

416
417
	list2_raw = read_reflections(bfile);
	if ( list2_raw == NULL ) {
Thomas White's avatar
Thomas White committed
418
		ERROR("Couldn't read file '%s'\n", bfile);
419
420
421
		return 1;
	}

Thomas White's avatar
Thomas White committed
422
	/* Check that the intensities have the correct symmetry */
423
	if ( check_list_symmetry(list1_raw, sym) ) {
Thomas White's avatar
Thomas White committed
424
		ERROR("The first input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
425
		      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
426
427
		return 1;
	}
428
	if ( check_list_symmetry(list2_raw, sym) ) {
Thomas White's avatar
Thomas White committed
429
		ERROR("The second input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
430
		      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
431
432
433
		return 1;
	}

434
435
436
437
	list1 = asymmetric_indices(list1_raw, sym);
	list2 = asymmetric_indices(list2_raw, sym);

	/* Find common reflections and calculate ratio */
Thomas White's avatar
Thomas White committed
438
	ratio = reflist_new();
439
	ncom = 0;
440
	nrej = 0;
Thomas White's avatar
Thomas White committed
441
442
	for ( refl1 = first_refl(list1, &iter);
	      refl1 != NULL;
Thomas White's avatar
Thomas White committed
443
444
	      refl1 = next_refl(refl1, iter) )
	{
Thomas White's avatar
Thomas White committed
445
		signed int h, k, l;
446
		double val1, val2;
447
		double esd1, esd2;
Thomas White's avatar
Thomas White committed
448
449
		Reflection *refl2;
		Reflection *tr;
450

Thomas White's avatar
Thomas White committed
451
		get_indices(refl1, &h, &k, &l);
452

453
454
		refl2 = find_refl(list2, h, k, l);
		if ( refl2 == NULL ) continue;
455

Thomas White's avatar
Thomas White committed
456
457
458
		val1 = get_intensity(refl1);
		val2 = get_intensity(refl2);

459
460
461
462
463
464
465
466
467
468
469
470
		esd1 = get_esd_intensity(refl1);
		esd2 = get_esd_intensity(refl2);

		if ( (val1 < sigma_cutoff * esd1)
		  || (val2 < sigma_cutoff * esd2) )
		{
			nrej++;
			continue;
		}

		ncom++;

Thomas White's avatar
Thomas White committed
471
472
		/* Add divided version to 'output' list */
		tr = add_refl(ratio, h, k, l);
473
		set_intensity(tr, val1/val2);
474
		set_redundancy(tr, 1);
475
	}
Thomas White's avatar
Thomas White committed
476

477
	if ( ratiofile != NULL ) {
478
		write_reflist(ratiofile, ratio);
479
480
481
	}
	reflist_free(ratio);

482
483
	gsl_set_error_handler_off();

484
485
486
487
488
489
490
	STATUS("%i,%i reflections: %i in common where both versions have "
	       "I/sigma(I) >= %f.\n",
	       num_reflections(list1), num_reflections(list2), ncom,
	       sigma_cutoff);

	STATUS("Discarded %i reflections because either or both versions "
	       " had I/sigma(I) < %f\n", nrej, sigma_cutoff);
Thomas White's avatar
Thomas White committed
491

492
	R1 = stat_r1_ignore(list1, list2, &scale_r1fi, config_unity);
Thomas White's avatar
Thomas White committed
493
494
	STATUS("R1(F) = %5.4f %% (scale=%5.2e)"
	       " (ignoring negative intensities)\n",
495
	       R1*100.0, scale_r1fi);
Thomas White's avatar
Thomas White committed
496

497
	R1 = stat_r1_zero(list1, list2, &scale_r1, config_unity);
Thomas White's avatar
Thomas White committed
498
499
	STATUS("R1(F) = %5.4f %% (scale=%5.2e)"
	       " (zeroing negative intensities)\n",
500
	       R1*100.0, scale_r1);
Thomas White's avatar
Thomas White committed
501

502
	R2 = stat_r2(list1, list2, &scale_r2, config_unity);
503
	STATUS("R2(I) = %5.4f %% (scale=%5.2e)\n", R2*100.0, scale_r2);
Thomas White's avatar
Thomas White committed
504

505
	R1i = stat_r1_i(list1, list2, &scale_r1i, config_unity);
506
	STATUS("R1(I) = %5.4f %% (scale=%5.2e)\n", R1i*100.0, scale_r1i);
Thomas White's avatar
Thomas White committed
507

508
	Rdiff = stat_rdiff_ignore(list1, list2, &scale_rdig,
509
	                          config_unity);
Thomas White's avatar
Thomas White committed
510
511
	STATUS("Rint(F) = %5.4f %% (scale=%5.2e)"
	       " (ignoring negative intensities)\n",
512
	       Rdiff*100.0, scale_rdig);
Thomas White's avatar
Thomas White committed
513

514
	Rdiff = stat_rdiff_zero(list1, list2, &scale, config_unity);
Thomas White's avatar
Thomas White committed
515
516
	STATUS("Rint(F) = %5.4f %% (scale=%5.2e)"
	       " (zeroing negative intensities)\n",
Thomas White's avatar
Thomas White committed
517
518
	       Rdiff*100.0, scale);

519
	Rdiff = stat_rdiff_intensity(list1, list2, &scale_rintint,
520
	                             config_unity);
Thomas White's avatar
Thomas White committed
521
	STATUS("Rint(I) = %5.4f %% (scale=%5.2e)\n",
522
	       Rdiff*100.0, scale_rintint);
Thomas White's avatar
Thomas White committed
523

524
	pearson = stat_pearson_i(list1, list2);
Thomas White's avatar
Thomas White committed
525
526
	STATUS("Pearson r(I) = %5.4f\n", pearson);

527
	pearson = stat_pearson_f_ignore(list1, list2);
Thomas White's avatar
Thomas White committed
528
529
530
	STATUS("Pearson r(F) = %5.4f (ignoring negative intensities)\n",
	       pearson);

531
	pearson = stat_pearson_f_zero(list1, list2);
Thomas White's avatar
Thomas White committed
532
533
	STATUS("Pearson r(F) = %5.4f (zeroing negative intensities)\n",
	       pearson);
Thomas White's avatar
Thomas White committed
534

535
536
537
538
539
540
541
542
543
544
	switch ( config_shells ) {
		case R_SHELL_R1I : scale_for_shells = scale_r1i;  break;
		case R_SHELL_R1F : scale_for_shells = scale_r1;  break;
		case R_SHELL_RSPLIT : scale_for_shells = scale_rintint;  break;
		default : scale_for_shells = 0.0;
	}

	if ( config_shells != R_SHELL_NONE ) {
		plot_shells(list1, list2, scale_for_shells,
		            cell, rmin_fix, rmax_fix, config_shells);
Thomas White's avatar
Thomas White committed
545
546
	}

547
548
	return 0;
}