/* * sfac.c * * Scattering factors * * (c) 2006-2010 Thomas White * * Part of CrystFEL - crystallography with a FEL * */ #include #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("The atoms were 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(const char *filename) { 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(filename, "r"); if ( fh == NULL ) { ERROR("Couldn't open PDB file\n"); return NULL; } do { int j, r; int done = 0; char xs[8]; char ys[8]; char zs[8]; char occs[6]; char Bs[6]; char el[3]; float xf, yf, zf, occf, Bf; double x, y, z, occ, B; 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) && (strncmp(line, "ATOM ", 6) != 0) ) continue; chomp(line); if ( strlen(line) != 80 ) { STATUS("Line does not have the correct length (%i):\n", (int)strlen(line)); STATUS("'%s'\n", line); continue; } /* Separate out fixed-width fields */ memcpy(xs, line+30, 5); xs[7] = '\0'; memcpy(ys, line+38, 5); ys[7] = '\0'; memcpy(zs, line+46, 5); zs[7] = '\0'; memcpy(occs, line+54, 5); occs[5] = '\0'; memcpy(Bs, line+60, 5); Bs[5] = '\0'; memcpy(el, line+76, 2); el[2] = '\0'; /* Convert fields */ r = sscanf(xs, "%f", &xf); r += sscanf(ys, "%f", &yf); r += sscanf(zs, "%f", &zf); r += sscanf(occs, "%f", &occf); r += sscanf(Bs, "%f", &Bf); if ( el[0] == ' ' ) { el[0] = el[1]; el[1] = '\0'; } else { el[1] = tolower(el[1]); } /* 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++; if ( mol->species[j]->n_atoms == MAX_ATOMS ) { ERROR("Too many atoms of type '%s'!\n", el); exit(1); } 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, 1+strlen(el)); 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); fflush(stderr); 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); } double *get_reflections(struct molecule *mol, double en, double res, double *phases, ReflItemList *items) { double *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_intensity(); 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, oneoverd; /* We need sin(theta)/lambda = 1/2d */ s = resolution(mol->cell, h, k, l); oneoverd = 2.0 * s; if ( oneoverd > res ) continue; /* 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; double complex cpart; 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 */ cpart = cexp(-2.0*M_PI*I*ph); if ( do_thermal ) { cpart *= exp(-2.0 * spec->B[j] * s * s); } contrib += cpart; } sfac = get_sfac(spec->species, s, en); F += sfac * contrib; } set_intensity(reflections, h, k, l, pow(cabs(F), 2.0)); if ( phases != NULL ) set_phase(phases, h, k, l, carg(F)); if ( items != NULL ) add_item(items, h, k, l); } progress_bar((k+INDMAX)+IDIM*(h+INDMAX), IDIM*IDIM-1, "Calculating reflection intensities"); } } return reflections; }