Exemplo n.º 1
0
    def __init__(self,
                 plane_data,
                 instr,
                 crystal_params=None,
                 sample_rmat=None,
                 min_energy=5.,
                 max_energy=35.,
                 tth_width=None,
                 eta_width=None,
                 eta_period=np.r_[-180., 180.]):
        self.plane_data = plane_data
        self._instrument = instr
        if crystal_params is None:
            self._crystal_params = np.hstack(
                [constants.zeros_3, constants.zeros_3, constants.identity_6x1])
        else:
            self.crystal_params = crystal_params

        self._min_energy = min_energy
        self._max_energy = max_energy
        if sample_rmat is None:
            self._sample_rmat = constants.identity_3x3
        else:
            self.sample_rmat = sample_rmat

        self.tth_width = tth_width
        self.eta_width = eta_width

        eta_period = np.asarray(eta_period, float).flatten()
        assert len(eta_period) == 2, "eta period must be a 2-element sequence"
        if xfcapi.angularDifference(
                eta_period[0], eta_period[1], units='degrees') > 1e-4:
            raise RuntimeError("period specification is not 360 degrees")
        self._eta_period = eta_period
Exemplo n.º 2
0
Arquivo: grains.py Projeto: cjh1/hexrd
def matchOmegas(xyo_det,
                hkls_idx,
                chi,
                rMat_c,
                bMat,
                wavelength,
                vInv=vInv_ref,
                beamVec=bVec_ref,
                etaVec=eta_ref,
                omePeriod=None):
    """
    For a given list of (x, y, ome) points, outputs the index into the results
    from oscillAnglesOfHKLs, including the calculated omega values.
    """
    # get omegas for rMat_s calculation
    if omePeriod is not None:
        meas_omes = xfcapi.mapAngle(xyo_det[:, 2], omePeriod)
    else:
        meas_omes = xyo_det[:, 2]

    oangs0, oangs1 = xfcapi.oscillAnglesOfHKLs(hkls_idx.T,
                                               chi,
                                               rMat_c,
                                               bMat,
                                               wavelength,
                                               vInv=vInv,
                                               beamVec=beamVec,
                                               etaVec=etaVec)
    if np.any(np.isnan(oangs0)):
        # debugging
        # TODO: remove this
        import pdb
        pdb.set_trace()
        nanIdx = np.where(np.isnan(oangs0[:, 0]))[0]
        errorString = "Infeasible parameters for hkls:\n"
        for i in range(len(nanIdx)):
            errorString += "%d  %d  %d\n" % tuple(hkls_idx[:, nanIdx[i]])
        raise RuntimeError(errorString)
    else:
        # CAPI version gives vstacked angles... must be (2, nhkls)
        calc_omes = np.vstack([oangs0[:, 2], oangs1[:, 2]])
    if omePeriod is not None:
        calc_omes = np.vstack([
            xfcapi.mapAngle(oangs0[:, 2], omePeriod),
            xfcapi.mapAngle(oangs1[:, 2], omePeriod)
        ])
    # do angular difference
    diff_omes = xfcapi.angularDifference(np.tile(meas_omes, (2, 1)), calc_omes)
    match_omes = np.argsort(diff_omes, axis=0) == 0
    calc_omes = calc_omes.T.flatten()[match_omes.T.flatten()]

    return match_omes, calc_omes
Exemplo n.º 3
0
    def __init__(self, plane_data, instr, tvec=np.zeros(3),
                 eta_steps=360, eta_period=np.r_[-180., 180.]):
        self._plane_data = plane_data
        self._instrument = instr
        tvec = np.asarray(tvec, float).flatten()
        assert len(tvec) == 3, "tvec input must have exactly 3 elements"
        self._tvec = tvec
        self._eta_steps = eta_steps

        eta_period = np.asarray(eta_period, float).flatten()
        assert len(eta_period) == 2, "eta period must be a 2-element sequence"
        if xfcapi.angularDifference(eta_period[0], eta_period[1],
                                    units='degrees') > 1e-4:
            raise RuntimeError("period specification is not 360 degrees")
        self._eta_period = eta_period
Exemplo n.º 4
0
 def eta_period(self, x):
     x = np.asarray(x, float).flatten()
     assert len(x) == 2, "eta period must be a 2-element sequence"
     if xfcapi.angularDifference(x[0], x[1], units='degrees') > 1e-4:
         raise RuntimeError("period specification is not 360 degrees")
     self._eta_period = x
Exemplo n.º 5
0
    def generate_ring_points(self, tths, etas, panel, display_mode):
        delta_eta_nom = np.degrees(np.median(np.diff(etas)))
        ring_pts = []
        skipped_tth = []
        for i, tth in enumerate(tths):
            # construct ideal angular coords
            ang_crds = np.vstack([np.tile(tth, len(etas)), etas]).T

            # !!! must apply offset
            xys_full = panel.angles_to_cart(ang_crds, tvec_c=self.tvec)

            # skip if ring not on panel
            if len(xys_full) == 0:
                skipped_tth.append(i)
                continue

            # clip to detector panel
            xys, on_panel = panel.clip_to_panel(xys_full, buffer_edges=False)

            if display_mode == ViewType.polar:
                # !!! apply offset correction
                ang_crds, _ = panel.cart_to_angles(xys,
                                                   tvec_c=self.instrument.tvec)

                if len(ang_crds) == 0:
                    skipped_tth.append(i)
                    continue

                # Swap columns, convert to degrees
                ang_crds[:, [0, 1]] = np.degrees(ang_crds[:, [1, 0]])

                # fix eta period
                ang_crds[:, 0] = xfcapi.mapAngle(ang_crds[:, 0],
                                                 self.eta_period,
                                                 units='degrees')

                # sort points for monotonic eta
                eidx = np.argsort(ang_crds[:, 0])
                ang_crds = ang_crds[eidx, :]

                # branch cut
                delta_eta_est = np.median(np.diff(ang_crds[:, 0]))
                cut_on_panel = bool(
                    xfcapi.angularDifference(np.min(ang_crds[:, 0]),
                                             np.max(ang_crds[:, 0]),
                                             units='degrees') < 2 *
                    delta_eta_est)
                if cut_on_panel and len(ang_crds) > 2:
                    split_idx = np.argmax(
                        np.abs(np.diff(ang_crds[:, 0]) - delta_eta_est)) + 1

                    ang_crds = np.vstack([
                        ang_crds[:split_idx, :], nans_row,
                        ang_crds[split_idx:, :]
                    ])

                # append to list with nan padding
                ring_pts.append(np.vstack([ang_crds, nans_row]))

            elif display_mode in [ViewType.raw, ViewType.cartesian]:

                if display_mode == ViewType.raw:
                    # !!! distortion
                    if panel.distortion is not None:
                        xys = panel.distortion.apply_inverse(xys)

                    # Convert to pixel coordinates
                    # ??? keep in pixels?
                    xys = panel.cartToPixel(xys)

                diff_tol = np.radians(self.delta_eta) + 1e-4
                ring_breaks = np.where(
                    np.abs(np.diff(etas[on_panel])) > diff_tol)[0] + 1
                n_segments = len(ring_breaks) + 1

                if n_segments == 1:
                    ring_pts.append(np.vstack([xys, nans_row]))
                else:
                    src_len = sum(on_panel)
                    dst_len = src_len + len(ring_breaks)
                    nxys = np.nan * np.ones((dst_len, 2))
                    ii = 0
                    for i in range(n_segments - 1):
                        jj = ring_breaks[i]
                        nxys[ii + i:jj + i, :] = xys[ii:jj, :]
                        ii = jj
                    i = n_segments - 1
                    nxys[ii + i:, :] = xys[ii:, :]
                    ring_pts.append(np.vstack([nxys, nans_row]))

        return ring_pts, skipped_tth
Exemplo n.º 6
0
def fit_grain_FF_reduced(grain_id):
    """
    Perform non-linear least-square fit for the specified grain.

    Parameters
    ----------
    grain_id : int
        The grain id.

    Returns
    -------
    grain_id : int
        The grain id.
    completeness : float
        The ratio of predicted to measured (observed) Bragg reflections.
    chisq: float
        Figure of merit describing the sum of squared residuals for each Bragg
        reflection in the form (x, y, omega) normalized by the total number of
        degrees of freedom.
    grain_params : array_like
        The optimized grain parameters
        [<orientation [3]>, <centroid [3]> <inverse stretch [6]>].

    Notes
    -----
    input parameters are
    [plane_data, instrument, imgser_dict,
    tth_tol, eta_tol, ome_tol, npdiv, threshold]
    """
    grains_table = paramMP['grains_table']
    plane_data = paramMP['plane_data']
    instrument = paramMP['instrument']
    imgser_dict = paramMP['imgser_dict']
    tth_tol = paramMP['tth_tol']
    eta_tol = paramMP['eta_tol']
    ome_tol = paramMP['ome_tol']
    npdiv = paramMP['npdiv']
    refit = paramMP['refit']
    threshold = paramMP['threshold']
    eta_ranges = paramMP['eta_ranges']
    ome_period = paramMP['ome_period']
    analysis_dirname = paramMP['analysis_dirname']
    prefix = paramMP['spots_filename']
    spots_filename = None if prefix is None else prefix % grain_id

    grain = grains_table[grain_id]
    grain_params = grain[3:15]

    for tols in zip(tth_tol, eta_tol, ome_tol):
        complvec, results = instrument.pull_spots(plane_data,
                                                  grain_params,
                                                  imgser_dict,
                                                  tth_tol=tols[0],
                                                  eta_tol=tols[1],
                                                  ome_tol=tols[2],
                                                  npdiv=npdiv,
                                                  threshold=threshold,
                                                  eta_ranges=eta_ranges,
                                                  ome_period=ome_period,
                                                  dirname=analysis_dirname,
                                                  filename=spots_filename,
                                                  save_spot_list=False,
                                                  quiet=True,
                                                  check_only=False,
                                                  interp='nearest')

        # ======= DETERMINE VALID REFLECTIONS =======

        culled_results = dict.fromkeys(results)
        num_refl_tot = 0
        num_refl_valid = 0
        for det_key in culled_results:
            panel = instrument.detectors[det_key]
            '''
            grab panel results:
                peak_id
                hkl_id
                hkl
                sum_int
                max_int,
                pred_angs,
                meas_angs,
                meas_xy
            '''
            presults = results[det_key]
            nrefl = len(presults)

            # make data arrays
            refl_ids = np.empty(nrefl)
            max_int = np.empty(nrefl)
            for i, spot_data in enumerate(presults):
                refl_ids[i] = spot_data[0]
                max_int[i] = spot_data[4]

            valid_refl_ids = refl_ids >= 0

            # find unsaturated spots on this panel
            unsat_spots = np.ones(len(valid_refl_ids), dtype=bool)
            if panel.saturation_level is not None:
                unsat_spots[valid_refl_ids] = \
                    max_int[valid_refl_ids] < panel.saturation_level

            idx = np.logical_and(valid_refl_ids, unsat_spots)

            # if an overlap table has been written, load it and use it
            overlaps = np.zeros_like(idx, dtype=bool)
            try:
                ot = np.load(
                    os.path.join(analysis_dirname,
                                 os.path.join(det_key, 'overlap_table.npz')))
                for key in ot.keys():
                    for this_table in ot[key]:
                        these_overlaps = np.where(this_table[:,
                                                             0] == grain_id)[0]
                        if len(these_overlaps) > 0:
                            mark_these = np.array(this_table[these_overlaps,
                                                             1],
                                                  dtype=int)
                            otidx = [
                                np.where(refl_ids == mt)[0]
                                for mt in mark_these
                            ]
                            overlaps[otidx] = True
                idx = np.logical_and(idx, ~overlaps)
                # logger.info("found overlap table for '%s'", det_key)
            except (IOError, IndexError):
                # logger.info("no overlap table found for '%s'", det_key)
                pass

            # attach to proper dict entry
            # FIXME: want to avoid looping again here
            culled_results[det_key] = [presults[i] for i in np.where(idx)[0]]
            num_refl_tot += len(valid_refl_ids)
            num_refl_valid += sum(valid_refl_ids)

            pass  # now we have culled data

        # CAVEAT: completeness from pullspots only; incl saturated and overlaps
        # <JVB 2015-12-15>
        completeness = num_refl_valid / float(num_refl_tot)

        # ======= DO LEASTSQ FIT =======

        if num_refl_valid <= 12:  # not enough reflections to fit... exit
            return grain_id, completeness, np.inf, grain_params
        else:
            grain_params = fitGrain(grain_params, instrument, culled_results,
                                    plane_data.latVecOps['B'],
                                    plane_data.wavelength)
            # get chisq
            # TODO: do this while evaluating fit???
            chisq = objFuncFitGrain(grain_params[gFlag_ref],
                                    grain_params,
                                    gFlag_ref,
                                    instrument,
                                    culled_results,
                                    plane_data.latVecOps['B'],
                                    plane_data.wavelength,
                                    ome_period,
                                    simOnly=False,
                                    return_value_flag=2)
            pass  # end conditional on fit
        pass  # end tolerance looping

    if refit is not None:
        # first get calculated x, y, ome from previous solution
        # NOTE: this result is a dict
        xyo_det_fit_dict = objFuncFitGrain(grain_params[gFlag_ref],
                                           grain_params,
                                           gFlag_ref,
                                           instrument,
                                           culled_results,
                                           plane_data.latVecOps['B'],
                                           plane_data.wavelength,
                                           ome_period,
                                           simOnly=True,
                                           return_value_flag=2)

        # make dict to contain new culled results
        culled_results_r = dict.fromkeys(culled_results)
        num_refl_valid = 0
        for det_key in culled_results_r:
            presults = culled_results[det_key]

            if not presults:
                culled_results_r[det_key] = []
                continue

            ims = imgser_dict[det_key]
            ome_step = sum(np.r_[-1, 1] * ims.metadata['omega'][0, :])

            xyo_det = np.atleast_2d(
                np.vstack([np.r_[x[7], x[6][-1]] for x in presults]))

            xyo_det_fit = xyo_det_fit_dict[det_key]

            xpix_tol = refit[0] * panel.pixel_size_col
            ypix_tol = refit[0] * panel.pixel_size_row
            fome_tol = refit[1] * ome_step

            # define difference vectors for spot fits
            x_diff = abs(xyo_det[:, 0] - xyo_det_fit['calc_xy'][:, 0])
            y_diff = abs(xyo_det[:, 1] - xyo_det_fit['calc_xy'][:, 1])
            ome_diff = np.degrees(
                xfcapi.angularDifference(xyo_det[:, 2],
                                         xyo_det_fit['calc_omes']))

            # filter out reflections with centroids more than
            # a pixel and delta omega away from predicted value
            idx_new = np.logical_and(
                x_diff <= xpix_tol,
                np.logical_and(y_diff <= ypix_tol, ome_diff <= fome_tol))

            # attach to proper dict entry
            culled_results_r[det_key] = [
                presults[i] for i in np.where(idx_new)[0]
            ]

            num_refl_valid += sum(idx_new)
            pass

        # only execute fit if left with enough reflections
        if num_refl_valid > 12:
            grain_params = fitGrain(grain_params, instrument, culled_results_r,
                                    plane_data.latVecOps['B'],
                                    plane_data.wavelength)
            # get chisq
            # TODO: do this while evaluating fit???
            chisq = objFuncFitGrain(grain_params[gFlag_ref],
                                    grain_params,
                                    gFlag_ref,
                                    instrument,
                                    culled_results_r,
                                    plane_data.latVecOps['B'],
                                    plane_data.wavelength,
                                    ome_period,
                                    simOnly=False,
                                    return_value_flag=2)
            pass
        pass  # close refit conditional
    return grain_id, completeness, chisq, grain_params
Exemplo n.º 7
0
Arquivo: grains.py Projeto: 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