/* * sfac.c * * Scattering factors * * (c) 2006-2010 Thomas White * * Part of CrystFEL - crystallography with a FEL * */ #include #include #include #include #include #include "utils.h" #include "sfac.h" #define N_MEMO 1024 /* Look up f1 and f2 for this atom at this energy (in J/photon) */ static double complex get_f1f2(const char *n, double en) { FILE *fh; char filename[64]; char line[1024]; char *rval; double last_E, last_f1, last_f2; static char *memo_n[N_MEMO]; static int memo_eV[N_MEMO]; static double complex memo_res[N_MEMO]; static int n_memo = 0; int eV; int i; eV = (int)rint(J_to_eV(en)); for ( i=0; in_species; i++ ) { struct mol_species *spec; int j; spec = mol->species[i]; for ( j=0; jn_atoms; j++ ) { double sfac = get_waas_kirf(spec->species, 0.0); tx += spec->x[j] * sfac; ty += spec->y[j] * sfac; tz += spec->z[j] * sfac; total += sfac; } } mol->xc = tx / total; mol->yc = ty / total; mol->zc = tz / total; for ( i=0; in_species; i++ ) { struct mol_species *spec; int j; spec = mol->species[i]; for ( j=0; jn_atoms; j++ ) { spec->x[j] -= mol->xc; spec->y[j] -= mol->yc; spec->z[j] -= mol->zc; } } //STATUS("Molecule was shifted by %5.3f, %5.3f, %5.3f nm\n", // mol->xc*1e9, mol->yc*1e9, mol->zc*1e9); } /* Load PDB file into a memory format suitable for efficient(ish) structure * factor calculation */ struct molecule *load_molecule() { struct molecule *mol; FILE *fh; char line[1024]; char *rval; int i; mol = malloc(sizeof(struct molecule)); if ( mol == NULL ) return NULL; mol->n_species = 0; mol->reflections = NULL; mol->cell = NULL; fh = fopen("molecule.pdb", "r"); if ( fh == NULL ) { ERROR("Couldn't open PDB file\n"); return NULL; } do { char el[4]; int j, r; int done = 0; float xf, yf, zf, occf, Bf; double x, y, z, occ, B; char *coords; rval = fgets(line, 1023, fh); if ( strncmp(line, "CRYST1", 6) == 0 ) { float a, b, c, al, be, ga; r = sscanf(line+7, "%f %f %f %f %f %f", &a, &b, &c, &al, &be, &ga); mol->cell = cell_new_from_parameters(a*1e-10, b*1e-10, c*1e-10, deg2rad(al), deg2rad(be), deg2rad(ga)); } /* Only interested in atoms */ if ( strncmp(line, "HETATM", 6) != 0 ) continue; /* The following crimes against programming style * were brought to you by Wizbit Enterprises, Inc. */ if ( line[76] == ' ' ) { el[0] = line[77]; el[1] = '\0'; } else { el[0] = line[76]; el[1] = line[77]; el[2] = '\0'; } coords = line + 29; r = sscanf(coords, "%f %f %f %f %f", &xf, &yf, &zf, &occf, &Bf); if ( r != 5 ) { STATUS("I didn't understand a line in the PDB file.\n"); continue; } /* Promote to double precision */ x = xf; y = yf; z = zf; occ = occf; B = Bf; for ( j=0; jn_species; j++ ) { struct mol_species *spec; int n; spec = mol->species[j]; if ( strcmp(spec->species, el) != 0 ) continue; n = mol->species[j]->n_atoms; spec->x[n] = x*1.0e-10; /* Convert to m */ spec->y[n] = y*1.0e-10; spec->z[n] = z*1.0e-10; spec->occ[n] = occ; spec->B[n] = B*1.0e-20; /* Convert to m^2 */ mol->species[j]->n_atoms++; done = 1; } if ( !done ) { /* Need to create record for this species */ struct mol_species *spec; spec = malloc(sizeof(struct mol_species)); memcpy(spec->species, el, 4); spec->x[0] = x*1.0e-10; /* Convert to m */ spec->y[0] = y*1.0e-10; spec->z[0] = z*1.0e-10; spec->occ[0] = occ; spec->B[0] = B*1.0e-20; /* Convert to m^2 */ spec->n_atoms = 1; mol->species[mol->n_species] = spec; mol->n_species++; } } while ( rval != NULL ); fclose(fh); centre_molecule(mol); //STATUS("There are %i species\n", mol->n_species); for ( i=0; in_species; i++ ) { // STATUS("%3s : %6i\n", mol->species[i]->species, // mol->species[i]->n_atoms); } if ( mol->cell == NULL ) { ERROR("No unit cell found in PDB file\n"); return NULL; } return mol; } void free_molecule(struct molecule *mol) { int i; for ( i=0; in_species; i++ ) { free(mol->species[i]); } free(mol->cell); free(mol); } struct molecule *copy_molecule(struct molecule *orig) { struct molecule *new; int i; new = malloc(sizeof(*new)); *new = *orig; /* Now sort out pointers */ for ( i=0; in_species; i++ ) { new->species[i] = malloc(sizeof(struct mol_species)); memcpy(new->species[i], orig->species[i], sizeof(struct mol_species)); } new->cell = cell_new_from_cell(orig->cell); new->reflections = NULL; return new; } double complex *get_reflections(struct molecule *mol, double en) { double complex *reflections; double asx, asy, asz; double bsx, bsy, bsz; double csx, csy, csz; signed int h, k, l; const int do_thermal = 1; cell_get_reciprocal(mol->cell, &asx, &asy, &asz, &bsx, &bsy, &bsz, &csx, &csy, &csz); reflections = new_list_sfac(); for ( h=-INDMAX; h<=INDMAX; h++ ) { for ( k=-INDMAX; k<=INDMAX; k++ ) { for ( l=-INDMAX; l<=INDMAX; l++ ) { double complex F = 0.0; int i; double s; /* We need sin(theta)/lambda = 1/2d */ s = resolution(mol->cell, h, k, l); /* Atoms are grouped by species for faster calculation */ for ( i=0; in_species; i++ ) { double complex sfac; double complex contrib = 0.0; struct mol_species *spec; int j; spec = mol->species[i]; for ( j=0; jn_atoms; j++ ) { double ph, u, v, w; u = h*asx + k*bsx + l*csx; v = h*asy + k*bsy + l*csy; w = h*asz + k*bsz + l*csz; ph = u*spec->x[j] + v*spec->y[j] + w*spec->z[j]; /* Conversion from revolutions to radians */ contrib += cexp(-2.0*M_PI*I*ph); } sfac = get_sfac(spec->species, s, en); F += sfac * contrib; if ( do_thermal ) { F *= exp(-2.0 * spec->B[j] * s); } } set_sfac(reflections, h, k, l, F); } progress_bar((k+INDMAX)+IDIM*(h+INDMAX), IDIM*IDIM-1, "Calculating structure factors"); } } return reflections; } void get_reflections_cached(struct molecule *mol, double en) { char s[1024]; FILE *fh; size_t r; /* Add 0.5 to improve rounding */ snprintf(s, 1023, "reflections-%ieV.cache", (int)(J_to_eV(en)+0.5)); fh = fopen(s, "rb"); if ( fh == NULL ) { STATUS("No cache file found (looked for %s)\n", s); goto calc; } mol->reflections = new_list_sfac(); r = fread(mol->reflections, sizeof(double complex), IDIM*IDIM*IDIM, fh); if ( r < IDIM*IDIM*IDIM ) { STATUS("Found cache file (%s), but failed to read" " (got %lli items, wanted %i).\n", s, (long long)r, IDIM*IDIM*IDIM); goto calc; } STATUS("Read structure factors (at Bragg positions) from %s\n", s); return; calc: STATUS("Calculating structure factors at Bragg positions...\n"); mol->reflections = get_reflections(mol, en); fh = fopen(s, "wb"); if ( fh == NULL ) { STATUS("Failed to write cache file (%s)\n", s); return; } r = fwrite(mol->reflections, sizeof(double complex), IDIM*IDIM*IDIM, fh); if ( r < IDIM*IDIM*IDIM ) { STATUS("Failed to write cache file (%s)\n", s); return; } fclose(fh); STATUS("Successfully saved structure factors at Bragg positions to" " file %s\n", s); }