def _conf_to_spsim_opts(D_source,D_particle,D_detector): import spsim # Create temporary file for pdb file tmpf_pdb = tempfile.NamedTemporaryFile(mode='w+b', bufsize=-1, suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) tmpf_pdb_name = tmpf_pdb.name tmpf_pdb.close() # Write pdb file mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) spsim.write_pdb_from_mol(tmpf_pdb_name, mol) spsim.free_mol(mol) # Start with default spsim configuration opts = spsim.set_defaults() # Create temporary file for spsim configuration tmpf_conf = tempfile.NamedTemporaryFile(mode='w+b', bufsize=-1, suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) # Write string sequence from configuration dicts s = [] s += "# THIS FILE WAS CREATED AUTOMATICALLY BY CONDOR\n" s += "# Temporary configuration file for spsim\n" s += "verbosity_level = 0;\n" s += "number_of_dimensions = 2;\n" s += "number_of_patterns = 1;\n" s += "origin_to_com = 1;\n" s += "input_type = \"pdb\";\n" #s += "pdb_filename = \"%s\";\n" % D_particle["pdb_filename"] s += "pdb_filename = \"%s\";\n" % tmpf_pdb_name s += "detector_distance = %.6e;\n" % D_detector["distance"] s += "detector_width = %.6e;\n" % (D_detector["pixel_size"] * D_detector["nx"]) s += "detector_height = %.6e;\n" % (D_detector["pixel_size"] * D_detector["ny"]) s += "detector_pixel_width = %.6e;\n" % D_detector["pixel_size"] s += "detector_pixel_height = %.6e;\n" % D_detector["pixel_size"] s += "detector_center_x = %.6e;\n" % (D_detector["pixel_size"] * (D_detector["cx"] - (D_detector["nx"]-1)/2.)) s += "detector_center_y = %.6e;\n" % (D_detector["pixel_size"] * (D_detector["cy"] - (D_detector["ny"]-1)/2.)) s += "detector_binning = 1;\n" s += "experiment_wavelength = %.6e;\n" % D_source["wavelength"] s += "experiment_beam_intensity = %.6e;\n" % D_particle["intensity"] #s += "use_cuda = 0;\n" intrinsic_rotation = condor.utils.rotation.Rotation(values=D_particle["extrinsic_quaternion"],formalism="quaternion") intrinsic_rotation.invert() e0, e1, e2 = intrinsic_rotation.get_as_euler_angles("zxz") s += "phi = %.6e;\n" % e0 s += "theta = %.6e;\n" % e1 s += "psi = %.6e;\n" % e2 s += "random_orientation = 0;\n" # Write string sequence to file tmpf_conf.writelines(s) # Close temporary file tmpf_conf_name = tmpf_conf.name tmpf_conf.close() # Read configuration into options struct spsim.read_options_file(tmpf_conf_name, opts) # This deletes the temporary files os.unlink(tmpf_pdb_name) os.unlink(tmpf_conf_name) return opts
def _conf_to_spsim_opts(D_source, D_particle, D_detector, ndim=2, qn=None, qmax=None): if ndim == 2: if qn is not None or qmax is not None: log_warning( logger, "As ndim=2 the passed values for qn and qmax take no effect.") if ndim == 3: if qn is None and qmax is None: log_and_raise_error( logger, "As ndim=3 both qn and qmax must be not None.") return import spsim # Create temporary file for pdb file tmpf_pdb = tempfile.NamedTemporaryFile(mode='w+b', bufsize=-1, suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) tmpf_pdb_name = tmpf_pdb.name tmpf_pdb.close() # Write pdb file mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) spsim.write_pdb_from_mol(tmpf_pdb_name, mol) spsim.free_mol(mol) # Start with default spsim configuration opts = spsim.set_defaults() # Create temporary file for spsim configuration tmpf_conf = tempfile.NamedTemporaryFile(mode='w+b', bufsize=-1, suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) # Write string sequence from configuration dicts s = [] s += "# THIS FILE WAS CREATED AUTOMATICALLY BY CONDOR\n" s += "# Temporary configuration file for spsim\n" s += "verbosity_level = 0;\n" s += "number_of_dimensions = %i;\n" % ndim s += "number_of_patterns = 1;\n" s += "origin_to_com = 1;\n" s += "input_type = \"pdb\";\n" #s += "pdb_filename = \"%s\";\n" % D_particle["pdb_filename"] s += "pdb_filename = \"%s\";\n" % tmpf_pdb_name if ndim == 2: D = D_detector["distance"] Lx = D_detector["pixel_size"] * D_detector["nx"] Ly = D_detector["pixel_size"] * D_detector["ny"] else: k0 = 2. * numpy.pi / D_source["wavelength"] D = qn / 2. * D_detector["pixel_size"] * k0 / qmax Lx = Ly = Lz = D_detector["pixel_size"] * qn s += "detector_distance = %.12e;\n" % D s += "detector_width = %.12e;\n" % Lx s += "detector_height = %.12e;\n" % Ly if ndim == 3: s += "detector_depth = %.12e;\n" % Lz s += "detector_pixel_width = %.12e;\n" % D_detector["pixel_size"] s += "detector_pixel_height = %.12e;\n" % D_detector["pixel_size"] if ndim == 3: s += "detector_pixel_depth = %.12e;\n" % D_detector["pixel_size"] if ndim == 2: s += "detector_center_x = %.12e;\n" % (D_detector["pixel_size"] * (D_detector["cx"] - (D_detector["nx"] - 1) / 2.)) s += "detector_center_y = %.12e;\n" % (D_detector["pixel_size"] * (D_detector["cy"] - (D_detector["ny"] - 1) / 2.)) else: s += "detector_center_x = 0;\n" s += "detector_center_y = 0;\n" s += "detector_center_z = 0;\n" s += "detector_binning = 1;\n" s += "experiment_wavelength = %.12e;\n" % D_source["wavelength"] s += "experiment_beam_intensity = %.12e;\n" % D_particle["intensity"] s += "experiment_polarization = \"ignore\";\n" # polarization correction will be done in Condor if needed (see experiment.py) #s += "use_cuda = 0;\n" intrinsic_rotation = condor.utils.rotation.Rotation( values=D_particle["extrinsic_quaternion"], formalism="quaternion") intrinsic_rotation.invert() e0, e1, e2 = intrinsic_rotation.get_as_euler_angles("zxz") if not numpy.isfinite(e0): print "ERROR: phi is not finite" if not numpy.isfinite(e1): print "ERROR: theta is not finite" if not numpy.isfinite(e2): print "ERROR: psi is not finite" s += "phi = %.12e;\n" % e0 s += "theta = %.12e;\n" % e1 s += "psi = %.12e;\n" % e2 s += "random_orientation = 0;\n" # Write string sequence to file tmpf_conf.writelines(s) # Close temporary file tmpf_conf_name = tmpf_conf.name tmpf_conf.close() # Read configuration into options struct spsim.read_options_file(tmpf_conf_name, opts) # This deletes the temporary files os.unlink(tmpf_pdb_name) os.unlink(tmpf_conf_name) return opts
def propagate(self, save_map3d = False, save_qmap = False): log_debug(logger, "Start propagation") # Iterate objects D_source = self.source.get_next() D_particles = self._get_next_particles() D_detector = self.detector.get_next() # Pull out variables nx = D_detector["nx"] ny = D_detector["ny"] cx = D_detector["cx"] cy = D_detector["cy"] pixel_size = D_detector["pixel_size"] detector_distance = D_detector["distance"] wavelength = D_source["wavelength"] # Qmap without rotation Y,X = numpy.meshgrid(numpy.float64(numpy.arange(ny))-cy, numpy.float64(numpy.arange(nx))-cx, indexing="ij") qmap0 = condor.utils.scattering_vector.generate_qmap(X, Y, pixel_size, detector_distance, wavelength, extrinsic_rotation=None) qmap_singles = {} F_tot = None # Calculate patterns of all single particles individually for particle_key, D_particle in D_particles.items(): p = D_particle["_class_instance"] # Intensity at interaction point pos = D_particle["position"] D_particle["intensity"] = self.source.get_intensity(pos, "ph/m2", pulse_energy=D_source["pulse_energy"]) I_0 = D_particle["intensity"] # Calculate primary wave amplitude # F0 = sqrt(I_0) 2pi/wavelength^2 F0 = numpy.sqrt(I_0)*2*numpy.pi/wavelength**2 D_particle["F0"] = F0 # 3D Orientation extrinsic_rotation = Rotation(values=D_particle["extrinsic_quaternion"], formalism="quaternion") # Resolution dx_required = self.detector.get_resolution_element_r(wavelength, cx=cx, cy=cy, center_variation=False) dx_suggested = self.detector.get_resolution_element_r(wavelength, center_variation=True) # UNIFORM SPHERE if isinstance(p, condor.particle.ParticleSphere): # Refractive index dn = p.get_dn(wavelength) # Scattering vectors qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=None) q = numpy.sqrt(qmap[:,:,0]**2+qmap[:,:,1]**2) # Intensity scaling factor R = D_particle["diameter"]/2. V = 4/3.*numpy.pi*R**3 K = (F0*V*dn)**2 # Geometrical factor Omega_p = self.detector.get_all_pixel_solid_angles(cx, cy) # Pattern F = condor.utils.sphere_diffraction.F_sphere_diffraction(K, q, R) * numpy.sqrt(Omega_p) # UNIFORM SPHEROID elif isinstance(p, condor.particle.ParticleSpheroid): # Refractive index dn = p.get_dn(wavelength) # Scattering vectors qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=None, order="xyz") qx = qmap[:,:,0] qy = qmap[:,:,1] # Intensity scaling factor R = D_particle["diameter"]/2. V = 4/3.*numpy.pi*R**3 K = (F0*V*abs(dn))**2 # Geometrical factors a = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_a(D_particle["diameter"], D_particle["flattening"]) c = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_c(D_particle["diameter"], D_particle["flattening"]) Omega_p = self.detector.get_all_pixel_solid_angles(cx, cy) # Pattern # Spheroid axis before rotation v0 = numpy.array([0.,1.,0.]) v1 = extrinsic_rotation.rotate_vector(v0) theta = numpy.arcsin(v1[2]) phi = numpy.arctan2(-v1[0],v1[1]) F = condor.utils.spheroid_diffraction.F_spheroid_diffraction(K, qx, qy, a, c, theta, phi) * numpy.sqrt(Omega_p) # MAP elif isinstance(p, condor.particle.ParticleMap): # Scattering vectors (the nfft requires order z,y,x) qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=extrinsic_rotation, order="zyx") # Geometrical factor Omega_p = self.detector.get_all_pixel_solid_angles(cx, cy) # Generate map map3d_dn, dx = p.get_new_dn_map(D_particle, dx_required, dx_suggested, wavelength) log_debug(logger, "Sampling of map: dx_required = %e m, dx_suggested = %e m, dx = %e m" % (dx_required, dx_suggested, dx)) if save_map3d: D_particle["map3d_dn"] = map3d_dn D_particle["dx"] = dx # Rescale and shape qmap for nfft qmap_scaled = dx * qmap / (2. * numpy.pi) qmap_shaped = qmap_scaled.reshape(qmap_scaled.shape[0]*qmap_scaled.shape[1], 3) # For Jing: #D_particle["qmap3d"] = detector.generate_qmap_ori(nfft_scaled=True) # Check inputs invalid_mask = ((qmap_shaped>=-0.5) * (qmap_shaped<0.5)) == False if (invalid_mask).sum() > 0: qmap_shaped[invalid_mask] = 0. log_warning(logger, "%i invalid pixel positions." % invalid_mask.sum()) log_debug(logger, "Map3d input shape: (%i,%i,%i), number of dimensions: %i, sum %f" % (map3d_dn.shape[0], map3d_dn.shape[1], map3d_dn.shape[2], len(list(map3d_dn.shape)), abs(map3d_dn).sum())) if (numpy.isfinite(abs(map3d_dn))==False).sum() > 0: log_warning(logger, "There are infinite values in the dn map of the object.") log_debug(logger, "Scattering vectors shape: (%i,%i); Number of dimensions: %i" % (qmap_shaped.shape[0], qmap_shaped.shape[1], len(list(qmap_shaped.shape)))) if (numpy.isfinite(qmap_shaped)==False).sum() > 0: log_warning(logger, "There are infinite values in the scattering vectors.") # NFFT fourier_pattern = log_execution_time(logger)(condor.utils.nfft.nfft)(map3d_dn, qmap_shaped) # Check output - masking in case of invalid values if (invalid_mask).sum() > 0: fourier_pattern[numpy.any(invalid_mask)] = numpy.nan # reshaping fourier_pattern = numpy.reshape(fourier_pattern, (qmap_scaled.shape[0], qmap_scaled.shape[1])) log_debug(logger, "Got pattern of %i x %i pixels." % (fourier_pattern.shape[1], fourier_pattern.shape[0])) #F = F0 * fourier_pattern * dx_required**3 * numpy.sqrt(Omega_p) F = F0 * fourier_pattern * dx**3 * numpy.sqrt(Omega_p) # ATOMS elif isinstance(p, condor.particle.ParticleAtoms): # Import only here (otherwise errors if spsim library not installed) import spsim # Create options struct opts = condor.utils.config._conf_to_spsim_opts(D_source, D_particle, D_detector) spsim.write_options_file("./spsim.confout",opts) # Create molecule struct mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) # Always recenter molecule spsim.origin_to_center_of_mass(mol) spsim.write_pdb_from_mol("./mol.pdbout", mol) # Calculate diffraction pattern pat = spsim.simulate_shot(mol, opts) # Extract complex Fourier values from spsim output F_img = spsim.make_cimage(pat.F, pat.rot, opts) phot_img = spsim.make_image(opts.detector.photons_per_pixel, pat.rot, opts) F = numpy.sqrt(abs(phot_img.image[:])) * numpy.exp(1.j * numpy.angle(F_img.image[:])) spsim.sp_image_free(F_img) spsim.sp_image_free(phot_img) # Extract qmap from spsim output qmap_img = spsim.sp_image_alloc(3,D_detector["nx"], D_detector["ny"]) spsim.array_to_image(pat.HKL_list, qmap_img) qmap = numpy.zeros(shape=(D_detector["ny"], D_detector["nx"], 3)) qmap[:,:,0] = 2*numpy.pi*qmap_img.image.real[:,:,0] qmap[:,:,1] = 2*numpy.pi*qmap_img.image.real[:,:,1] qmap[:,:,2] = 2*numpy.pi*qmap_img.image.real[:,:,2] spsim.sp_image_free(qmap_img) spsim.free_diffraction_pattern(pat) spsim.free_output_in_options(opts) else: log_and_raise_error(logger, "No valid particles initialized.") sys.exit(0) if save_qmap: qmap_singles[particle_key] = qmap # Superimpose patterns if F_tot is None: F_tot = numpy.zeros_like(F) v = D_particle["position"] F_tot = F_tot + F * numpy.exp(-1.j*(v[0]*qmap0[:,:,0]+v[1]*qmap0[:,:,1]+v[2]*qmap0[:,:,2])) I_tot, M_tot = self.detector.detect_photons(abs(F_tot)**2) IXxX_tot, MXxX_tot = self.detector.bin_photons(I_tot, M_tot) if self.detector.binning is not None: FXxX_tot, MXxX_tot = condor.utils.resample.downsample(F_tot, self.detector.binning, mode="integrate", mask2d0=M_tot, bad_bits=PixelMask.PIXEL_IS_IN_MASK, min_N_pixels=1) M_tot_binary = M_tot == 0 MXxX_tot_binary = None if MXxX_tot is None else (MXxX_tot == 0) O = {} O["source"] = D_source O["particles"] = D_particles O["detector"] = D_detector O["entry_1"] = {} data_1 = {} data_1["data_fourier"] = F_tot data_1["data"] = I_tot data_1["mask"] = M_tot data_1["full_period_resolution"] = 2 * self.detector.get_max_resolution(wavelength) O["entry_1"]["data_1"] = data_1 if self.detector.binning is not None: data_2 = {} data_2["data_fourier"] = FXxX_tot data_2["data"] = IXxX_tot data_2["mask"] = MXxX_tot O["entry_1"]["data_2"] = data_2 O = remove_from_dict(O, "_") return O
def _propagate(self, save_map3d=False, save_qmap=False, ndim=2, qn=None, qmax=None): if ndim not in [2,3]: log_and_raise_error(logger, "ndim = %i is an invalid input. Has to be either 2 or 3." % ndim) log_debug(logger, "Start propagation") # Iterate objects D_source = self.source.get_next() D_particles = self._get_next_particles() D_detector = self.detector.get_next() # Pull out variables nx = D_detector["nx"] ny = D_detector["ny"] cx = D_detector["cx"] cy = D_detector["cy"] pixel_size = D_detector["pixel_size"] detector_distance = D_detector["distance"] wavelength = D_source["wavelength"] # Qmap without rotation if ndim == 2: qmap0 = self.detector.generate_qmap(wavelength, cx=cx, cy=cy, extrinsic_rotation=None) else: qmax = numpy.sqrt((self.detector.get_q_max(wavelength, pos="edge")**2).sum()) qn = max([nx, ny]) qmap0 = self.detector.generate_qmap_3d(wavelength, qn=qn, qmax=qmax, extrinsic_rotation=None, order='xyz') if self.detector.solid_angle_correction: log_and_raise_error(logger, "Carrying out solid angle correction for a simulation of a 3D Fourier volume does not make sense. Please set solid_angle_correction=False for your Detector and try again.") return qmap_singles = {} F_tot = 0. # Calculate patterns of all single particles individually for particle_key, D_particle in D_particles.items(): p = D_particle["_class_instance"] # Intensity at interaction point pos = D_particle["position"] D_particle["intensity"] = self.source.get_intensity(pos, "ph/m2", pulse_energy=D_source["pulse_energy"]) I_0 = D_particle["intensity"] # Calculate primary wave amplitude # F0 = sqrt(I_0) 2pi/wavelength^2 F0 = numpy.sqrt(I_0)*2*numpy.pi/wavelength**2 D_particle["F0"] = F0 # 3D Orientation extrinsic_rotation = Rotation(values=D_particle["extrinsic_quaternion"], formalism="quaternion") if isinstance(p, condor.particle.ParticleSphere) or isinstance(p, condor.particle.ParticleSpheroid) or isinstance(p, condor.particle.ParticleMap): # Solid angles if self.detector.solid_angle_correction: Omega_p = self.detector.get_all_pixel_solid_angles(cx, cy) else: Omega_p = pixel_size**2 / detector_distance**2 # UNIFORM SPHERE if isinstance(p, condor.particle.ParticleSphere): # Refractive index dn = p.get_dn(wavelength) # Scattering vectors if ndim == 2: qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=None) else: qmap = qmap0 q = numpy.sqrt((qmap**2).sum(axis=ndim)) # Intensity scaling factor R = D_particle["diameter"]/2. V = 4/3.*numpy.pi*R**3 K = (F0*V*dn)**2 # Pattern F = condor.utils.sphere_diffraction.F_sphere_diffraction(K, q, R) * numpy.sqrt(Omega_p) # UNIFORM SPHEROID elif isinstance(p, condor.particle.ParticleSpheroid): if ndim == 3: log_and_raise_error(logger, "Spheroid simulation with ndim = 3 is not supported.") return # Refractive index dn = p.get_dn(wavelength) # Scattering vectors qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=None, order="xyz") qx = qmap[:,:,0] qy = qmap[:,:,1] # Intensity scaling factor R = D_particle["diameter"]/2. V = 4/3.*numpy.pi*R**3 K = (F0*V*abs(dn))**2 # Geometrical factors a = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_a(D_particle["diameter"], D_particle["flattening"]) c = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_c(D_particle["diameter"], D_particle["flattening"]) # Pattern # Spheroid axis before rotation v0 = numpy.array([0.,1.,0.]) v1 = extrinsic_rotation.rotate_vector(v0) theta = numpy.arcsin(v1[2]) phi = numpy.arctan2(-v1[0],v1[1]) F = condor.utils.spheroid_diffraction.F_spheroid_diffraction(K, qx, qy, a, c, theta, phi) * numpy.sqrt(Omega_p) # MAP elif isinstance(p, condor.particle.ParticleMap): # Resolution dx_required = self.detector.get_resolution_element_r(wavelength, cx=cx, cy=cy, center_variation=False) dx_suggested = self.detector.get_resolution_element_r(wavelength, center_variation=True) # Scattering vectors (the nfft requires order z,y,x) if ndim == 2: qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=extrinsic_rotation, order="zyx") else: qmap = self.detector.generate_qmap_3d(wavelength=wavelength, qn=qn, qmax=qmax, extrinsic_rotation=extrinsic_rotation, order="zyx") # Generate map map3d_dn, dx = p.get_new_dn_map(D_particle, dx_required, dx_suggested, wavelength) log_debug(logger, "Sampling of map: dx_required = %e m, dx_suggested = %e m, dx = %e m" % (dx_required, dx_suggested, dx)) if save_map3d: D_particle["map3d_dn"] = map3d_dn D_particle["dx"] = dx # Rescale and shape qmap for nfft qmap_scaled = dx * qmap / (2. * numpy.pi) qmap_shaped = qmap_scaled.reshape(qmap_scaled.size/3, 3) # Check inputs invalid_mask = ~((qmap_shaped>=-0.5) * (qmap_shaped<0.5)) if numpy.any(invalid_mask): qmap_shaped[invalid_mask] = 0. log_warning(logger, "%i invalid pixel positions." % invalid_mask.sum()) log_debug(logger, "Map3d input shape: (%i,%i,%i), number of dimensions: %i, sum %f" % (map3d_dn.shape[0], map3d_dn.shape[1], map3d_dn.shape[2], len(list(map3d_dn.shape)), abs(map3d_dn).sum())) if (numpy.isfinite(abs(map3d_dn))==False).sum() > 0: log_warning(logger, "There are infinite values in the dn map of the object.") log_debug(logger, "Scattering vectors shape: (%i,%i); Number of dimensions: %i" % (qmap_shaped.shape[0], qmap_shaped.shape[1], len(list(qmap_shaped.shape)))) if (numpy.isfinite(qmap_shaped)==False).sum() > 0: log_warning(logger, "There are infinite values in the scattering vectors.") # NFFT fourier_pattern = log_execution_time(logger)(condor.utils.nfft.nfft)(map3d_dn, qmap_shaped) # Check output - masking in case of invalid values if numpy.any(invalid_mask): fourier_pattern[invalid_mask.any(axis=1)] = numpy.nan # reshaping fourier_pattern = numpy.reshape(fourier_pattern, tuple(list(qmap_scaled.shape)[:-1])) log_debug(logger, "Generated pattern of shape %s." % str(fourier_pattern.shape)) F = F0 * fourier_pattern * dx**3 * numpy.sqrt(Omega_p) # ATOMS elif isinstance(p, condor.particle.ParticleAtoms): # Import here to make other functionalities of Condor independent of spsim import spsim # Check version from distutils.version import StrictVersion spsim_version_min = "0.1.0" if not hasattr(spsim, "__version__") or StrictVersion(spsim.__version__) < StrictVersion(spsim_version_min): log_and_raise_error(logger, "Your spsim version is too old. Please install the newest spsim version and try again.") sys.exit(0) # Create options struct opts = condor.utils.config._conf_to_spsim_opts(D_source, D_particle, D_detector, ndim=ndim, qn=qn, qmax=qmax) spsim.write_options_file("./spsim.confout",opts) # Create molecule struct mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) # Always recenter molecule spsim.origin_to_center_of_mass(mol) spsim.write_pdb_from_mol("./mol.pdbout", mol) # Calculate diffraction pattern pat = spsim.simulate_shot(mol, opts) # Extract complex Fourier values from spsim output F_img = spsim.make_cimage(pat.F, pat.rot, opts) phot_img = spsim.make_image(opts.detector.photons_per_pixel, pat.rot, opts) F = numpy.sqrt(abs(phot_img.image[:])) * numpy.exp(1.j * numpy.angle(F_img.image[:])) spsim.sp_image_free(F_img) spsim.sp_image_free(phot_img) # Extract qmap from spsim output if ndim == 2: qmap_img = spsim.sp_image_alloc(3, nx, ny) else: qmap_img = spsim.sp_image_alloc(3*qn, qn, qn) spsim.array_to_image(pat.HKL_list, qmap_img) if ndim == 2: qmap = 2*numpy.pi * qmap_img.image.real else: qmap = 2*numpy.pi * numpy.reshape(qmap_img.image.real, (qn, qn, qn, 3)) spsim.sp_image_free(qmap_img) spsim.free_diffraction_pattern(pat) spsim.free_output_in_options(opts) else: log_and_raise_error(logger, "No valid particles initialized.") sys.exit(0) if save_qmap: qmap_singles[particle_key] = qmap v = D_particle["position"] # Calculate phase factors if needed if not numpy.allclose(v, numpy.zeros_like(v), atol=1E-12): if ndim == 2: F = F * numpy.exp(-1.j*(v[0]*qmap0[:,:,0]+v[1]*qmap0[:,:,1]+v[2]*qmap0[:,:,2])) else: F = F * numpy.exp(-1.j*(v[0]*qmap0[:,:,:,0]+v[1]*qmap0[:,:,:,1]+v[2]*qmap0[:,:,:,2])) # Superimpose patterns F_tot = F_tot + F # Polarization correction if ndim == 2: P = self.detector.calculate_polarization_factors(cx=cx, cy=cy, polarization=self.source.polarization) else: if self.source.polarization != "ignore": log_and_raise_error(logger, "polarization=\"%s\" for a 3D propagation does not make sense. Set polarization=\"ignore\" in your Source configuration and try again." % self.source.polarization) return P = 1. F_tot = numpy.sqrt(P) * F_tot # Photon detection I_tot, M_tot = self.detector.detect_photons(abs(F_tot)**2) if ndim == 2: M_tot_binary = M_tot == 0 if self.detector.binning is not None: IXxX_tot, MXxX_tot = self.detector.bin_photons(I_tot, M_tot) FXxX_tot, MXxX_tot = condor.utils.resample.downsample(F_tot, self.detector.binning, mode="integrate", mask2d0=M_tot, bad_bits=PixelMask.PIXEL_IS_IN_MASK, min_N_pixels=1) MXxX_tot_binary = None if MXxX_tot is None else (MXxX_tot == 0) else: M_tot_binary = None O = {} O["source"] = D_source O["particles"] = D_particles O["detector"] = D_detector O["entry_1"] = {} data_1 = {} data_1["data_fourier"] = F_tot data_1["data"] = I_tot data_1["mask"] = M_tot data_1["full_period_resolution"] = 2 * self.detector.get_max_resolution(wavelength) O["entry_1"]["data_1"] = data_1 if self.detector.binning is not None: data_2 = {} data_2["data_fourier"] = FXxX_tot data_2["data"] = IXxX_tot data_2["mask"] = MXxX_tot O["entry_1"]["data_2"] = data_2 O = remove_from_dict(O, "_") return O
def _propagate(self, save_map3d=False, save_qmap=False, ndim=2, qn=None, qmax=None): if ndim not in [2,3]: log_and_raise_error(logger, "ndim = %i is an invalid input. Has to be either 2 or 3." % ndim) log_debug(logger, "Start propagation") # Iterate objects D_source = self.source.get_next() D_particles = self._get_next_particles() D_detector = self.detector.get_next() # Pull out variables nx = D_detector["nx"] ny = D_detector["ny"] cx = D_detector["cx"] cy = D_detector["cy"] pixel_size = D_detector["pixel_size"] detector_distance = D_detector["distance"] wavelength = D_source["wavelength"] # Qmap without rotation if ndim == 2: qmap0 = self.detector.generate_qmap(wavelength, cx=cx, cy=cy, extrinsic_rotation=None) else: qmax = numpy.sqrt((self.detector.get_q_max(wavelength, pos="edge")**2).sum()) qn = max([nx, ny]) qmap0 = self.detector.generate_qmap_3d(wavelength, qn=qn, qmax=qmax, extrinsic_rotation=None, order='xyz') if self.detector.solid_angle_correction: log_and_raise_error(logger, "Carrying out solid angle correction for a simulation of a 3D Fourier volume does not make sense. Please set solid_angle_correction=False for your Detector and try again.") return qmap_singles = {} F_tot = 0. # Calculate patterns of all single particles individually for particle_key, D_particle in D_particles.items(): p = D_particle["_class_instance"] # Intensity at interaction point pos = D_particle["position"] D_particle["intensity"] = self.source.get_intensity(pos, "ph/m2", pulse_energy=D_source["pulse_energy"]) I_0 = D_particle["intensity"] # Calculate primary wave amplitude # F0 = sqrt(I_0) 2pi/wavelength^2 F0 = numpy.sqrt(I_0)*2*numpy.pi/wavelength**2 D_particle["F0"] = F0 # 3D Orientation extrinsic_rotation = Rotation(values=D_particle["extrinsic_quaternion"], formalism="quaternion") if isinstance(p, condor.particle.ParticleSphere) or isinstance(p, condor.particle.ParticleSpheroid) or isinstance(p, condor.particle.ParticleMap): # Solid angles if self.detector.solid_angle_correction: Omega_p = self.detector.get_all_pixel_solid_angles(cx, cy) else: Omega_p = pixel_size**2 / detector_distance**2 # UNIFORM SPHERE if isinstance(p, condor.particle.ParticleSphere): # Refractive index dn = p.get_dn(wavelength) # Scattering vectors if ndim == 2: qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=None) else: qmap = qmap0 q = numpy.sqrt((qmap**2).sum(axis=ndim)) # Intensity scaling factor R = D_particle["diameter"]/2. V = 4/3.*numpy.pi*R**3 K = (F0*V*dn)**2 # Pattern F = condor.utils.sphere_diffraction.F_sphere_diffraction(K, q, R) * numpy.sqrt(Omega_p) # UNIFORM SPHEROID elif isinstance(p, condor.particle.ParticleSpheroid): if ndim == 3: log_and_raise_error(logger, "Spheroid simulation with ndim = 3 is not supported.") return # Refractive index dn = p.get_dn(wavelength) # Scattering vectors qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=None, order="xyz") qx = qmap[:,:,0] qy = qmap[:,:,1] # Intensity scaling factor R = D_particle["diameter"]/2. V = 4/3.*numpy.pi*R**3 K = (F0*V*abs(dn))**2 # Geometrical factors a = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_a(D_particle["diameter"], D_particle["flattening"]) c = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_c(D_particle["diameter"], D_particle["flattening"]) # Pattern # Spheroid axis before rotation v0 = numpy.array([0.,1.,0.]) v1 = extrinsic_rotation.rotate_vector(v0) theta = numpy.arcsin(v1[2]) phi = numpy.arctan2(-v1[0],v1[1]) F = condor.utils.spheroid_diffraction.F_spheroid_diffraction(K, qx, qy, a, c, theta, phi) * numpy.sqrt(Omega_p) # MAP elif isinstance(p, condor.particle.ParticleMap): # Resolution dx_required = self.detector.get_resolution_element_r(wavelength, cx=cx, cy=cy, center_variation=False) dx_suggested = self.detector.get_resolution_element_r(wavelength, center_variation=True) # Scattering vectors (the nfft requires order z,y,x) if ndim == 2: qmap = self.get_qmap(nx=nx, ny=ny, cx=cx, cy=cy, pixel_size=pixel_size, detector_distance=detector_distance, wavelength=wavelength, extrinsic_rotation=extrinsic_rotation, order="zyx") else: qmap = self.detector.generate_qmap_3d(wavelength=wavelength, qn=qn, qmax=qmax, extrinsic_rotation=extrinsic_rotation, order="zyx") # Generate map map3d_dn, dx = p.get_new_dn_map(D_particle, dx_required, dx_suggested, wavelength) log_debug(logger, "Sampling of map: dx_required = %e m, dx_suggested = %e m, dx = %e m" % (dx_required, dx_suggested, dx)) if save_map3d: D_particle["map3d_dn"] = map3d_dn D_particle["dx"] = dx # Rescale and shape qmap for nfft qmap_scaled = dx * qmap / (2. * numpy.pi) qmap_shaped = qmap_scaled.reshape(int(qmap_scaled.size/3), 3) # Check inputs invalid_mask = ~((qmap_shaped>=-0.5) * (qmap_shaped<0.5)) if numpy.any(invalid_mask): qmap_shaped[invalid_mask] = 0. log_warning(logger, "%i invalid pixel positions." % invalid_mask.sum()) log_debug(logger, "Map3d input shape: (%i,%i,%i), number of dimensions: %i, sum %f" % (map3d_dn.shape[0], map3d_dn.shape[1], map3d_dn.shape[2], len(list(map3d_dn.shape)), abs(map3d_dn).sum())) if (numpy.isfinite(abs(map3d_dn))==False).sum() > 0: log_warning(logger, "There are infinite values in the dn map of the object.") log_debug(logger, "Scattering vectors shape: (%i,%i); Number of dimensions: %i" % (qmap_shaped.shape[0], qmap_shaped.shape[1], len(list(qmap_shaped.shape)))) if (numpy.isfinite(qmap_shaped)==False).sum() > 0: log_warning(logger, "There are infinite values in the scattering vectors.") # NFFT fourier_pattern = log_execution_time(logger)(condor.utils.nfft.nfft)(map3d_dn, qmap_shaped) # Check output - masking in case of invalid values if numpy.any(invalid_mask): fourier_pattern[invalid_mask.any(axis=1)] = numpy.nan # reshaping fourier_pattern = numpy.reshape(fourier_pattern, tuple(list(qmap_scaled.shape)[:-1])) log_debug(logger, "Generated pattern of shape %s." % str(fourier_pattern.shape)) F = F0 * fourier_pattern * dx**3 * numpy.sqrt(Omega_p) # ATOMS elif isinstance(p, condor.particle.ParticleAtoms): # Import here to make other functionalities of Condor independent of spsim import spsim # Check version from distutils.version import StrictVersion spsim_version_min = "0.1.0" if not hasattr(spsim, "__version__") or StrictVersion(spsim.__version__) < StrictVersion(spsim_version_min): log_and_raise_error(logger, "Your spsim version is too old. Please install the newest spsim version and try again.") sys.exit(0) # Create options struct opts = condor.utils.config._conf_to_spsim_opts(D_source, D_particle, D_detector, ndim=ndim, qn=qn, qmax=qmax) spsim.write_options_file("./spsim.confout",opts) # Create molecule struct mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) # Always recenter molecule spsim.origin_to_center_of_mass(mol) spsim.write_pdb_from_mol("./mol.pdbout", mol) # Calculate diffraction pattern pat = spsim.simulate_shot(mol, opts) # Extract complex Fourier values from spsim output F_img = spsim.make_cimage(pat.F, pat.rot, opts) phot_img = spsim.make_image(opts.detector.photons_per_pixel, pat.rot, opts) F = numpy.sqrt(abs(phot_img.image[:])) * numpy.exp(1.j * numpy.angle(F_img.image[:])) spsim.sp_image_free(F_img) spsim.sp_image_free(phot_img) # Extract qmap from spsim output if ndim == 2: qmap_img = spsim.sp_image_alloc(3, nx, ny) else: qmap_img = spsim.sp_image_alloc(3*qn, qn, qn) spsim.array_to_image(pat.HKL_list, qmap_img) if ndim == 2: qmap = 2*numpy.pi * qmap_img.image.real else: qmap = 2*numpy.pi * numpy.reshape(qmap_img.image.real, (qn, qn, qn, 3)) self._qmap_cache = { "qmap" : qmap, "nx" : nx, "ny" : ny, "cx" : cx, "cy" : cy, "pixel_size" : pixel_size, "detector_distance" : detector_distance, "wavelength" : wavelength, "extrinsic_rotation": copy.deepcopy(extrinsic_rotation), "order" : 'zyx', } spsim.sp_image_free(qmap_img) spsim.free_diffraction_pattern(pat) spsim.free_output_in_options(opts) else: log_and_raise_error(logger, "No valid particles initialized.") sys.exit(0) if save_qmap: qmap_singles[particle_key] = qmap v = D_particle["position"] # Calculate phase factors if needed if not numpy.allclose(v, numpy.zeros_like(v), atol=1E-12): if ndim == 2: F = F * numpy.exp(-1.j*(v[0]*qmap0[:,:,0]+v[1]*qmap0[:,:,1]+v[2]*qmap0[:,:,2])) else: F = F * numpy.exp(-1.j*(v[0]*qmap0[:,:,:,0]+v[1]*qmap0[:,:,:,1]+v[2]*qmap0[:,:,:,2])) # Superimpose patterns F_tot = F_tot + F # Polarization correction if ndim == 2: P = self.detector.calculate_polarization_factors(cx=cx, cy=cy, polarization=self.source.polarization) else: if self.source.polarization != "ignore": log_and_raise_error(logger, "polarization=\"%s\" for a 3D propagation does not make sense. Set polarization=\"ignore\" in your Source configuration and try again." % self.source.polarization) return P = 1. F_tot = numpy.sqrt(P) * F_tot # Photon detection I_tot, M_tot = self.detector.detect_photons(abs(F_tot)**2) if ndim == 2: M_tot_binary = M_tot == 0 if self.detector.binning is not None: IXxX_tot, MXxX_tot = self.detector.bin_photons(I_tot, M_tot) FXxX_tot, MXxX_tot = condor.utils.resample.downsample(F_tot, self.detector.binning, mode="integrate", mask2d0=M_tot, bad_bits=PixelMask.PIXEL_IS_IN_MASK, min_N_pixels=1) MXxX_tot_binary = None if MXxX_tot is None else (MXxX_tot == 0) else: M_tot_binary = None O = {} O["source"] = D_source O["particles"] = D_particles O["detector"] = D_detector O["entry_1"] = {} data_1 = {} data_1["data_fourier"] = F_tot data_1["data"] = I_tot data_1["mask"] = M_tot data_1["full_period_resolution"] = 2 * self.detector.get_max_resolution(wavelength) O["entry_1"]["data_1"] = data_1 if self.detector.binning is not None: data_2 = {} data_2["data_fourier"] = FXxX_tot data_2["data"] = IXxX_tot data_2["mask"] = MXxX_tot O["entry_1"]["data_2"] = data_2 O = remove_from_dict(O, "_") return O
def _conf_to_spsim_opts(D_source,D_particle,D_detector,ndim=2,qn=None,qmax=None): if ndim == 2: if qn is not None or qmax is not None: log_warning(logger, "As ndim=2 the passed values for qn and qmax take no effect.") if ndim == 3: if qn is None and qmax is None: log_and_raise_error(logger, "As ndim=3 both qn and qmax must be not None.") return import spsim # Create temporary file for pdb file tmpf_pdb = tempfile.NamedTemporaryFile(mode='w+', suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) tmpf_pdb_name = tmpf_pdb.name tmpf_pdb.close() # Write pdb file mol = spsim.get_molecule_from_atoms(D_particle["atomic_numbers"], D_particle["atomic_positions"]) spsim.write_pdb_from_mol(tmpf_pdb_name, mol) spsim.free_mol(mol) # Start with default spsim configuration opts = spsim.set_defaults() # Create temporary file for spsim configuration tmpf_conf = tempfile.NamedTemporaryFile(mode='w+', suffix='.conf', prefix='tmp_spsim', dir=None, delete=False) # Write string sequence from configuration dicts s = [] s += "# THIS FILE WAS CREATED AUTOMATICALLY BY CONDOR\n" s += "# Temporary configuration file for spsim\n" s += "verbosity_level = 0;\n" s += "number_of_dimensions = %i;\n" % ndim s += "number_of_patterns = 1;\n" s += "origin_to_com = 1;\n" s += "input_type = \"pdb\";\n" #s += "pdb_filename = \"%s\";\n" % D_particle["pdb_filename"] s += "pdb_filename = \"%s\";\n" % tmpf_pdb_name if ndim == 2: D = D_detector["distance"] Lx = D_detector["pixel_size"] * D_detector["nx"] Ly = D_detector["pixel_size"] * D_detector["ny"] else: k0 = 2. * numpy.pi / D_source["wavelength"] D = qn / 2. * D_detector["pixel_size"] * k0 / qmax Lx = Ly = Lz = D_detector["pixel_size"] * qn s += "detector_distance = %.12e;\n" % D s += "detector_width = %.12e;\n" % Lx s += "detector_height = %.12e;\n" % Ly if ndim == 3: s += "detector_depth = %.12e;\n" % Lz s += "detector_pixel_width = %.12e;\n" % D_detector["pixel_size"] s += "detector_pixel_height = %.12e;\n" % D_detector["pixel_size"] if ndim == 3: s += "detector_pixel_depth = %.12e;\n" % D_detector["pixel_size"] if ndim == 2: s += "detector_center_x = %.12e;\n" % (D_detector["pixel_size"] * (D_detector["cx"] - (D_detector["nx"]-1)/2.)) s += "detector_center_y = %.12e;\n" % (D_detector["pixel_size"] * (D_detector["cy"] - (D_detector["ny"]-1)/2.)) else: s += "detector_center_x = 0;\n" s += "detector_center_y = 0;\n" s += "detector_center_z = 0;\n" s += "detector_binning = 1;\n" s += "experiment_wavelength = %.12e;\n" % D_source["wavelength"] s += "experiment_beam_intensity = %.12e;\n" % D_particle["intensity"] s += "experiment_polarization = \"ignore\";\n" # polarization correction will be done in Condor if needed (see experiment.py) #s += "use_cuda = 0;\n" intrinsic_rotation = condor.utils.rotation.Rotation(values=D_particle["extrinsic_quaternion"],formalism="quaternion") intrinsic_rotation.invert() e0, e1, e2 = intrinsic_rotation.get_as_euler_angles("zxz") if not numpy.isfinite(e0): print("ERROR: phi is not finite") if not numpy.isfinite(e1): print("ERROR: theta is not finite") if not numpy.isfinite(e2): print("ERROR: psi is not finite") s += "phi = %.12e;\n" % e0 s += "theta = %.12e;\n" % e1 s += "psi = %.12e;\n" % e2 s += "random_orientation = 0;\n" # Write string sequence to file tmpf_conf.writelines(s) # Close temporary file tmpf_conf_name = tmpf_conf.name tmpf_conf.close() # Read configuration into options struct spsim.read_options_file(tmpf_conf_name, opts) # This deletes the temporary files os.unlink(tmpf_pdb_name) os.unlink(tmpf_conf_name) return opts