def __init__(self, I, **args): """ For mapper naive we just have a single object. The support projection is: Psup O = F . S . F-1 . O And the modulus projection is: Pmod O = O sqrt{ I / M } Where M is the forward mapping (self.Imap) of O to the diffraction intensity: M = (1 - gaus) sum_i | O_i |^2 + N * L * gaus * | sum_i O_i |^2 Parameters ---------- I : numpy.ndarray, (N, M, K) Merged diffraction patterns to be phased. N : the number of pixels along slowest scan axis of the detector M : the number of pixels along slow scan axis of the detector K : the number of pixels along fast scan axis of the detector O : numpy.ndarray, (N, M, K) The real-space scattering density of the object such that: I = |F[O]|^2 where F[.] is the 3D Fourier transform of '.'. dtype : np.float32 or np.float64 Determines the numerical precision of the calculation. c_dtype : np.complex64 or np.complex128 Determines the numerical precision of the complex variables. support : (numpy.ndarray, None or int), optional (N, M, K) Real-space region where the object function is known to be zero. If support is an integer then the N most intense pixels will be kept at each iteration. voxel_number : None or int, optional If int, then the voxel number support is used. If support is not None or False then the voxel number support is used within the sample support bounds. mask : numpy.ndarray, (N, M, K), optional, default (1) The valid detector pixels. Mask[i, j, k] = 1 (or True) when the detector pixel i, j, k is valid, Mask[i, j, k] = 0 (or False) otherwise. alpha : float, optional, default (1.0e-10) A floating point number to regularise array division (prevents 1/0 errors). disorder['sigma'] : float The standard deviation of the isotropic displacement of the solid unit within the crystal. detector['shape'] : tupple The shape of the detector (3D). crystal['unit_cell'] : tupple The dimensions of the unit cell Returns ------- O : numpy.ndarray, (U, V, K) The real-space object function after 'iters' iterations of the ERA algorithm. info : dict contains diagnostics: 'I' : the diffraction pattern corresponding to object above 'eMod' : the modulus error for each iteration: eMod_i = sqrt( sum(| O_i - Pmod(O_i) |^2) / I ) 'eCon' : the convergence error for each iteration: eCon_i = sqrt( sum(| O_i - O_i-1 |^2) / sum(| O_i |^2) ) """ # dtype #----------------------------------------------- if isValid('dtype', args) : dtype = args['dtype'] else : dtype = np.float64 if isValid('c_dtype', args) : c_dtype = args['c_dtype'] else : c_dtype = np.complex128 # initialise the object #----------------------------------------------- if isValid('O', args): modes = np.fft.fftn(args['O']) else : print('initialising object with random numbers') modes = np.random.random(I.shape).astype(c_dtype) # initialise the mask, alpha value and amp #----------------------------------------------- self.mask = 1 if isValid('mask', args): self.mask = args['mask'] self.alpha = 1.0e-10 if isValid('alpha', args): self.alpha = args['alpha'] self.I_norm = (self.mask * I).sum() self.amp = np.sqrt(I.astype(dtype)) # define the support projection #----------------------------------------------- if isValid('voxel_number', args) : self.voxel_number = args['voxel_number'] self.support = None else : self.voxel_number = False # self.support = args['support'] self.S = self.support.copy() # make the unit cell and diffuse weightings #----------------------------------------------- self.sym_ops = get_sym_ops(args) N = args['disorder']['n'] exp = make_exp(args['disorder']['sigma'], args['detector']['shape']) lattice = symmetry_operations.lattice(args['crystal']['unit_cell'], args['detector']['shape']) #self.solid_syms = lambda x : sym_ops.solid_syms(x) self.unit_cell_weighting = N * lattice * exp self.diffuse_weighting = (1. - exp) self.modes = modes
def __init__(self, I, **args): """ """ # dtype #----------------------------------------------- if isValid('dtype', args) : dtype = args['dtype'] else : dtype = np.float64 if isValid('c_dtype', args) : c_dtype = args['c_dtype'] else : c_dtype = np.complex128 # initialise the object #----------------------------------------------- if isValid('O', args): O = np.fft.fftn(args['O']) else : print('initialising object with random numbers') O = np.random.random(I.shape).astype(c_dtype) # initialise the mask, alpha value and amp #----------------------------------------------- self.mask = 1 if isValid('mask', args): self.mask = args['mask'] self.alpha = 1.0e-10 if isValid('alpha', args): self.alpha = args['alpha'] self.I_norm = (self.mask * I).sum() self.amp = np.sqrt(I.astype(dtype)) # define the support projection #----------------------------------------------- if isValid('voxel_number', args) : self.voxel_number = args['voxel_number'] self.support = None self.S = None else : self.voxel_number = False # self.support = args['support'] self.S = self.support.copy() # make the unit cell and diffuse weightings #----------------------------------------------- self.sym_ops = get_sym_ops(args) N = args['disorder']['n'] exp = make_exp(args['disorder']['sigma'], args['detector']['shape']) lattice = symmetry_operations.lattice(args['crystal']['unit_cell'], args['detector']['shape']) self.unit_cell = args['crystal']['unit_cell'] self.unit_cell_weighting = N * lattice * exp self.diffuse_weighting = (1. - exp) self.modes = np.zeros( (2 * self.sym_ops.syms.shape[0],) + self.sym_ops.syms.shape[1:], O.dtype) # diffuse terms self.modes[:self.modes.shape[0]//2] = self.sym_ops.solid_syms_Fourier(O, apply_translation = False) # unit cell terms self.modes[self.modes.shape[0]//2:] = self.sym_ops.solid_syms_Fourier(O, apply_translation = True) print('eMod(modes0):', self.Emod(self.modes)) # Ellipse axes #----------------------------------------------- # Here we have : # (x / e0)**2 + (y / e1)**2 = 1 , where # # e0 = sqrt{ self.diffuse_weighting / I } and # # e1 = sqrt{ self.unit_cell_weighting / I } #----------------------------------------------- # floating point tolerance for 1/x (log10) tol = 100. #1.0e+100 # check for numbers close to infinity in sqrt(I / self.diffuse_weighting) m = self.diffuse_weighting <= 0.0 m[~m] = 0.5 * (np.log10(I[~m]) - np.log10(self.diffuse_weighting[~m])) > tol self.e0_inf = m.copy() self.e0 = np.zeros_like(self.diffuse_weighting) self.e0[~m] = np.sqrt(I[~m]) / np.sqrt(self.diffuse_weighting[~m]) # check for numbers close to infinity in sqrt(I / self.unit_cell_weighting) m = self.unit_cell_weighting <= 0.0 m[~m] = 0.5 * (np.log10(I[~m]) - np.log10(self.unit_cell_weighting[~m])) > tol self.e1_inf = m.copy() self.e1 = np.zeros_like(self.unit_cell_weighting) self.e1[~m] = np.sqrt(I[~m]) / np.sqrt(self.sym_ops.syms.shape[0] * self.unit_cell_weighting[~m]) self.iters = 0