aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas White <taw@physics.org>2014-01-20 17:20:10 +0100
committerThomas White <taw@physics.org>2014-01-20 17:20:10 +0100
commit8e2f2f44f46c18f7bd621a2ef9a3d0aa813d76d9 (patch)
tree80f8b99b1d37ac8357aeb3298838fb995403e300
parent2304299259c55be3726929f5537ad2eed3155086 (diff)
pattern_sim: Overhaul and add SASE spectrum simulation
-rw-r--r--data/diffraction.cl83
-rw-r--r--libcrystfel/src/hdf5-file.c61
-rw-r--r--libcrystfel/src/image.h15
-rw-r--r--src/diffraction-gpu.c142
-rw-r--r--src/diffraction.c281
-rw-r--r--src/diffraction.h12
-rw-r--r--src/pattern_sim.c63
-rw-r--r--src/pattern_sim.h2
-rw-r--r--tests/gpu_sim_check.c11
9 files changed, 524 insertions, 146 deletions
diff --git a/data/diffraction.cl b/data/diffraction.cl
index 75933927..78cf1cf4 100644
--- a/data/diffraction.cl
+++ b/data/diffraction.cl
@@ -3,9 +3,27 @@
*
* GPU calculation kernel for truncated lattice diffraction
*
- * (c) 2006-2010 Thomas White <taw@physics.org>
+ * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
*
- * Part of CrystFEL - crystallography with a FEL
+ * Authors:
+ * 2009-2014 Thomas White <taw@physics.org>
+ * 2013 Alexandra Tolstikova
+ *
+ * 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/>.
*
*/
@@ -13,14 +31,13 @@
/* Maxmimum index to hold values up to (can be increased if necessary)
* WARNING: Altering this value constitutes an ABI change, and means you must
* update src/pattern_sim.h then recompile and reinstall everything. */
-#define INDMAX 120
+#define INDMAX 130
#define IDIM (INDMAX*2 +1)
#ifndef M_PI
#define M_PI ((float)(3.14159265))
#endif
-
const sampler_t sampler_a = CLK_NORMALIZED_COORDS_TRUE
| CLK_ADDRESS_REPEAT
| CLK_FILTER_LINEAR;
@@ -122,7 +139,7 @@ float molecule_factor(global float *intensities, global float *flags,
signed int i;
#ifdef FLAT_INTENSITIES
- return 1.0e5;
+ return 100.0;
#else
hf = cell.s0*q.x + cell.s1*q.y + cell.s2*q.z; /* h */
@@ -154,6 +171,15 @@ float molecule_factor(global float *intensities, global float *flags,
val += lookup_flagged_intensity(intensities, flags, h, k, -l);
#endif /* PGMMM */
+ #ifdef PG321H
+ val += lookup_flagged_intensity(intensities, flags, h, k, l);
+ val += lookup_flagged_intensity(intensities, flags, i, h, l);
+ val += lookup_flagged_intensity(intensities, flags, k, h, -l);
+ val += lookup_flagged_intensity(intensities, flags, k, i, l);
+ val += lookup_flagged_intensity(intensities, flags, i, k, -l);
+ val += lookup_flagged_intensity(intensities, flags, h, i, -l);
+ #endif /* PG321H */
+
#ifdef PG6
val += lookup_flagged_intensity(intensities, flags, h, k, l);
val += lookup_flagged_intensity(intensities, flags, i, h, l);
@@ -248,33 +274,36 @@ float molecule_factor(global float *intensities, global float *flags,
val += lookup_flagged_intensity(intensities, flags, -l, h, k);
#endif /* PGM3 */
+ /* FIXME: Add the remaining point groups */
+
return val;
#endif /* FLAT_INTENSITIIES */
}
-kernel void diffraction(global float *diff, float k,
+kernel void diffraction(global float *diff, float k, float weight,
int w, float corner_x, float corner_y,
+ float fsx, float fsy, float ssx, float ssy,
float res, float clen, float16 cell,
- global float *intensities,
+ global float *intensities, global float *flags,
read_only image2d_t func_a,
read_only image2d_t func_b,
read_only image2d_t func_c,
- global float *flags,
- float fsx, float fsy, float ssx, float ssy,
- float xo, float yo)
+ local float *tmp)
{
- float tt;
float fs, ss;
float f_lattice, I_lattice;
float I_molecule;
float4 q;
- float intensity;
- int idx;
+ const int ls0 = get_local_size(0);
+ const int ls1 = get_local_size(1);
+ const int li0 = get_local_id(0);
+ const int li1 = get_local_id(1);
+ const int ls = ls0 * ls1;
/* Calculate fractional coordinates in fs/ss */
- fs = convert_float(get_global_id(0)) + xo;
- ss = convert_float(get_global_id(1)) + yo;
+ fs = convert_float(get_global_id(0)) / convert_float(ls0);
+ ss = convert_float(get_global_id(1)) / convert_float(ls1);
/* Get the scattering vector */
q = get_q(fs, ss, res, clen, k,
@@ -285,7 +314,25 @@ kernel void diffraction(global float *diff, float k,
I_molecule = molecule_factor(intensities, flags, cell, q);
I_lattice = pow(f_lattice, 2.0f);
- /* Write the value to memory */
- idx = convert_int_rtz(fs) + w*convert_int_rtz(ss);
- diff[idx] = I_molecule * I_lattice;
+ tmp[li0 + ls0*li1] = I_molecule * I_lattice;
+
+ barrier(CLK_LOCAL_MEM_FENCE);
+
+ /* First thread in group sums the samples */
+ if ( li0 + li1 == 0 ) {
+
+ int i;
+ float sum = 0.0;
+ float val;
+ int idx;
+
+ idx = convert_int_rtz(fs) + w*convert_int_rtz(ss);
+
+ for ( i=0; i<ls; i++ ) sum += tmp[i];
+
+ val = weight * sum / convert_float(ls);
+ diff[idx] = val;
+
+ }
+
}
diff --git a/libcrystfel/src/hdf5-file.c b/libcrystfel/src/hdf5-file.c
index 95ceb9c1..ad9495b5 100644
--- a/libcrystfel/src/hdf5-file.c
+++ b/libcrystfel/src/hdf5-file.c
@@ -295,6 +295,8 @@ int hdf5_write_image(const char *filename, struct image *image)
herr_t r;
hsize_t size[2];
double lambda, eV;
+ double *arr;
+ int i;
fh = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
if ( fh < 0 ) {
@@ -362,11 +364,7 @@ int hdf5_write_image(const char *filename, struct image *image)
eV = ph_lambda_to_eV(image->lambda);
r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL,
H5S_ALL, H5P_DEFAULT, &eV);
- if ( r < 0 ) {
- H5Dclose(dh);
- H5Fclose(fh);
- return 1;
- }
+
H5Dclose(dh);
dh = H5Dcreate2(fh, "/LCLS/photon_wavelength_A", H5T_NATIVE_DOUBLE, sh,
@@ -383,6 +381,59 @@ int hdf5_write_image(const char *filename, struct image *image)
H5Fclose(fh);
return 1;
}
+
+ H5Dclose(dh);
+
+ arr = malloc(image->spectrum_size*sizeof(double));
+ if ( arr == NULL ) {
+ H5Fclose(fh);
+ return 1;
+ }
+ for ( i=0; i<image->spectrum_size; i++ ) {
+ arr[i] = 1.0e10/image->spectrum[i].k;
+ }
+
+ size[0] = image->spectrum_size;
+ sh = H5Screate_simple(1, size, NULL);
+
+ dh = H5Dcreate2(gh, "spectrum_wavelengths_A", H5T_NATIVE_DOUBLE, sh,
+ H5P_DEFAULT, H5S_ALL, H5P_DEFAULT);
+ if ( dh < 0 ) {
+ H5Fclose(fh);
+ return 1;
+ }
+ r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL,
+ H5S_ALL, H5P_DEFAULT, arr);
+ H5Dclose(dh);
+
+ for ( i=0; i<image->spectrum_size; i++ ) {
+ arr[i] = image->spectrum[i].weight;
+ }
+ dh = H5Dcreate2(gh, "spectrum_weights", H5T_NATIVE_DOUBLE, sh,
+ H5P_DEFAULT, H5S_ALL, H5P_DEFAULT);
+ if ( dh < 0 ) {
+ H5Fclose(fh);
+ return 1;
+ }
+ r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL,
+ H5S_ALL, H5P_DEFAULT, arr);
+
+ H5Dclose(dh);
+ free(arr);
+
+ size[0] = 1;
+ sh = H5Screate_simple(1, size, NULL);
+
+ dh = H5Dcreate2(gh, "number_of_samples", H5T_NATIVE_INT, sh,
+ H5P_DEFAULT, H5S_ALL, H5P_DEFAULT);
+ if ( dh < 0 ) {
+ H5Fclose(fh);
+ return 1;
+ }
+
+ r = H5Dwrite(dh, H5T_NATIVE_INT, H5S_ALL,
+ H5S_ALL, H5P_DEFAULT, &image->nsamples);
+
H5Dclose(dh);
H5Gclose(gh);
diff --git a/libcrystfel/src/image.h b/libcrystfel/src/image.h
index a1ac75a3..0c189cad 100644
--- a/libcrystfel/src/image.h
+++ b/libcrystfel/src/image.h
@@ -44,6 +44,10 @@
#include "crystal.h"
#include "index.h"
+typedef enum {
+ SPECTRUM_TOPHAT,
+ SPECTRUM_SASE
+} SpectrumType;
/* Structure describing a feature in an image */
struct imagefeature {
@@ -67,6 +71,13 @@ struct imagefeature {
/* An opaque type representing a list of image features */
typedef struct _imagefeaturelist ImageFeatureList;
+/* Structure describing a wavelength sample from a spectrum */
+struct sample
+{
+ double k;
+ double weight;
+};
+
/**
* image:
@@ -144,6 +155,10 @@ struct image {
int id; /* ID number of the thread
* handling this image */
+ struct sample *spectrum;
+ int nsamples; /* Number of wavelengths */
+ int spectrum_size; /* Size of "spectrum" */
+
/* Per-shot radiation values */
double lambda; /* Wavelength in m */
double div; /* Divergence in radians */
diff --git a/src/diffraction-gpu.c b/src/diffraction-gpu.c
index f41a45ec..ff4f2414 100644
--- a/src/diffraction-gpu.c
+++ b/src/diffraction-gpu.c
@@ -3,11 +3,13 @@
*
* Calculate diffraction patterns by Fourier methods (GPU version)
*
- * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
+ * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2012 Thomas White <taw@physics.org>
+ * 2009-2014 Thomas White <taw@physics.org>
+ * 2013 Alexandra Tolstikova
+ * 2013-2014 Chun Hong Yoon <chun.hong.yoon@desy.de>
*
* This file is part of CrystFEL.
*
@@ -168,18 +170,20 @@ static int set_arg_mem(struct gpu_context *gctx, int idx, cl_mem val)
static void do_panels(struct gpu_context *gctx, struct image *image,
- int *n_inf, int *n_neg, int *n_nan, double k, double frac,
- double xo, double yo)
+ double k, double weight,
+ int *n_inf, int *n_neg, int *n_nan)
{
int i;
+ const int sampling = 4; /* This, squared, number of samples / pixel */
if ( set_arg_float(gctx, 1, k) ) return;
+ if ( set_arg_float(gctx, 2, weight) ) return;
/* Iterate over panels */
for ( i=0; i<image->det->n_panels; i++ ) {
size_t dims[2];
- size_t ldims[2] = {1, 1};
+ size_t ldims[2];
struct panel *p;
cl_mem diff;
size_t diff_size;
@@ -203,20 +207,29 @@ static void do_panels(struct gpu_context *gctx, struct image *image,
}
if ( set_arg_mem(gctx, 0, diff) ) return;
- if ( set_arg_int(gctx, 2, pan_width) ) return;
- if ( set_arg_float(gctx, 3, p->cnx) ) return;
- if ( set_arg_float(gctx, 4, p->cny) ) return;
- if ( set_arg_float(gctx, 5, p->res) ) return;
- if ( set_arg_float(gctx, 6, p->clen) ) return;
- if ( set_arg_float(gctx, 13, p->fsx) ) return;
- if ( set_arg_float(gctx, 14, p->fsy) ) return;
- if ( set_arg_float(gctx, 15, p->ssx) ) return;
- if ( set_arg_float(gctx, 16, p->ssy) ) return;
- if ( set_arg_float(gctx, 17, xo) ) return;
- if ( set_arg_float(gctx, 18, yo) ) return;
-
- dims[0] = pan_width;
- dims[1] = pan_height;
+
+ if ( set_arg_int(gctx, 3, pan_width) ) return;
+ if ( set_arg_float(gctx, 4, p->cnx) ) return;
+ if ( set_arg_float(gctx, 5, p->cny) ) return;
+ if ( set_arg_float(gctx, 6, p->fsx) ) return;
+ if ( set_arg_float(gctx, 7, p->fsy) ) return;
+ if ( set_arg_float(gctx, 8, p->ssx) ) return;
+ if ( set_arg_float(gctx, 9, p->ssy) ) return;
+ if ( set_arg_float(gctx, 10, p->res) ) return;
+ if ( set_arg_float(gctx, 11, p->clen) ) return;
+
+ dims[0] = pan_width * sampling;
+ dims[1] = pan_height * sampling;
+
+ ldims[0] = sampling;
+ ldims[1] = sampling;
+
+ err = clSetKernelArg(gctx->kern, 18,
+ sampling*sampling*sizeof(cl_float), NULL);
+ if ( err != CL_SUCCESS ) {
+ ERROR("Couldn't set local memory: %s\n", clError(err));
+ return;
+ }
err = clEnqueueNDRangeKernel(gctx->cq, gctx->kern, 2, NULL,
dims, ldims, 0, NULL, NULL);
@@ -250,7 +263,7 @@ static void do_panels(struct gpu_context *gctx, struct image *image,
tfs = p->min_fs + fs;
tss = p->min_ss + ss;
- image->data[tfs + image->width*tss] += frac*val;
+ image->data[tfs + image->width*tss] += val;
}
}
@@ -276,78 +289,54 @@ void get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
int n_neg = 0;
int n_nan = 0;
int fs, ss;
- const int nkstep = 10;
- const int nxs = 4;
- const int nys = 4;
- int kstep;
- double klow, khigh, kinc, w;
- double frac;
- int xs, ys;
- int n, ntot;
+ int i;
if ( gctx == NULL ) {
ERROR("GPU setup failed.\n");
return;
}
- cell_get_cartesian(ucell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
- cell.s[0] = ax; cell.s[1] = ay; cell.s[2] = az;
- cell.s[3] = bx; cell.s[4] = by; cell.s[5] = bz;
- cell.s[6] = cx; cell.s[7] = cy; cell.s[8] = cz;
-
/* Ensure all required LUTs are available */
check_sinc_lut(gctx, na);
check_sinc_lut(gctx, nb);
check_sinc_lut(gctx, nc);
- if ( set_arg_mem(gctx, 8, gctx->intensities) ) return;
- if ( set_arg_mem(gctx, 9, gctx->sinc_luts[na-1]) ) return;
- if ( set_arg_mem(gctx, 10, gctx->sinc_luts[nb-1]) ) return;
- if ( set_arg_mem(gctx, 11, gctx->sinc_luts[nc-1]) ) return;
- if ( set_arg_mem(gctx, 12, gctx->flags) ) return;
-
/* Unit cell */
- err = clSetKernelArg(gctx->kern, 7, sizeof(cl_float16), &cell);
+ cell_get_cartesian(ucell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
+ cell.s[0] = ax; cell.s[1] = ay; cell.s[2] = az;
+ cell.s[3] = bx; cell.s[4] = by; cell.s[5] = bz;
+ cell.s[6] = cx; cell.s[7] = cy; cell.s[8] = cz;
+
+ err = clSetKernelArg(gctx->kern, 12, sizeof(cl_float16), &cell);
if ( err != CL_SUCCESS ) {
ERROR("Couldn't set unit cell: %s\n", clError(err));
return;
}
+ if ( set_arg_mem(gctx, 13, gctx->intensities) ) return;
+ if ( set_arg_mem(gctx, 14, gctx->flags) ) return;
+ if ( set_arg_mem(gctx, 15, gctx->sinc_luts[na-1]) ) return;
+ if ( set_arg_mem(gctx, 16, gctx->sinc_luts[nb-1]) ) return;
+ if ( set_arg_mem(gctx, 17, gctx->sinc_luts[nc-1]) ) return;
+
/* Allocate memory for the result */
image->data = calloc(image->width * image->height, sizeof(float));
- w = image->lambda * image->bw;
- klow = 1.0 / (image->lambda + (w/2.0)); /* Smallest k */
- khigh = 1.0 / (image->lambda - (w/2.0)); /* Largest k */
- kinc = (khigh-klow) / (nkstep+1);
-
- ntot = nkstep * nxs * nys;
- frac = 1.0 / ntot;
-
- n = 0;
- for ( xs=0; xs<nxs; xs++ ) {
- for ( ys=0; ys<nys; ys++ ) {
-
- double xo, yo;
-
- xo = (1.0/nxs) * xs;
- yo = (1.0/nys) * ys;
+ double tot = 0.0;
+ for ( i=0; i<image->nsamples; i++ ) {
- for ( kstep=0; kstep<nkstep; kstep++ ) {
+ printf("%.1f eV, weight = %.5f\n",
+ ph_lambda_to_eV(1.0/image->spectrum[i].k),
+ image->spectrum[i].weight);
- double k;
+ do_panels(gctx, image, image->spectrum[i].k,
+ image->spectrum[i].weight,
+ &n_inf, &n_neg, &n_nan);
- k = klow + kstep*kinc;
+ tot += image->spectrum[i].weight;
- do_panels(gctx, image, &n_inf, &n_neg, &n_nan, k, frac,
- xo, yo);
-
- n++;
- progress_bar(n, ntot, "Simulating");
-
- }
- }
}
+ printf("total weight = %f\n", tot);
if ( n_neg + n_inf + n_nan ) {
ERROR("WARNING: The GPU calculation produced %i negative"
@@ -360,16 +349,9 @@ void get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
for ( fs=0; fs<image->width; fs++ ) {
for ( ss=0; ss<image->height; ss++ ) {
- double twotheta, k;
- int idx;
-
- /* Calculate k this time round */
- k = 1.0/image->lambda;
-
- get_q(image, fs, ss, &twotheta, k);
-
- idx = fs + image->width*ss;
- image->twotheta[idx] = twotheta;
+ double twotheta;
+ get_q(image, fs, ss, &twotheta, 1.0/image->lambda);
+ image->twotheta[fs + image->width*ss] = twotheta;
}
}
@@ -437,7 +419,7 @@ struct gpu_context *setup_gpu(int no_sfac,
}
} else {
for ( i=0; i<IDIM*IDIM*IDIM; i++ ) {
- intensities_ptr[i] = 1e5;
+ intensities_ptr[i] = 100.0; /* Does nothing */
}
strncat(cflags, "-DFLAT_INTENSITIES ", 511-strlen(cflags));
}
@@ -468,6 +450,8 @@ struct gpu_context *setup_gpu(int no_sfac,
strncat(cflags, "-DPG23 ", 511-strlen(cflags));
} else if ( strcmp(sym, "m-3") == 0 ) {
strncat(cflags, "-DPGM3 ", 511-strlen(cflags));
+ } else if ( strcmp(sym, "321_H") == 0 ) {
+ strncat(cflags, "-DPG321H ", 511-strlen(cflags));
} else {
ERROR("Sorry! Point group '%s' is not currently"
" supported on the GPU."
diff --git a/src/diffraction.c b/src/diffraction.c
index 2f764987..826aac77 100644
--- a/src/diffraction.c
+++ b/src/diffraction.c
@@ -3,11 +3,13 @@
*
* Calculate diffraction patterns by Fourier methods
*
- * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
+ * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
*
* Authors:
- * 2009-2012 Thomas White <taw@physics.org>
+ * 2009-2014 Thomas White <taw@physics.org>
+ * 2013-2014 Chun Hong Yoon <chun.hong.yoon@desy.de>
+ * 2013 Alexandra Tolstikova
*
* This file is part of CrystFEL.
*
@@ -325,7 +327,7 @@ static double molecule_factor(const double *intensities, const double *phases,
ld = q.u * cx + q.v * cy + q.w * cz;
/* No flags -> flat intensity distribution */
- if ( flags == NULL ) return 1.0e5;
+ if ( flags == NULL ) return 100.0;
switch ( m ) {
@@ -358,18 +360,250 @@ static double molecule_factor(const double *intensities, const double *phases,
}
+
+static void diffraction_at_k(struct image *image, const double *intensities,
+ const double *phases, const unsigned char *flags,
+ UnitCell *cell, GradientMethod m,
+ const SymOpList *sym, double k,
+ double ax, double ay, double az,
+ double bx, double by, double bz,
+ double cx, double cy, double cz,
+ double *lut_a, double *lut_b, double *lut_c,
+ double weight)
+{
+ unsigned int fs, ss;
+ const int nxs = 4;
+ const int nys = 4;
+
+ weight /= nxs*nys;
+
+ for ( fs=0; fs<image->width; fs++ ) {
+ for ( ss=0; ss<image->height; ss++ ) {
+
+ int idx;
+ double f_lattice, I_lattice;
+ double I_molecule;
+ struct rvec q;
+ double twotheta;
+ int xs, ys;
+ float xo, yo;
+
+ for ( xs=0; xs<nxs; xs++ ) {
+ for ( ys=0; ys<nys; ys++ ) {
+
+ xo = (1.0/nxs) * xs;
+ yo = (1.0/nys) * ys;
+
+ q = get_q(image, fs+xo, ss+yo, &twotheta, k);
+
+ f_lattice = lattice_factor(q, ax, ay, az,
+ bx, by, bz,
+ cx, cy, cz,
+ lut_a, lut_b, lut_c);
+
+ I_molecule = molecule_factor(intensities,
+ phases, flags, q,
+ ax, ay, az,
+ bx, by, bz,
+ cx, cy, cz,
+ m, sym);
+
+ I_lattice = pow(f_lattice, 2.0);
+
+ idx = fs + image->width*ss;
+ image->data[idx] += I_lattice * I_molecule * weight;
+ image->twotheta[idx] = twotheta;
+
+ }
+ }
+ }
+ progress_bar(fs, image->width-1, "Calculating diffraction");
+ }
+}
+
+
+static int compare_samples(const void *a, const void *b)
+{
+ struct sample *sample1 = (struct sample *)a;
+ struct sample *sample2 = (struct sample *)b;
+ if ( sample1->weight < sample2->weight ) {
+ return 1;
+ }
+ return -1;
+}
+
+
+static struct sample *get_gaussian_spectrum(double eV_cen, double eV_step,
+ double sigma, int spec_size)
+{
+ struct sample *spectrum;
+ int i;
+ double eV;
+
+ spectrum = malloc(spec_size * sizeof(struct sample));
+ if ( spectrum == NULL ) return NULL;
+
+ eV = eV_cen - (spec_size/2)*eV_step;
+ for ( i=0; i<spec_size; i++ ) {
+
+ spectrum[i].k = 1.0/ph_eV_to_lambda(eV);
+ spectrum[i].weight = exp(-(pow(eV - eV_cen, 2.0)
+ / (2.0*sigma*sigma)));
+ eV += eV_step;
+ }
+
+ return spectrum;
+}
+
+
+static int add_sase_noise(struct sample *spectrum, int nsteps)
+{
+ struct sample *noise;
+ int i, j;
+ double *gaussianNoise;
+ int shiftLim = 5;
+ double noise_mean = 0.0;
+ double noise_sigma = 1.0;
+
+ if ( shiftLim > nsteps ) shiftLim = nsteps;
+
+ noise = malloc(nsteps * sizeof(struct sample));
+ if ( noise == NULL ) return 1;
+
+ gaussianNoise = malloc(3 * nsteps * sizeof(double));
+ if ( gaussianNoise == NULL ) {
+ free(noise);
+ return 1;
+ }
+
+ /* Generate Gaussian noise of length of spectrum
+ * (replicate on both ends for circshift below) */
+ for ( i=0; i<nsteps; i++) {
+
+ noise[i].weight = 0.0;
+
+ /* Gaussian noise with mean = 0, std = 1 */
+ gaussianNoise[i] = gaussian_noise(noise_mean, noise_sigma);
+ gaussianNoise[i+nsteps] = gaussianNoise[i];
+ gaussianNoise[i+2*nsteps] = gaussianNoise[i];
+ }
+
+ /* Sum Gaussian noise by circshifting by +/- shiftLim */
+ for ( i=nsteps; i<2*nsteps; i++ ) {
+ for ( j=-shiftLim; j<=shiftLim; j++ ) {
+ noise[i-nsteps].weight += gaussianNoise[i+j];
+ }
+ }
+
+ /* Normalize the number of circshift sum */
+ for ( i=0; i<nsteps; i++) {
+ noise[i].weight = noise[i].weight/(2*shiftLim+1);
+ }
+
+ /* Noise amplitude should have a 2 x Gaussian distribution */
+ for ( i=0; i<nsteps; i++ ) {
+ noise[i].weight = 2.0 * spectrum[i].weight * noise[i].weight;
+ }
+
+ /* Add noise to spectrum */
+ for ( i=0; i<nsteps; i++ ) {
+
+ spectrum[i].weight += noise[i].weight;
+
+ /* The final spectrum can not be negative */
+ if ( spectrum[i].weight < 0.0 ) spectrum[i].weight = 0.0;
+
+ }
+
+ return 0;
+}
+
+
+struct sample *generate_tophat(struct image *image)
+{
+ struct sample *spectrum;
+ int i;
+ double k, k_step;
+
+ double halfwidth = image->bw * image->lambda / 2.0; /* m */
+ double mink = 1.0/(image->lambda + halfwidth);
+ double maxk = 1.0/(image->lambda - halfwidth);
+
+ spectrum = malloc(image->nsamples * sizeof(struct sample));
+ if ( spectrum == NULL ) return NULL;
+
+ k = mink;
+ k_step = (maxk-mink)/(image->nsamples-1);
+ for ( i=0; i<image->nsamples; i++ ) {
+ spectrum[i].k = k;
+ spectrum[i].weight = 1.0/(double)image->nsamples;
+ k += k_step;
+ }
+
+ image->spectrum_size = image->nsamples;
+
+ return spectrum;
+}
+
+
+struct sample *generate_SASE(struct image *image)
+{
+ struct sample *spectrum;
+ int i;
+ const int spec_size = 1024;
+ double eV_cen; /* Central photon energy for this spectrum */
+ const double jitter_sigma_eV = 8.0;
+
+ /* Central wavelength jitters with Gaussian distribution */
+ eV_cen = gaussian_noise(ph_lambda_to_eV(image->lambda),
+ jitter_sigma_eV);
+
+ /* Convert FWHM to standard deviation. Note that bandwidth is taken to
+ * be "delta E over E" (E = photon energy), not the bandwidth in terms
+ * of wavelength, but the difference should be very small */
+ double sigma = (image->bw*eV_cen) / (2.0*sqrt(2.0*log(2.0)));
+
+ /* The spectrum will be calculated to a resolution which spreads six
+ * sigmas of the original (no SASE noise) Gaussian pulse over spec_size
+ * points */
+ double eV_step = 6.0*sigma/(spec_size-1);
+
+ spectrum = get_gaussian_spectrum(eV_cen, eV_step, sigma, spec_size);
+
+ /* Add SASE-type noise to Gaussian spectrum */
+ add_sase_noise(spectrum, spec_size);
+
+ /* Normalise intensity (before taking restricted number of samples) */
+ double total_weight = 0.0;
+ for ( i=0; i<spec_size; i++ ) {
+ total_weight += spectrum[i].weight;
+ }
+ for ( i=0; i<spec_size; i++ ) {
+ spectrum[i].weight /= total_weight;
+ }
+
+ /* Sort samples in spectrum by weight. Diffraction calculation will
+ * take the requested number, starting from the brightest */
+ qsort(spectrum, spec_size, sizeof(struct sample), compare_samples);
+
+ image->spectrum_size = spec_size;
+
+ return spectrum;
+}
+
+
void get_diffraction(struct image *image, int na, int nb, int nc,
const double *intensities, const double *phases,
const unsigned char *flags, UnitCell *cell,
GradientMethod m, const SymOpList *sym)
{
- unsigned int fs, ss;
double ax, ay, az;
double bx, by, bz;
double cx, cy, cz;
double *lut_a;
double *lut_b;
double *lut_c;
+ int i;
cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
@@ -383,39 +617,18 @@ void get_diffraction(struct image *image, int na, int nb, int nc,
lut_b = get_sinc_lut(nb);
lut_c = get_sinc_lut(nc);
- for ( fs=0; fs<image->width; fs++ ) {
- for ( ss=0; ss<image->height; ss++ ) {
+ for ( i=0; i<image->nsamples; i++ ) {
- int idx;
- double k;
- double f_lattice, I_lattice;
- double I_molecule;
- struct rvec q;
- double twotheta;
+ printf("%.1f eV, weight = %.5f\n",
+ ph_lambda_to_eV(1.0/image->spectrum[i].k),
+ image->spectrum[i].weight);
- /* Calculate k this time round */
- k = 1.0/image->lambda;
+ diffraction_at_k(image, intensities, phases,
+ flags, cell, m, sym, image->spectrum[i].k,
+ ax, ay, az, bx, by, bz, cx, cy, cz,
+ lut_a, lut_b, lut_c, image->spectrum[i].weight);
- q = get_q(image, fs, ss, &twotheta, k);
- f_lattice = lattice_factor(q, ax, ay, az,
- bx, by, bz,
- cx, cy, cz,
- lut_a, lut_b, lut_c);
-
- I_molecule = molecule_factor(intensities,
- phases, flags, q,
- ax,ay,az,bx,by,bz,cx,cy,cz,
- m, sym);
-
- I_lattice = pow(f_lattice, 2.0);
-
- idx = fs + image->width*ss;
- image->data[idx] = I_lattice * I_molecule;
- image->twotheta[idx] = twotheta;
-
- }
- progress_bar(fs, image->width-1, "Calculating diffraction");
}
free(lut_a);
diff --git a/src/diffraction.h b/src/diffraction.h
index b7e34a0e..a903346e 100644
--- a/src/diffraction.h
+++ b/src/diffraction.h
@@ -3,11 +3,13 @@
*
* Calculate diffraction patterns by Fourier methods
*
- * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
+ * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
*
* Authors:
- * 2009-2012 Thomas White <taw@physics.org>
+ * 2009-2014 Thomas White <taw@physics.org>
+ * 2013-2014 Chun Hong Yoon <chun.hong.yoon@desy.de>
+ * 2013 Alexandra Tolstikova
*
* This file is part of CrystFEL.
*
@@ -48,4 +50,8 @@ extern void get_diffraction(struct image *image, int na, int nb, int nc,
const unsigned char *flags, UnitCell *cell,
GradientMethod m, const SymOpList *sym);
+extern struct sample *generate_tophat(struct image *image);
+
+extern struct sample *generate_SASE(struct image *image);
+
#endif /* DIFFRACTION_H */
diff --git a/src/pattern_sim.c b/src/pattern_sim.c
index 606a173c..81a94c37 100644
--- a/src/pattern_sim.c
+++ b/src/pattern_sim.c
@@ -110,6 +110,15 @@ static void show_help(const char *s)
" --max-size=<s> Use <s> as the maximum crystal size in nm.\n"
" --min-size is also required.\n"
" --no-noise Do not calculate Poisson noise.\n"
+" -s, --sample-spectrum=<N> Use N samples from spectrum. Default 3.\n"
+" -x, --spectrum=<type> Use <type> for the calculation of spectrum.\n"
+" Choose from:\n"
+" tophat : Tophat spectrum. Bandwidth is\n"
+" taken from beam parameters.\n"
+" SASE : SASE spectrum. Random SASE pulse \n"
+" is generated from a model.\n"
+" Bandwidth is taken from beam \n"
+" parameters.\n"
);
}
@@ -253,7 +262,9 @@ int main(int argc, char *argv[])
char *outfile = NULL;
char *geometry = NULL;
char *beamfile = NULL;
+ char *spectrum_str = NULL;
GradientMethod grad;
+ SpectrumType spectrum_type;
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 */
@@ -266,6 +277,7 @@ int main(int argc, char *argv[])
double max_size = 0.0;
char *sym_str = NULL;
SymOpList *sym;
+ int nsamples = 3;
/* Long options */
const struct option longopts[] = {
@@ -283,6 +295,9 @@ int main(int argc, char *argv[])
{"output", 1, NULL, 'o'},
{"geometry", 1, NULL, 'g'},
{"beam", 1, NULL, 'b'},
+ {"sample-spectrum", 1, NULL, 's'},
+ {"type-spectrum", 1, NULL, 'x'},
+ {"spectrum", 1, NULL, 'x'},
{"really-random", 0, &config_random, 1},
{"gpu-dev", 1, NULL, 2},
{"min-size", 1, NULL, 3},
@@ -291,7 +306,7 @@ int main(int argc, char *argv[])
};
/* Short options */
- while ((c = getopt_long(argc, argv, "hrn:i:t:p:o:g:b:y:",
+ while ((c = getopt_long(argc, argv, "hrn:i:t:p:o:g:b:y:s:x:",
longopts, NULL)) != -1) {
switch (c) {
@@ -344,6 +359,18 @@ int main(int argc, char *argv[])
sym_str = strdup(optarg);
break;
+ case 's' :
+ nsamples = strtol(optarg, &rval, 10);
+ if ( *rval != '\0' ) {
+ ERROR("Invalid number of spectrum samples.\n");
+ return 1;
+ }
+ break;
+
+ case 'x' :
+ spectrum_str = strdup(optarg);
+ break;
+
case 2 :
gpu_dev = atoi(optarg);
break;
@@ -443,6 +470,20 @@ int main(int argc, char *argv[])
return 1;
}
+ if ( spectrum_str == NULL ) {
+ STATUS("You didn't specify a spectrum type, so"
+ " I'm using a 'tophat' spectrum.\n");
+ spectrum_type = SPECTRUM_TOPHAT;
+ } else if ( strcmp(spectrum_str, "tophat") == 0) {
+ spectrum_type = SPECTRUM_TOPHAT;
+ } else if ( strcmp(spectrum_str, "SASE") == 0) {
+ spectrum_type = SPECTRUM_SASE;
+ } else {
+ ERROR("Unrecognised spectrum type '%s'\n", spectrum_str);
+ return 1;
+ }
+ free(spectrum_str);
+
if ( intfile == NULL ) {
/* Gentle reminder */
@@ -504,12 +545,13 @@ int main(int argc, char *argv[])
ERROR("Photon energy must be specified, not taken from the"
" HDF5 file. Please alter %s accordingly.\n", beamfile)
return 1;
- } else {
- double wl = ph_en_to_lambda(eV_to_J(image.beam->photon_energy));
- image.lambda = wl;
}
+
+ double wl = ph_en_to_lambda(eV_to_J(image.beam->photon_energy));
+ image.lambda = wl;
image.bw = image.beam->bandwidth;
image.div = image.beam->divergence;
+ image.nsamples = nsamples;
free(beamfile);
/* Load unit cell */
@@ -571,6 +613,18 @@ int main(int argc, char *argv[])
cell = cell_rotate(input_cell, orientation);
+ switch ( spectrum_type ) {
+
+ case SPECTRUM_TOPHAT :
+ image.spectrum = generate_tophat(&image);
+ break;
+
+ case SPECTRUM_SASE :
+ image.spectrum = generate_SASE(&image);
+ break;
+
+ }
+
/* Ensure no residual information */
image.data = NULL;
image.twotheta = NULL;
@@ -638,6 +692,7 @@ int main(int argc, char *argv[])
/* Clean up */
free(image.data);
free(image.twotheta);
+
cell_free(cell);
skip:
diff --git a/src/pattern_sim.h b/src/pattern_sim.h
index 61bd87d0..4269ea90 100644
--- a/src/pattern_sim.h
+++ b/src/pattern_sim.h
@@ -37,7 +37,7 @@
/* Maxmimum index to hold values up to (can be increased if necessary)
* WARNING: Altering this value constitutes an ABI change, and means you must
* update data/diffraction.cl then recompile and reinstall everything. */
-#define INDMAX 120
+#define INDMAX 130
/* Array size */
#define IDIM (INDMAX*2 +1)
diff --git a/tests/gpu_sim_check.c b/tests/gpu_sim_check.c
index 5c45ea02..c183a2a2 100644
--- a/tests/gpu_sim_check.c
+++ b/tests/gpu_sim_check.c
@@ -142,14 +142,21 @@ int main(int argc, char *argv[])
beam = calloc(1, sizeof(struct beam_params));
beam->fluence = 1.0e15; /* Does nothing */
beam->beam_radius = 1.0e-6;
- beam->photon_energy = 9000.0;
- beam->bandwidth = 0.1 / 100.0;
+ beam->photon_energy = 6000.0;
+ beam->bandwidth = 1.0 / 100.0;
beam->divergence = 0.0;
cpu_image.beam = beam;
gpu_image.beam = beam;
cpu_image.lambda = ph_en_to_lambda(eV_to_J(beam->photon_energy));
gpu_image.lambda = ph_en_to_lambda(eV_to_J(beam->photon_energy));
+ cpu_image.bw = beam->bandwidth;
+ gpu_image.bw = beam->bandwidth;
+
+ cpu_image.nsamples = 10;
+ gpu_image.nsamples = 10;
+ cpu_image.spectrum = generate_tophat(&cpu_image);
+ gpu_image.spectrum = generate_tophat(&gpu_image);
start = get_hires_seconds();
get_diffraction_gpu(gctx, &gpu_image, 8, 8, 8, cell);