Ejemplo n.º 1
0
def simulate_diffractions(grain_params, experiment, controller):
    """actual forward simulation of the diffraction"""

    # use a packed array for the image_stack
    array_dims = (experiment.nframes, experiment.ncols,
                  ((experiment.nrows - 1) // 8) + 1)
    image_stack = np.zeros(array_dims, dtype=np.uint8)

    count = len(grain_params)
    subprocess = 'simulate diffractions'

    _project = xrdutil._project_on_detector_plane
    rD = experiment.rMat_d
    chi = experiment.chi
    tD = experiment.tVec_d
    tS = experiment.tVec_s
    distortion = experiment.distortion

    eta_range = [
        (-np.pi, np.pi),
    ]
    ome_range = experiment.ome_range
    ome_period = (-np.pi, np.pi)

    full_hkls = xrdutil._fetch_hkls_from_planedata(experiment.plane_data)
    bMat = experiment.plane_data.latVecOps['B']
    wlen = experiment.plane_data.wavelength

    controller.start(subprocess, count)
    for i in range(count):
        rC = xfcapi.makeRotMatOfExpMap(grain_params[i][0:3])
        tC = np.ascontiguousarray(grain_params[i][3:6])
        vInv_s = np.ascontiguousarray(grain_params[i][6:12])
        ang_list = np.vstack(
            xfcapi.oscillAnglesOfHKLs(full_hkls[:, 1:],
                                      chi,
                                      rC,
                                      bMat,
                                      wlen,
                                      vInv=vInv_s))
        # hkls not needed here
        all_angs, _ = xrdutil._filter_hkls_eta_ome(full_hkls, ang_list,
                                                   eta_range, ome_range)
        all_angs[:, 2] = xfcapi.mapAngle(all_angs[:, 2], ome_period)

        proj_pts = _project(all_angs, rD, rC, chi, tD, tC, tS, distortion)
        det_xy = proj_pts[0]
        _write_pixels(det_xy, all_angs[:, 2], image_stack, experiment.base,
                      experiment.inv_deltas, experiment.clip_vals)

        controller.update(i + 1)

    controller.finish(subprocess)
    return image_stack
Ejemplo n.º 2
0
def extract_detector_transformation(detector_params):
    """
    Construct arrays from detector parameters.

    goes from 10 vector of detector parames OR instrument config dictionary
    (from YAML spec) to affine transformation arrays

    Parameters
    ----------
    detector_params : TYPE
        DESCRIPTION.

    Returns
    -------
    rMat_d : TYPE
        DESCRIPTION.
    tVec_d : TYPE
        DESCRIPTION.
    chi : TYPE
        DESCRIPTION.
    tVec_s : TYPE
        DESCRIPTION.

    """
    # extract variables for convenience
    if isinstance(detector_params, dict):
        rMat_d = xfcapi.makeRotMatOfExpMap(
            np.array(detector_params['detector']['transform']['tilt']))
        tVec_d = np.r_[detector_params['detector']['transform']['translation']]
        chi = detector_params['oscillation_stage']['chi']
        tVec_s = np.r_[detector_params['oscillation_stage']['translation']]
    else:
        assert len(detector_params >= 10), \
            "list of detector parameters must have length >= 10"
        rMat_d = xfcapi.makeRotMatOfExpMap(detector_params[:3])
        tVec_d = np.ascontiguousarray(detector_params[3:6])
        chi = detector_params[6]
        tVec_s = np.ascontiguousarray(detector_params[7:10])
    return rMat_d, tVec_d, chi, tVec_s
Ejemplo n.º 3
0
def convert_tilt_convention(iconfig, old_convention, new_convention):
    """
    convert the tilt angles from an old convention to a new convention

    This should work for both configs with statuses and without
    """
    if new_convention == old_convention:
        return

    def _get_tilt_array(data):
        # This works for both a config with statuses, and without
        if isinstance(data, dict):
            return data.get('value')
        return data

    def _set_tilt_array(data, val):
        # This works for both a config with statuses, and without
        if isinstance(data, dict):
            data['value'] = val
        else:
            data.clear()
            data.extend(val)

    old_axes, old_extrinsic = old_convention
    new_axes, new_extrinsic = new_convention

    det_keys = iconfig['detectors'].keys()
    if old_axes is not None and old_extrinsic is not None:
        # First, convert these to the matrix invariants
        rme = RotMatEuler(np.zeros(3), old_axes, old_extrinsic)
        for key in det_keys:
            tilts = iconfig['detectors'][key]['transform']['tilt']
            rme.angles = np.array(_get_tilt_array(tilts))
            phi, n = angleAxisOfRotMat(rme.rmat)
            _set_tilt_array(tilts, (phi * n.flatten()).tolist())

        if new_axes is None or new_extrinsic is None:
            # We are done
            return

    # Update to the new mapping
    rme = RotMatEuler(np.zeros(3), new_axes, new_extrinsic)
    for key in det_keys:
        tilts = iconfig['detectors'][key]['transform']['tilt']
        tilt = np.array(_get_tilt_array(tilts))
        rme.rmat = makeRotMatOfExpMap(tilt)
        # Use np.ndarray.tolist() to convert back to native python types
        _set_tilt_array(tilts, np.array(rme.angles).tolist())
Ejemplo n.º 4
0
def convert_angle_convention(angles, old_convention, new_convention):
    if old_convention is not None:
        # First, convert these to the matrix invariants
        rme = RotMatEuler(np.zeros(3), **old_convention)
        rme.angles = np.array(angles)
        phi, n = angleAxisOfRotMat(rme.rmat)
        angles = (phi * n.flatten()).tolist()

        if new_convention is None:
            # We are done
            return angles

    # Update to the new mapping
    rme = RotMatEuler(np.zeros(3), **new_convention)
    rme.rmat = makeRotMatOfExpMap(np.array(angles))
    return np.array(rme.angles).tolist()
Ejemplo n.º 5
0
Archivo: grains.py Proyecto: cjh1/hexrd
def objFuncFitGrain(gFit,
                    gFull,
                    gFlag,
                    instrument,
                    reflections_dict,
                    bMat,
                    wavelength,
                    omePeriod,
                    simOnly=False,
                    return_value_flag=return_value_flag):
    """
    Calculate residual between measured and simulated ff-HEDM G-vectors.

    gFull[0]  = expMap_c[0]
    gFull[1]  = expMap_c[1]
    gFull[2]  = expMap_c[2]
    gFull[3]  = tVec_c[0]
    gFull[4]  = tVec_c[1]
    gFull[5]  = tVec_c[2]
    gFull[6]  = vInv_MV[0]
    gFull[7]  = vInv_MV[1]
    gFull[8]  = vInv_MV[2]
    gFull[9]  = vInv_MV[3]
    gFull[10] = vInv_MV[4]
    gFull[11] = vInv_MV[5]

    OLD CALL
    objFuncFitGrain(gFit, gFull, gFlag,
                    detectorParams,
                    xyo_det, hkls_idx, bMat, wavelength,
                    bVec, eVec,
                    dFunc, dParams,
                    omePeriod,
                    simOnly=False, return_value_flag=return_value_flag)

    Parameters
    ----------
    gFit : TYPE
        DESCRIPTION.
    gFull : TYPE
        DESCRIPTION.
    gFlag : TYPE
        DESCRIPTION.
    instrument : TYPE
        DESCRIPTION.
    reflections_dict : TYPE
        DESCRIPTION.
    bMat : TYPE
        DESCRIPTION.
    wavelength : TYPE
        DESCRIPTION.
    omePeriod : TYPE
        DESCRIPTION.
    simOnly : TYPE, optional
        DESCRIPTION. The default is False.
    return_value_flag : TYPE, optional
        DESCRIPTION. The default is return_value_flag.

    Raises
    ------
    RuntimeError
        DESCRIPTION.

    Returns
    -------
    retval : TYPE
        DESCRIPTION.

    """
    bVec = instrument.beam_vector
    eVec = instrument.eta_vector

    # fill out parameters
    gFull[gFlag] = gFit

    # map parameters to functional arrays
    rMat_c = xfcapi.makeRotMatOfExpMap(gFull[:3])
    tVec_c = gFull[3:6].reshape(3, 1)
    vInv_s = gFull[6:]
    vMat_s = mutil.vecMVToSymm(vInv_s)  # NOTE: Inverse of V from F = V * R

    # loop over instrument panels
    # CAVEAT: keeping track of key ordering in the "detectors" attribute of
    # instrument here because I am not sure if instatiating them using
    # dict.fromkeys() preserves the same order if using iteration...
    # <JVB 2017-10-31>
    calc_omes_dict = dict.fromkeys(instrument.detectors, [])
    calc_xy_dict = dict.fromkeys(instrument.detectors)
    meas_xyo_all = []
    det_keys_ordered = []
    for det_key, panel in instrument.detectors.items():
        det_keys_ordered.append(det_key)

        rMat_d, tVec_d, chi, tVec_s = extract_detector_transformation(
            instrument.detector_parameters[det_key])

        results = reflections_dict[det_key]
        if len(results) == 0:
            continue
        """
        extract data from results list fields:
          refl_id, gvec_id, hkl, sum_int, max_int, pred_ang, meas_ang, meas_xy

        or array from spots tables:
          0:5    ID    PID    H    K    L
          5:7    sum(int)    max(int)
          7:10   pred tth    pred eta    pred ome
          10:13  meas tth    meas eta    meas ome
          13:15  pred X    pred Y
          15:17  meas X    meas Y
        """
        if isinstance(results, list):
            # WARNING: hkls and derived vectors below must be columnwise;
            # strictly necessary??? change affected APIs instead?
            # <JVB 2017-03-26>
            hkls = np.atleast_2d(np.vstack([x[2] for x in results])).T

            meas_xyo = np.atleast_2d(
                np.vstack([np.r_[x[7], x[6][-1]] for x in results]))
        elif isinstance(results, np.ndarray):
            hkls = np.atleast_2d(results[:, 2:5]).T
            meas_xyo = np.atleast_2d(results[:, [15, 16, 12]])

        # distortion handling
        if panel.distortion is not None:
            meas_omes = meas_xyo[:, 2]
            xy_unwarped = panel.distortion.apply(meas_xyo[:, :2])
            meas_xyo = np.vstack([xy_unwarped.T, meas_omes]).T
            pass

        # append to meas_omes
        meas_xyo_all.append(meas_xyo)

        # G-vectors:
        #   1. calculate full g-vector components in CRYSTAL frame from B
        #   2. rotate into SAMPLE frame and apply stretch
        #   3. rotate back into CRYSTAL frame and normalize to unit magnitude
        # IDEA: make a function for this sequence of operations with option for
        # choosing ouput frame (i.e. CRYSTAL vs SAMPLE vs LAB)
        gVec_c = np.dot(bMat, hkls)
        gVec_s = np.dot(vMat_s, np.dot(rMat_c, gVec_c))
        gHat_c = mutil.unitVector(np.dot(rMat_c.T, gVec_s))

        # !!!: check that this operates on UNWARPED xy
        match_omes, calc_omes = matchOmegas(meas_xyo,
                                            hkls,
                                            chi,
                                            rMat_c,
                                            bMat,
                                            wavelength,
                                            vInv=vInv_s,
                                            beamVec=bVec,
                                            etaVec=eVec,
                                            omePeriod=omePeriod)

        # append to omes dict
        calc_omes_dict[det_key] = calc_omes

        # TODO: try Numba implementations
        rMat_s = xfcapi.makeOscillRotMatArray(chi, calc_omes)
        calc_xy = xfcapi.gvecToDetectorXYArray(gHat_c.T,
                                               rMat_d,
                                               rMat_s,
                                               rMat_c,
                                               tVec_d,
                                               tVec_s,
                                               tVec_c,
                                               beamVec=bVec)

        # append to xy dict
        calc_xy_dict[det_key] = calc_xy
        pass

    # stack results to concatenated arrays
    calc_omes_all = np.hstack([calc_omes_dict[k] for k in det_keys_ordered])
    tmp = []
    for k in det_keys_ordered:
        if calc_xy_dict[k] is not None:
            tmp.append(calc_xy_dict[k])
    calc_xy_all = np.vstack(tmp)
    meas_xyo_all = np.vstack(meas_xyo_all)

    npts = len(meas_xyo_all)
    if np.any(np.isnan(calc_xy)):
        raise RuntimeError("infeasible pFull: may want to scale" +
                           "back finite difference step size")

    # return values
    if simOnly:
        # return simulated values
        if return_value_flag in [None, 1]:
            retval = np.hstack([calc_xy_all, calc_omes_all.reshape(npts, 1)])
        else:
            rd = dict.fromkeys(det_keys_ordered)
            for det_key in det_keys_ordered:
                rd[det_key] = {
                    'calc_xy': calc_xy_dict[det_key],
                    'calc_omes': calc_omes_dict[det_key]
                }
            retval = rd
    else:
        # return residual vector
        # IDEA: try angles instead of xys?
        diff_vecs_xy = calc_xy_all - meas_xyo_all[:, :2]
        diff_ome = xfcapi.angularDifference(calc_omes_all, meas_xyo_all[:, 2])
        retval = np.hstack([diff_vecs_xy, diff_ome.reshape(npts, 1)]).flatten()
        if return_value_flag == 1:
            # return scalar sum of squared residuals
            retval = sum(abs(retval))
        elif return_value_flag == 2:
            # return DOF-normalized chisq
            # TODO: check this calculation
            denom = 3 * npts - len(gFit) - 1.
            if denom != 0:
                nu_fac = 1. / denom
            else:
                nu_fac = 1.
            retval = nu_fac * sum(retval**2)
    return retval
Ejemplo n.º 6
0
def make_reflection_patches(instr_cfg,
                            tth_eta,
                            ang_pixel_size,
                            omega=None,
                            tth_tol=0.2,
                            eta_tol=1.0,
                            rmat_c=np.eye(3),
                            tvec_c=np.zeros((3, 1)),
                            npdiv=1,
                            quiet=False,
                            compute_areas_func=gutil.compute_areas):
    """
    Make angular patches on a detector.

    panel_dims are [(xmin, ymin), (xmax, ymax)] in mm

    pixel_pitch is [row_size, column_size] in mm

    FIXME: DISTORTION HANDING IS STILL A KLUDGE!!!

    patches are:

                 delta tth
    d  ------------- ... -------------
    e  | x | x | x | ... | x | x | x |
    l  ------------- ... -------------
    t                 .
    a                 .
                     .
    e  ------------- ... -------------
    t  | x | x | x | ... | x | x | x |
    a  ------------- ... -------------

    outputs are:
        (tth_vtx, eta_vtx),
        (x_vtx, y_vtx),
        connectivity,
        subpixel_areas,
        (x_center, y_center),
        (i_row, j_col)
    """
    npts = len(tth_eta)

    # detector quantities
    rmat_d = xfcapi.makeRotMatOfExpMap(
        np.r_[instr_cfg['detector']['transform']['tilt']])
    tvec_d = np.r_[instr_cfg['detector']['transform']['translation']]
    pixel_size = instr_cfg['detector']['pixels']['size']

    frame_nrows = instr_cfg['detector']['pixels']['rows']
    frame_ncols = instr_cfg['detector']['pixels']['columns']

    panel_dims = (
        -0.5 * np.r_[frame_ncols * pixel_size[1], frame_nrows * pixel_size[0]],
        0.5 * np.r_[frame_ncols * pixel_size[1], frame_nrows * pixel_size[0]])
    row_edges = np.arange(frame_nrows + 1)[::-1]*pixel_size[1] \
        + panel_dims[0][1]
    col_edges = np.arange(frame_ncols + 1)*pixel_size[0] \
        + panel_dims[0][0]

    # handle distortion
    distortion = None
    if distortion_key in instr_cfg['detector']:
        distortion_cfg = instr_cfg['detector'][distortion_key]
        if distortion_cfg is not None:
            try:
                func_name = distortion_cfg['function_name']
                dparams = distortion_cfg['parameters']
                distortion = distortion_pkg.get_mapping(func_name, dparams)
            except (KeyError):
                raise RuntimeError("problem with distortion specification")

    # sample frame
    chi = instr_cfg['oscillation_stage']['chi']
    tvec_s = np.r_[instr_cfg['oscillation_stage']['translation']]

    # beam vector
    bvec = np.r_[instr_cfg['beam']['vector']]

    # data to loop
    # ??? WOULD IT BE CHEAPER TO CARRY ZEROS OR USE CONDITIONAL?
    if omega is None:
        full_angs = np.hstack([tth_eta, np.zeros((npts, 1))])
    else:
        full_angs = np.hstack([tth_eta, omega.reshape(npts, 1)])

    patches = []
    for angs, pix in zip(full_angs, ang_pixel_size):
        # calculate bin edges for patch based on local angular pixel size
        # tth
        ntths, tth_edges = gutil.make_tolerance_grid(bin_width=np.degrees(
            pix[0]),
                                                     window_width=tth_tol,
                                                     num_subdivisions=npdiv)

        # eta
        netas, eta_edges = gutil.make_tolerance_grid(bin_width=np.degrees(
            pix[1]),
                                                     window_width=eta_tol,
                                                     num_subdivisions=npdiv)

        # FOR ANGULAR MESH
        conn = gutil.cellConnectivity(netas, ntths, origin='ll')

        # meshgrid args are (cols, rows), a.k.a (fast, slow)
        m_tth, m_eta = np.meshgrid(tth_edges, eta_edges)
        npts_patch = m_tth.size

        # calculate the patch XY coords from the (tth, eta) angles
        # !!! will CHEAT and ignore the small perturbation the different
        #     omega angle values causes and simply use the central value
        gVec_angs_vtx = np.tile(angs, (npts_patch, 1)) \
            + np.radians(
                np.vstack([m_tth.flatten(),
                            m_eta.flatten(),
                            np.zeros(npts_patch)
                ]).T
            )

        xy_eval_vtx, rmats_s, on_plane = _project_on_detector_plane(
            gVec_angs_vtx,
            rmat_d,
            rmat_c,
            chi,
            tvec_d,
            tvec_c,
            tvec_s,
            distortion,
            beamVec=bvec)

        areas = compute_areas_func(xy_eval_vtx, conn)

        # EVALUATION POINTS
        # !!! for lack of a better option will use centroids
        tth_eta_cen = gutil.cellCentroids(np.atleast_2d(gVec_angs_vtx[:, :2]),
                                          conn)

        gVec_angs = np.hstack(
            [tth_eta_cen, np.tile(angs[2], (len(tth_eta_cen), 1))])

        xy_eval, rmats_s, on_plane = _project_on_detector_plane(gVec_angs,
                                                                rmat_d,
                                                                rmat_c,
                                                                chi,
                                                                tvec_d,
                                                                tvec_c,
                                                                tvec_s,
                                                                distortion,
                                                                beamVec=bvec)

        row_indices = gutil.cellIndices(row_edges, xy_eval[:, 1])
        col_indices = gutil.cellIndices(col_edges, xy_eval[:, 0])

        # append patch data to list
        patches.append(
            ((gVec_angs_vtx[:, 0].reshape(m_tth.shape),
              gVec_angs_vtx[:, 1].reshape(m_tth.shape)),
             (xy_eval_vtx[:, 0].reshape(m_tth.shape),
              xy_eval_vtx[:, 1].reshape(m_tth.shape)), conn,
             areas.reshape(netas, ntths), (xy_eval[:, 0].reshape(netas, ntths),
                                           xy_eval[:, 1].reshape(netas,
                                                                 ntths)),
             (row_indices.reshape(netas,
                                  ntths), col_indices.reshape(netas, ntths))))
        pass  # close loop over angles
    return patches
Ejemplo n.º 7
0
def simulateLauePattern(hkls,
                        bMat,
                        rmat_d,
                        tvec_d,
                        panel_dims,
                        panel_buffer=5,
                        minEnergy=8,
                        maxEnergy=24,
                        rmat_s=np.eye(3),
                        grain_params=None,
                        distortion=None,
                        beamVec=None):

    if beamVec is None:
        beamVec = xfcapi.bVec_ref

    # parse energy ranges
    multipleEnergyRanges = False
    if hasattr(maxEnergy, '__len__'):
        assert len(maxEnergy) == len(minEnergy), \
            'energy cutoff ranges must have the same length'
        multipleEnergyRanges = True
        lmin = []
        lmax = []
        for i in range(len(maxEnergy)):
            lmin.append(processWavelength(maxEnergy[i]))
            lmax.append(processWavelength(minEnergy[i]))
    else:
        lmin = processWavelength(maxEnergy)
        lmax = processWavelength(minEnergy)

    # process crystal rmats and inverse stretches
    if grain_params is None:
        grain_params = np.atleast_2d(
            [0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.])

    n_grains = len(grain_params)

    # dummy translation vector... make input
    tvec_s = np.zeros((3, 1))

    # number of hkls
    nhkls_tot = hkls.shape[1]

    # unit G-vectors in crystal frame
    ghat_c = mutil.unitVector(np.dot(bMat, hkls))

    # pre-allocate output arrays
    xy_det = np.nan * np.ones((n_grains, nhkls_tot, 2))
    hkls_in = np.nan * np.ones((n_grains, 3, nhkls_tot))
    angles = np.nan * np.ones((n_grains, nhkls_tot, 2))
    dspacing = np.nan * np.ones((n_grains, nhkls_tot))
    energy = np.nan * np.ones((n_grains, nhkls_tot))
    """
    LOOP OVER GRAINS
    """

    for iG, gp in enumerate(grain_params):
        rmat_c = xfcapi.makeRotMatOfExpMap(gp[:3])
        tvec_c = gp[3:6].reshape(3, 1)
        vInv_s = mutil.vecMVToSymm(gp[6:].reshape(6, 1))

        # stretch them: V^(-1) * R * Gc
        ghat_s_str = mutil.unitVector(np.dot(vInv_s, np.dot(rmat_c, ghat_c)))
        ghat_c_str = np.dot(rmat_c.T, ghat_s_str)

        # project
        dpts = xfcapi.gvecToDetectorXY(ghat_c_str.T,
                                       rmat_d,
                                       rmat_s,
                                       rmat_c,
                                       tvec_d,
                                       tvec_s,
                                       tvec_c,
                                       beamVec=beamVec).T

        # check intersections with detector plane
        canIntersect = ~np.isnan(dpts[0, :])
        npts_in = sum(canIntersect)

        if np.any(canIntersect):
            dpts = dpts[:, canIntersect].reshape(2, npts_in)
            dhkl = hkls[:, canIntersect].reshape(3, npts_in)

            # back to angles
            tth_eta, gvec_l = xfcapi.detectorXYToGvec(dpts.T,
                                                      rmat_d,
                                                      rmat_s,
                                                      tvec_d,
                                                      tvec_s,
                                                      tvec_c,
                                                      beamVec=beamVec)
            tth_eta = np.vstack(tth_eta).T

            # warp measured points
            if distortion is not None:
                dpts = distortion.apply_inverse(dpts)

            # plane spacings and energies
            dsp = 1. / mutil.columnNorm(np.dot(bMat, dhkl))
            wlen = 2 * dsp * np.sin(0.5 * tth_eta[:, 0])

            # find on spatial extent of detector
            xTest = np.logical_and(
                dpts[0, :] >= -0.5 * panel_dims[1] + panel_buffer,
                dpts[0, :] <= 0.5 * panel_dims[1] - panel_buffer)
            yTest = np.logical_and(
                dpts[1, :] >= -0.5 * panel_dims[0] + panel_buffer,
                dpts[1, :] <= 0.5 * panel_dims[0] - panel_buffer)

            onDetector = np.logical_and(xTest, yTest)
            if multipleEnergyRanges:
                validEnergy = np.zeros(len(wlen), dtype=bool)
                for i in range(len(lmin)):
                    validEnergy = validEnergy | \
                        np.logical_and(wlen >= lmin[i], wlen <= lmax[i])
                    pass
            else:
                validEnergy = np.logical_and(wlen >= lmin, wlen <= lmax)
                pass

            # index for valid reflections
            keepers = np.where(np.logical_and(onDetector, validEnergy))[0]

            # assign output arrays
            xy_det[iG][keepers, :] = dpts[:, keepers].T
            hkls_in[iG][:, keepers] = dhkl[:, keepers]
            angles[iG][keepers, :] = tth_eta[keepers, :]
            dspacing[iG, keepers] = dsp[keepers]
            energy[iG, keepers] = processWavelength(wlen[keepers])
            pass
        pass
    return xy_det, hkls_in, angles, dspacing, energy
Ejemplo n.º 8
0
def simulateGVecs(pd,
                  detector_params,
                  grain_params,
                  ome_range=[
                      (-np.pi, np.pi),
                  ],
                  ome_period=(-np.pi, np.pi),
                  eta_range=[
                      (-np.pi, np.pi),
                  ],
                  panel_dims=[(-204.8, -204.8), (204.8, 204.8)],
                  pixel_pitch=(0.2, 0.2),
                  distortion=None):
    """
    returns valid_ids, valid_hkl, valid_ang, valid_xy, ang_ps

    panel_dims are [(xmin, ymin), (xmax, ymax)] in mm

    pixel_pitch is [row_size, column_size] in mm

    simulate the monochormatic scattering for a specified

        - space group
        - wavelength
        - orientation
        - strain
        - position
        - detector parameters
        - oscillation axis tilt (chi)

    subject to

        - omega (oscillation) ranges (list of (min, max) tuples)
        - eta (azimuth) ranges

    pd................a hexrd.crystallography.PlaneData instance
    detector_params...a (10,) ndarray containing the tilt angles (3),
                      translation (3), chi (1), and sample frame translation
                      (3) parameters
    grain_params......a (12,) ndarray containing the exponential map (3),
                      translation (3), and inverse stretch tensor compnents
                      in Mandel-Voigt notation (6).

    * currently only one panel is supported, but this will likely change soon
    """
    bMat = pd.latVecOps['B']
    wlen = pd.wavelength
    full_hkls = _fetch_hkls_from_planedata(pd)

    # extract variables for convenience
    rMat_d = xfcapi.makeDetectorRotMat(detector_params[:3])
    tVec_d = np.ascontiguousarray(detector_params[3:6])
    chi = detector_params[6]
    tVec_s = np.ascontiguousarray(detector_params[7:10])
    rMat_c = xfcapi.makeRotMatOfExpMap(grain_params[:3])
    tVec_c = np.ascontiguousarray(grain_params[3:6])
    vInv_s = np.ascontiguousarray(grain_params[6:12])

    # first find valid G-vectors
    angList = np.vstack(
        xfcapi.oscillAnglesOfHKLs(full_hkls[:, 1:],
                                  chi,
                                  rMat_c,
                                  bMat,
                                  wlen,
                                  vInv=vInv_s))
    allAngs, allHKLs = _filter_hkls_eta_ome(full_hkls, angList, eta_range,
                                            ome_range)

    if len(allAngs) == 0:
        valid_ids = []
        valid_hkl = []
        valid_ang = []
        valid_xy = []
        ang_ps = []
    else:
        # ??? preallocate for speed?
        det_xy, rMat_s, on_plane = _project_on_detector_plane(
            allAngs, rMat_d, rMat_c, chi, tVec_d, tVec_c, tVec_s, distortion)
        #
        on_panel_x = np.logical_and(det_xy[:, 0] >= panel_dims[0][0],
                                    det_xy[:, 0] <= panel_dims[1][0])
        on_panel_y = np.logical_and(det_xy[:, 1] >= panel_dims[0][1],
                                    det_xy[:, 1] <= panel_dims[1][1])
        on_panel = np.logical_and(on_panel_x, on_panel_y)
        #
        op_idx = np.where(on_panel)[0]
        #
        valid_ang = allAngs[op_idx, :]
        valid_ang[:, 2] = xf.mapAngle(valid_ang[:, 2], ome_period)
        valid_ids = allHKLs[op_idx, 0]
        valid_hkl = allHKLs[op_idx, 1:]
        valid_xy = det_xy[op_idx, :]
        ang_ps = angularPixelSize(valid_xy,
                                  pixel_pitch,
                                  rMat_d,
                                  rMat_s,
                                  tVec_d,
                                  tVec_s,
                                  tVec_c,
                                  distortion=distortion)

    return valid_ids, valid_hkl, valid_ang, valid_xy, ang_ps
Ejemplo n.º 9
0
def simulateOmeEtaMaps(omeEdges,
                       etaEdges,
                       planeData,
                       expMaps,
                       chi=0.,
                       etaTol=None,
                       omeTol=None,
                       etaRanges=None,
                       omeRanges=None,
                       bVec=xf.bVec_ref,
                       eVec=xf.eta_ref,
                       vInv=xf.vInv_ref):
    """
    Simulate spherical maps.

    Parameters
    ----------
    omeEdges : TYPE
        DESCRIPTION.
    etaEdges : TYPE
        DESCRIPTION.
    planeData : TYPE
        DESCRIPTION.
    expMaps : (3, n) ndarray
        DESCRIPTION.
    chi : TYPE, optional
        DESCRIPTION. The default is 0..
    etaTol : TYPE, optional
        DESCRIPTION. The default is None.
    omeTol : TYPE, optional
        DESCRIPTION. The default is None.
    etaRanges : TYPE, optional
        DESCRIPTION. The default is None.
    omeRanges : TYPE, optional
        DESCRIPTION. The default is None.
    bVec : TYPE, optional
        DESCRIPTION. The default is xf.bVec_ref.
    eVec : TYPE, optional
        DESCRIPTION. The default is xf.eta_ref.
    vInv : TYPE, optional
        DESCRIPTION. The default is xf.vInv_ref.

    Returns
    -------
    eta_ome : TYPE
        DESCRIPTION.

    Notes
    -----
    all angular info is entered in degrees

    ??? might want to creat module-level angluar unit flag
    ??? might want to allow resvers delta omega

    """
    # convert to radians
    etaEdges = np.radians(np.sort(etaEdges))
    omeEdges = np.radians(np.sort(omeEdges))

    omeIndices = list(range(len(omeEdges)))
    etaIndices = list(range(len(etaEdges)))

    i_max = omeIndices[-1]
    j_max = etaIndices[-1]

    etaMin = etaEdges[0]
    etaMax = etaEdges[-1]
    omeMin = omeEdges[0]
    omeMax = omeEdges[-1]
    if omeRanges is None:
        omeRanges = [
            [omeMin, omeMax],
        ]

    if etaRanges is None:
        etaRanges = [
            [etaMin, etaMax],
        ]

    # signed deltas IN RADIANS
    del_ome = omeEdges[1] - omeEdges[0]
    del_eta = etaEdges[1] - etaEdges[0]

    delOmeSign = np.sign(del_eta)

    # tolerances are in degrees (easier)
    if omeTol is None:
        omeTol = abs(del_ome)
    else:
        omeTol = np.radians(omeTol)
    if etaTol is None:
        etaTol = abs(del_eta)
    else:
        etaTol = np.radians(etaTol)

    # pixel dialtions
    dpix_ome = round(omeTol / abs(del_ome))
    dpix_eta = round(etaTol / abs(del_eta))

    i_dil, j_dil = np.meshgrid(np.arange(-dpix_ome, dpix_ome + 1),
                               np.arange(-dpix_eta, dpix_eta + 1))

    # get symmetrically expanded hkls from planeData
    sym_hkls = planeData.getSymHKLs()
    nhkls = len(sym_hkls)

    # make things C-contiguous for use in xfcapi functions
    expMaps = np.array(expMaps.T, order='C')
    nOrs = len(expMaps)

    bMat = np.array(planeData.latVecOps['B'], order='C')
    wlen = planeData.wavelength

    bVec = np.array(bVec.flatten(), order='C')
    eVec = np.array(eVec.flatten(), order='C')
    vInv = np.array(vInv.flatten(), order='C')

    eta_ome = np.zeros((nhkls, max(omeIndices), max(etaIndices)), order='C')
    for iHKL in range(nhkls):
        these_hkls = np.ascontiguousarray(sym_hkls[iHKL].T, dtype=float)
        for iOr in range(nOrs):
            rMat_c = xfcapi.makeRotMatOfExpMap(expMaps[iOr, :])
            angList = np.vstack(
                xfcapi.oscillAnglesOfHKLs(these_hkls,
                                          chi,
                                          rMat_c,
                                          bMat,
                                          wlen,
                                          beamVec=bVec,
                                          etaVec=eVec,
                                          vInv=vInv))
            if not np.all(np.isnan(angList)):
                #
                angList[:, 1] = xf.mapAngle(
                    angList[:, 1], [etaEdges[0], etaEdges[0] + 2 * np.pi])
                angList[:, 2] = xf.mapAngle(
                    angList[:, 2], [omeEdges[0], omeEdges[0] + 2 * np.pi])
                #
                # do eta ranges
                angMask_eta = np.zeros(len(angList), dtype=bool)
                for etas in etaRanges:
                    angMask_eta = np.logical_or(
                        angMask_eta,
                        xf.validateAngleRanges(angList[:, 1], etas[0],
                                               etas[1]))

                # do omega ranges
                ccw = True
                angMask_ome = np.zeros(len(angList), dtype=bool)
                for omes in omeRanges:
                    if omes[1] - omes[0] < 0:
                        ccw = False
                    angMask_ome = np.logical_or(
                        angMask_ome,
                        xf.validateAngleRanges(angList[:, 2],
                                               omes[0],
                                               omes[1],
                                               ccw=ccw))

                # mask angles list, hkls
                angMask = np.logical_and(angMask_eta, angMask_ome)

                culledTTh = angList[angMask, 0]
                culledEta = angList[angMask, 1]
                culledOme = angList[angMask, 2]

                for iTTh in range(len(culledTTh)):
                    culledEtaIdx = np.where(etaEdges - culledEta[iTTh] > 0)[0]
                    if len(culledEtaIdx) > 0:
                        culledEtaIdx = culledEtaIdx[0] - 1
                        if culledEtaIdx < 0:
                            culledEtaIdx = None
                    else:
                        culledEtaIdx = None
                    culledOmeIdx = np.where(omeEdges - culledOme[iTTh] > 0)[0]
                    if len(culledOmeIdx) > 0:
                        if delOmeSign > 0:
                            culledOmeIdx = culledOmeIdx[0] - 1
                        else:
                            culledOmeIdx = culledOmeIdx[-1]
                        if culledOmeIdx < 0:
                            culledOmeIdx = None
                    else:
                        culledOmeIdx = None

                    if culledEtaIdx is not None and culledOmeIdx is not None:
                        if dpix_ome > 0 or dpix_eta > 0:
                            i_sup = omeIndices[culledOmeIdx] + \
                                np.array([i_dil.flatten()], dtype=int)
                            j_sup = etaIndices[culledEtaIdx] + \
                                np.array([j_dil.flatten()], dtype=int)

                            # catch shit that falls off detector...
                            # maybe make this fancy enough to wrap at 2pi?
                            idx_mask = np.logical_and(
                                np.logical_and(i_sup >= 0, i_sup < i_max),
                                np.logical_and(j_sup >= 0, j_sup < j_max))
                            eta_ome[iHKL, i_sup[idx_mask],
                                    j_sup[idx_mask]] = 1.
                        else:
                            eta_ome[iHKL, omeIndices[culledOmeIdx],
                                    etaIndices[culledEtaIdx]] = 1.
                            pass  # close conditional on pixel dilation
                        pass  # close conditional on ranges
                    pass  # close for loop on valid reflections
                pass  # close conditional for valid angles
    return eta_ome