pattern_sim.c 16 KB
Newer Older
1
/*
Thomas White's avatar
Thomas White committed
2
3
4
 * pattern_sim.c
 *
 * Simulate diffraction patterns from small crystals
5
 *
Thomas White's avatar
Thomas White committed
6
 * Copyright © 2012 Thomas White <taw@physics.org>
7
 *
Thomas White's avatar
Thomas White committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 * 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/>.
22
23
24
25
26
27
28
29
30
31
32
33
34
 *
 */


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

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
Thomas White's avatar
Thomas White committed
35
#include <getopt.h>
36

37
#include "image.h"
Thomas White's avatar
Thomas White committed
38
#include "diffraction.h"
39
#include "diffraction-gpu.h"
40
#include "cell.h"
41
42
#include "utils.h"
#include "hdf5-file.h"
Thomas White's avatar
Thomas White committed
43
#include "detector.h"
44
#include "peaks.h"
45
#include "beam-parameters.h"
Thomas White's avatar
Thomas White committed
46
#include "symmetry.h"
Thomas White's avatar
Thomas White committed
47
48
#include "reflist.h"
#include "reflist-utils.h"
49
#include "pattern_sim.h"
50
51


Thomas White's avatar
Thomas White committed
52
static void show_help(const char *s)
53
{
54
	printf("Syntax: %s [options]\n\n", s);
Thomas White's avatar
Thomas White committed
55
	printf(
Thomas White's avatar
Thomas White committed
56
"Simulate diffraction patterns from small crystals probed with femtosecond\n"
Thomas White's avatar
Thomas White committed
57
58
"pulses of X-rays from a free electron laser.\n"
"\n"
59
" -h, --help                Display this help message.\n"
Thomas White's avatar
Thomas White committed
60
"\n"
61
62
63
" -p, --pdb=<file>          PDB file from which to get the unit cell.\n"
"                            (The actual Bragg intensities come from the\n"
"                            intensities file)\n"
64
"     --gpu                 Use the GPU to speed up the calculation.\n"
Thomas White's avatar
Thomas White committed
65
66
"     --gpu-dev=<n>         Use GPU device <n>.  Omit this option to see the\n"
"                            available devices.\n"
67
68
" -g, --geometry=<file>     Get detector geometry from file.\n"
" -b, --beam=<file>         Get beam parameters from file.\n"
69
70
71
"\n"
" -n, --number=<N>          Generate N images.  Default 1.\n"
"     --no-images           Do not output any HDF5 files.\n"
72
73
74
75
76
77
" -o, --output=<filename>   Output HDF5 filename.  Default: sim.h5.\n"
"                            If you choose to simulate more than one pattern,\n"
"                            the filename given will be postfixed with a\n"
"                            hyphen, the image number and '.h5'.  In this\n"
"                            case, the default value is 'sim', such that the\n"
"                            files produced are sim-1.h5, sim-2.h5 and so on.\n"
Thomas White's avatar
Thomas White committed
78
" -r, --random-orientation  Use a randomly generated orientation\n"
79
"                            (a new orientation will be used for each image).\n"
80
81
"     --powder=<file>       Write a summed pattern of all images simulated by\n"
"                            this invocation as the given filename.\n"
82
" -i, --intensities=<file>  Specify file containing reflection intensities\n"
Thomas White's avatar
Thomas White committed
83
"                            (and phases) to use.\n"
84
" -y, --symmetry=<sym>      The symmetry of the intensities file.\n"
Thomas White's avatar
Thomas White committed
85
" -t, --gradients=<method>  Use <method> for the calculation of shape\n"
86
87
"                            transform intensities.  Choose from:\n"
"                             mosaic      : Take the intensity of the nearest\n"
Thomas White's avatar
Thomas White committed
88
89
90
"                                           Bragg position.  This is the\n"
"                                           fastest method and the only one\n"
"                                           supported on the GPU.\n"
91
92
"                             interpolate : Interpolate trilinearly between\n"
"                                           six adjacent Bragg intensities.\n"
Thomas White's avatar
Thomas White committed
93
94
"                                           This method has intermediate\n"
"                                           accuracy.\n"
95
"                             phased      : As 'interpolate', but take phase\n"
Thomas White's avatar
Thomas White committed
96
97
98
"                                           values into account.  This is the\n"
"                                           most accurate method, but the\n"
"                                           slowest.\n"
99
100
101
"     --really-random       Use really random numbers for the orientation and\n"
"                            crystal size.  By default, the same sequence\n"
"                            will be used for each new run.\n"
102
103
104
105
106
"     --min-size=<s>        Generate random crystal sizes using <s> as the\n"
"                            minimum crystal size in nm. --max-size is also\n"
"                            required.\n"
"     --max-size=<s>        Use <s> as the maximum crystal size in nm.\n"
"                            --min-size is also required.\n"
Thomas White's avatar
Thomas White committed
107
108
109
110
111
"\n"
"By default, the simulation aims to be as accurate as possible.  For greater\n"
"speed, or for testing, you can choose to disable certain things using the\n"
"following options.\n"
"\n"
Thomas White's avatar
Thomas White committed
112
"     --no-noise            Do not calculate Poisson noise.\n"
113
);
Thomas White's avatar
Thomas White committed
114
115
116
}


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
static double *intensities_from_list(RefList *list)
{
	Reflection *refl;
	RefListIterator *iter;
	double *out = new_list_intensity();

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {

		signed int h, k, l;
		double intensity = get_intensity(refl);

		get_indices(refl, &h, &k, &l);

		set_intensity(out, h, k, l, intensity);

	}

	return out;
}


static double *phases_from_list(RefList *list)
{
	Reflection *refl;
	RefListIterator *iter;
	double *out = new_list_phase();

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {

		signed int h, k, l;
		double phase = get_phase(refl, NULL);

		get_indices(refl, &h, &k, &l);

		set_phase(out, h, k, l, phase);

	}

	return out;

}


static unsigned char *flags_from_list(RefList *list)
{
	Reflection *refl;
	RefListIterator *iter;
	unsigned char *out = new_list_flag();

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {

		signed int h, k, l;

		get_indices(refl, &h, &k, &l);

		set_flag(out, h, k, l, 1);

	}

	return out;

}


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
static struct quaternion read_quaternion()
{
	do {

		int r;
		float w, x, y, z;
		char line[1024];
		char *rval;

		printf("Please input quaternion: w x y z\n");
		rval = fgets(line, 1023, stdin);
		if ( rval == NULL ) return invalid_quaternion();
		chomp(line);

		r = sscanf(line, "%f %f %f %f", &w, &x, &y, &z);
		if ( r == 4 ) {

			struct quaternion quat;

			quat.w = w;
			quat.x = x;
			quat.y = y;
			quat.z = z;

			return quat;

		} else {
214
			ERROR("Invalid rotation '%s'\n", line);
215
216
217
		}

	} while ( 1 );
218
219
220
}


221
222
223
224
225
226
227
228
229
230
231
232
static int random_ncells(double len, double min_nm, double max_nm)
{
	int min_cells, max_cells, wid;

	min_cells = min_nm*1e-9 / len;
	max_cells = max_nm*1e-9 / len;
	wid = max_cells - min_cells;

	return wid*random()/RAND_MAX + min_cells;
}


233
234
int main(int argc, char *argv[])
{
235
	int c;
236
	struct image image;
237
	struct gpu_context *gctx = NULL;
238
	double *powder;
239
240
	char *intfile = NULL;
	double *intensities;
241
	char *rval;
242
	double *phases;
243
	unsigned char *flags;
244
	int config_randomquat = 0;
245
	int config_noimages = 0;
Thomas White's avatar
Thomas White committed
246
	int config_nonoise = 0;
247
	int config_nosfac = 0;
248
	int config_gpu = 0;
249
	int config_random = 0;
250
	char *powder_fn = NULL;
251
	char *filename = NULL;
252
	char *grad_str = NULL;
253
	char *outfile = NULL;
Thomas White's avatar
Thomas White committed
254
	char *geometry = NULL;
255
	char *beamfile = NULL;
256
	GradientMethod grad;
Thomas White's avatar
Thomas White committed
257
258
259
	int ndone = 0;    /* Number of simulations done (images or not) */
	int number = 1;   /* Number used for filename of image */
	int n_images = 1; /* Generate one image by default */
260
	int done = 0;
Thomas White's avatar
Thomas White committed
261
262
	UnitCell *input_cell;
	struct quaternion orientation;
Thomas White's avatar
Thomas White committed
263
	int gpu_dev = -1;
264
265
266
	int random_size = 0;
	double min_size = 0.0;
	double max_size = 0.0;
Thomas White's avatar
Thomas White committed
267
268
	char *sym_str = NULL;
	SymOpList *sym;
Thomas White's avatar
Thomas White committed
269

270
	/* Long options */
Thomas White's avatar
Thomas White committed
271
	const struct option longopts[] = {
272
		{"help",               0, NULL,               'h'},
273
		{"gpu",                0, &config_gpu,         1},
274
275
		{"random-orientation", 0, NULL,               'r'},
		{"number",             1, NULL,               'n'},
Thomas White's avatar
Thomas White committed
276
		{"no-images",          0, &config_noimages,    1},
Thomas White's avatar
Thomas White committed
277
		{"no-noise",           0, &config_nonoise,     1},
278
		{"intensities",        1, NULL,               'i'},
279
		{"symmetry",           1, NULL,               'y'},
280
		{"powder",             1, NULL,               'w'},
Thomas White's avatar
Thomas White committed
281
		{"gradients",          1, NULL,               't'},
282
		{"pdb",                1, NULL,               'p'},
283
		{"output",             1, NULL,               'o'},
Thomas White's avatar
Thomas White committed
284
		{"geometry",           1, NULL,               'g'},
285
		{"beam",               1, NULL,               'b'},
286
		{"really-random",      0, &config_random,      1},
Thomas White's avatar
Thomas White committed
287
		{"gpu-dev",            1, NULL,                2},
288
289
		{"min-size",           1, NULL,                3},
		{"max-size",           1, NULL,                4},
290
		{0, 0, NULL, 0}
Thomas White's avatar
Thomas White committed
291
	};
292

293
	/* Short options */
294
	while ((c = getopt_long(argc, argv, "hrn:i:t:p:o:g:b:y:",
Thomas White's avatar
Thomas White committed
295
	                        longopts, NULL)) != -1) {
296

Thomas White's avatar
Thomas White committed
297
		switch (c) {
Thomas White's avatar
Thomas White committed
298
		case 'h' :
Thomas White's avatar
Thomas White committed
299
300
			show_help(argv[0]);
			return 0;
301

Thomas White's avatar
Thomas White committed
302
		case 'r' :
303
304
305
			config_randomquat = 1;
			break;

Thomas White's avatar
Thomas White committed
306
		case 'n' :
307
308
309
310
311
			n_images = strtol(optarg, &rval, 10);
			if ( *rval != '\0' ) {
				ERROR("Invalid number of images.\n");
				return 1;
			}
312
313
			break;

Thomas White's avatar
Thomas White committed
314
		case 'i' :
315
316
317
			intfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
318
		case 't' :
319
320
321
			grad_str = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
322
		case 'p' :
323
324
325
			filename = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
326
		case 'o' :
327
328
329
			outfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
330
		case 'w' :
331
332
333
			powder_fn = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
334
		case 'g' :
Thomas White's avatar
Thomas White committed
335
336
337
			geometry = strdup(optarg);
			break;

338
339
340
341
		case 'b' :
			beamfile = strdup(optarg);
			break;

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

Thomas White's avatar
Thomas White committed
346
347
348
349
		case 2 :
			gpu_dev = atoi(optarg);
			break;

350
		case 3 :
351
352
353
354
355
			min_size = strtod(optarg, &rval);
			if ( *rval != '\0' ) {
				ERROR("Invalid minimum size.\n");
				return 1;
			}
356
357
358
359
			random_size++;
			break;

		case 4 :
360
361
362
363
364
			max_size = strtod(optarg, &rval);
			if ( *rval != '\0' ) {
				ERROR("Invalid maximum size.\n");
				return 1;
			}
365
366
367
			random_size++;
			break;

Thomas White's avatar
Thomas White committed
368
		case 0 :
Thomas White's avatar
Thomas White committed
369
			break;
370

Thomas White's avatar
Thomas White committed
371
		default :
Thomas White's avatar
Thomas White committed
372
373
			return 1;
		}
374
375
376

	}

377
	if ( config_random ) {
378
379
380
381
382
383
384
385
		FILE *fh;
		unsigned int seed;
		fh = fopen("/dev/urandom", "r");
		fread(&seed, sizeof(seed), 1, fh);
		fclose(fh);
		srand(seed);
	}

386
387
388
389
390
	if ( random_size == 1 ) {
		ERROR("You must specify both --min-size and --max-size.\n");
		return 1;
	}

391
392
393
394
	if ( filename == NULL ) {
		filename = strdup("molecule.pdb");
	}

395
396
397
398
399
400
401
402
	if ( outfile == NULL ) {
		if ( n_images == 1 ) {
			outfile = strdup("sim.h5");
		} else {
			outfile = strdup("sim");
		}
	}

Thomas White's avatar
Thomas White committed
403
404
405
	if ( sym_str == NULL ) sym_str = strdup("1");
	sym = get_pointgroup(sym_str);
	/* sym_str is used below */
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
	if ( grad_str == NULL ) {
		STATUS("You didn't specify a gradient calculation method, so"
		       " I'm using the 'mosaic' method, which is fastest.\n");
		grad = GRADIENT_MOSAIC;
	} else if ( strcmp(grad_str, "mosaic") == 0 ) {
		grad = GRADIENT_MOSAIC;
	} else if ( strcmp(grad_str, "interpolate") == 0) {
		grad = GRADIENT_INTERPOLATE;
	} else if ( strcmp(grad_str, "phased") == 0) {
		grad = GRADIENT_PHASED;
	} else {
		ERROR("Unrecognised gradient method '%s'\n", grad_str);
		return 1;
	}
	free(grad_str);

	if ( config_gpu && (grad != GRADIENT_MOSAIC) ) {
		ERROR("Only the mosaic method can be used for gradients when"
		      "calculating on the GPU.\n");
		return 1;
	}

Thomas White's avatar
Thomas White committed
429
430
431
432
433
	if ( geometry == NULL ) {
		ERROR("You need to specify a geometry file with --geometry\n");
		return 1;
	}

434
435
436
437
438
439
	if ( beamfile == NULL ) {
		ERROR("You need to specify a beam parameter file"
		      " with --beam\n");
		return 1;
	}

440
	if ( intfile == NULL ) {
441

442
443
444
445
446
447
		/* Gentle reminder */
		STATUS("You didn't specify the file containing the ");
		STATUS("reflection intensities (with --intensities).\n");
		STATUS("I'll simulate a flat intensity distribution.\n");
		intensities = NULL;
		phases = NULL;
448
449
		flags = NULL;

450
	} else {
451

Thomas White's avatar
Thomas White committed
452
453
454
		RefList *reflections;

		reflections = read_reflections(intfile);
Thomas White's avatar
Thomas White committed
455
456
457
458
		if ( reflections == NULL ) {
			ERROR("Problem reading input file %s\n", intfile);
			return 1;
		}
Thomas White's avatar
Thomas White committed
459
		free(intfile);
460

461
		if ( grad == GRADIENT_PHASED ) {
Thomas White's avatar
Thomas White committed
462
			phases = phases_from_list(reflections);
463
464
465
		} else {
			phases = NULL;
		}
Thomas White's avatar
Thomas White committed
466
467
468
		intensities = intensities_from_list(reflections);
		phases = phases_from_list(reflections);
		flags = flags_from_list(reflections);
469

Thomas White's avatar
Thomas White committed
470
		/* Check that the intensities have the correct symmetry */
Thomas White's avatar
Thomas White committed
471
		if ( check_list_symmetry(reflections, sym) ) {
Thomas White's avatar
Thomas White committed
472
			ERROR("The input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
473
			      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
474
475
476
			return 1;
		}

Thomas White's avatar
Thomas White committed
477
478
		reflist_free(reflections);

479
480
	}

481
482
483
484
485
486
487
	image.det = get_detector_geometry(geometry);
	if ( image.det == NULL ) {
		ERROR("Failed to read detector geometry from '%s'\n", geometry);
		return 1;
	}
	free(geometry);

488
489
490
491
492
493
494
	image.beam = get_beam_parameters(beamfile);
	if ( image.beam == NULL ) {
		ERROR("Failed to read beam parameters from '%s'\n", beamfile);
		return 1;
	}
	free(beamfile);

Thomas White's avatar
Thomas White committed
495
	/* Define image parameters */
Thomas White's avatar
Thomas White committed
496
497
	image.width = image.det->max_fs + 1;
	image.height = image.det->max_ss + 1;
498
	image.lambda = ph_en_to_lambda(eV_to_J(image.beam->photon_energy));
Thomas White's avatar
Thomas White committed
499
500
501
502

	/* Load unit cell */
	input_cell = load_cell_from_pdb(filename);
	if ( input_cell == NULL ) {
Thomas White's avatar
Thomas White committed
503
504
		exit(1);
	}
Thomas White's avatar
Thomas White committed
505
506

	/* Initialise stuff */
507
	image.filename = NULL;
Thomas White's avatar
Thomas White committed
508
509
	image.features = NULL;
	image.flags = NULL;
Thomas White's avatar
Thomas White committed
510

511
512
	powder = calloc(image.width*image.height, sizeof(*powder));

Thomas White's avatar
Thomas White committed
513
	/* Splurge a few useful numbers */
514
	STATUS("Wavelength is %f nm\n", image.lambda/1.0e-9);
Thomas White's avatar
Thomas White committed
515

516
517
	do {

Thomas White's avatar
Thomas White committed
518
		int na, nb, nc;
519
		double a, b, c, d;
Thomas White's avatar
Thomas White committed
520
		UnitCell *cell;
Thomas White's avatar
Thomas White committed
521

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
		if ( random_size ) {

			double alen, blen, clen, dis;

			cell_get_parameters(input_cell, &alen, &blen, &clen,
			                    &dis, &dis, &dis);

			na = random_ncells(alen, min_size, max_size);
			nb = random_ncells(blen, min_size, max_size);
			nc = random_ncells(clen, min_size, max_size);

		} else {

			na = 8;
			nb = 8;
			nc = 8;

		}
Thomas White's avatar
Thomas White committed
540

541
542
		/* Read quaternion from stdin */
		if ( config_randomquat ) {
Thomas White's avatar
Thomas White committed
543
			orientation = random_quaternion();
544
		} else {
Thomas White's avatar
Thomas White committed
545
			orientation = read_quaternion();
546
547
		}

Thomas White's avatar
Thomas White committed
548
		STATUS("Orientation is %5.3f %5.3f %5.3f %5.3f\n",
Thomas White's avatar
Thomas White committed
549
550
		       orientation.w, orientation.x,
		       orientation.y, orientation.z);
Thomas White's avatar
Thomas White committed
551

Thomas White's avatar
Thomas White committed
552
		if ( !quaternion_valid(orientation) ) {
553
			ERROR("Orientation modulus is not zero!\n");
554
555
556
			return 1;
		}

Thomas White's avatar
Thomas White committed
557
558
		cell = cell_rotate(input_cell, orientation);

559
560
561
		/* Ensure no residual information */
		image.data = NULL;
		image.twotheta = NULL;
562

563
		cell_get_parameters(cell, &a, &b, &c, &d, &d, &d);
Thomas White's avatar
Thomas White committed
564
565
		STATUS("Particle size = %i x %i x %i"
		       " ( = %5.2f x %5.2f x %5.2f nm)\n",
566
567
	               na, nb, nc, na*a/1.0e-9, nb*b/1.0e-9, nc*c/1.0e-9);

568
		if ( config_gpu ) {
569
			if ( gctx == NULL ) {
570
				gctx = setup_gpu(config_nosfac,
Thomas White's avatar
Thomas White committed
571
				                 intensities, flags, sym_str,
572
				                 gpu_dev);
573
			}
574
			get_diffraction_gpu(gctx, &image, na, nb, nc, cell);
575
		} else {
Thomas White's avatar
Thomas White committed
576
			get_diffraction(&image, na, nb, nc, intensities, phases,
Thomas White's avatar
Thomas White committed
577
			                flags, cell, grad, sym);
578
		}
579
		if ( image.data == NULL ) {
580
581
582
583
			ERROR("Diffraction calculation failed.\n");
			goto skip;
		}

584
		record_image(&image, !config_nonoise);
Thomas White's avatar
Thomas White committed
585

586
		if ( powder_fn != NULL ) {
587
588
589
590
591
592
593

			int x, y, w;

			w = image.width;

			for ( x=0; x<image.width; x++ ) {
			for ( y=0; y<image.height; y++ ) {
594
				powder[x+w*y] += (double)image.data[x+w*y];
595
596
597
598
			}
			}

			if ( !(ndone % 10) ) {
599
				hdf5_write(powder_fn, powder,
600
				           image.width, image.height,
601
				           H5T_NATIVE_DOUBLE);
602
603
604
			}
		}

605
606
607
608
		if ( !config_noimages ) {

			char filename[1024];

609
610
611
612
613
614
615
			if ( n_images != 1 ) {
				snprintf(filename, 1023, "%s-%i.h5",
				         outfile, number);
			} else {
				strncpy(filename, outfile, 1023);
			}

616
			number++;
Thomas White's avatar
Thomas White committed
617

618
619
			/* Write the output file */
			hdf5_write(filename, image.data,
620
			           image.width, image.height, H5T_NATIVE_FLOAT);
621
622

		}
623

624
625
626
		/* Clean up */
		free(image.data);
		free(image.twotheta);
Thomas White's avatar
Thomas White committed
627
		cell_free(cell);
Thomas White's avatar
Thomas White committed
628

629
skip:
Thomas White's avatar
Thomas White committed
630
		ndone++;
631

632
		if ( n_images && (ndone >= n_images) ) done = 1;
Thomas White's avatar
Thomas White committed
633

634
	} while ( !done );
635

636
637
638
639
640
641
	if ( powder_fn != NULL ) {
		hdf5_write(powder_fn, powder,
		           image.width, image.height,
		           H5T_NATIVE_DOUBLE);
	}

642
643
644
645
	if ( gctx != NULL ) {
		cleanup_gpu(gctx);
	}

Thomas White's avatar
Thomas White committed
646
647
	free(image.det->panels);
	free(image.det);
648
	free(image.beam);
649
	free(powder);
Thomas White's avatar
Thomas White committed
650
	cell_free(input_cell);
651
	free(intensities);
Thomas White's avatar
Thomas White committed
652
653
	free(outfile);
	free(filename);
Thomas White's avatar
Thomas White committed
654
655
	free(sym_str);
	free_symoplist(sym);
656

657
	return 0;
658
}