def pseudovoigtfit(xdata, ydata, x0=None):
    [m, b, CoD, yD, err, mErr,
     bErr] = dm.linReg(np.concatenate((xdata[0:5], xdata[-5:])),
                       np.concatenate((ydata[0:5], ydata[-5:])))
    [yMax, i] = bf.max(ydata - (m * xdata + b))
    if x0 is None:
        yLeft = m * xdata[0] + b
        yRight = m * xdata[-1] + b
        ySum = np.sum(ydata * np.abs(np.gradient(xdata))) - (0.5 * np.abs(
            (xdata[-1] - xdata[0]) * (yRight - yLeft)) + np.abs(
                bf.min(yLeft, yRight)[0] *
                (xdata[-1] - xdata[0])))  # estimated area of peak
        xMax = xdata[i]  # find belonging x value
        sigma = ySum / (yMax * np.pi)  # estimate width of peak
        x0 = [ySum, sigma, xMax, m, b, 0.5]  # start values Pseudo-Voigt
    x_par = op.leastsq(pseudovoigtopt, x0, (xdata, ydata))
    x = x_par[0]
    yFit = pseudovoigtfunction(x, xdata)
    # check quality of peak
    meanNoise = (yLeft + yRight) / 2
    peakQuality = (yMax + meanNoise) / (meanNoise + 3 * meanNoise**0.5)
    err = 0
    # adapt dimensions of x if x0 has different dimensions
    if bf.size(x0) != bf.size(x):
        x = np.transpose(x)
    return x, x0, yMax, yFit, peakQuality, err, meanNoise
def pearson7fit(xdata, ydata, x0=None):
    [m, b, CoD, yD, err, mErr,
     bErr] = dm.linReg(np.concatenate((xdata[0:5], xdata[-5:])),
                       np.concatenate((ydata[0:5], ydata[-5:])))
    [yMax, i] = bf.max(ydata - (m * xdata + b))
    if x0 is None:
        yLeft = m * xdata[1] + b
        yRight = m * xdata[-1] + b
        ySum = np.sum(ydata * np.abs(np.gradient(xdata))) - (0.5 * np.abs(
            (xdata[-1] - xdata[0]) * (yRight - yLeft)) + np.abs(
                bf.min(yLeft, yRight)[0] *
                (xdata[-1] - xdata[0])))  # estimated area of peak
        xMax = xdata[i]  # find belonging x value
        sigma = ySum / (yMax * (2 * np.pi)**0.5)  # estimate width of peak
        x0 = [ySum, sigma, xMax, m, b, 2.0]  # start values Pearson7
    else:
        x0 = [x0[0], x0[1], x0[2], x0[3], x0[4], 1]  # start values Pearson7
    x_par = op.leastsq(pearson7opt, x0, (xdata, ydata))
    x = x_par[0]
    yFit = pearson7function(x, xdata)
    # adapt x to fit peak description
    x = [x[0], x[1] / (2 * x[5] - 3)**0.5, x[2], x[3], x[4], x[5]]
    # check quality of peak
    meanNoise = (yLeft + yRight) / 2
    peakQuality = (yMax + meanNoise) / (meanNoise + 3 * meanNoise**0.5)
    err = 0
    # adapt dimensions of x if x0 has different dimensions
    if bf.size(x0) != bf.size(x):
        x = np.transpose(x)
    return x, x0, yMax, yFit, peakQuality, err, meanNoise
def sin2PsiAnalysis(data, maxPsi=None):
    # data: dVals, errVals, tauVals, phiVals, psiVals, hklVal, s1Val, hs2Val, ibVals???
    # extract needed data
    dVals = data['dVals']
    dErrVals = data['dErr']
    tauVals = data['tauVals']
    phiVals = data['phiVals']
    psiVals = data['psiVals']
    ibVals = data['ibVals']
    hklVal = data['hklVal']
    s1Val = data['s1Val']
    hs2Val = data['hs2Val']
    a0Val = bf.getDictValOrDef(data, 'a0Val')
    # define derived values
    phiUni = np.sort(np.unique(phiVals))
    sinpsi2 = bc.sind(psiVals)**2
    sinpsi2Distr = np.arange(0, 1.001, 0.01)
    psiUni = np.unique(psiVals)
    psiUniWithoutZero = psiUni[psiUni != 0]
    psiSign = np.sign(psiUniWithoutZero[-1])  # sign of last psi value
    psiUni = psiUni[(np.sign(psiUni) == psiSign) |
                    (psiUni == 0)]  # only negative or positive values
    if maxPsi is not None:
        psiUni = psiUni[(psiUni <= maxPsi) & (
            psiUni >=
            -maxPsi)]  # take only values smaller than or equal to |maxPsi°|
    maxUsedPsi = np.max(np.abs(psiUni))
    sinpsi2Uni = bc.sind(psiUni)**2
    sin2psiUni = bc.sind(np.abs(2 * psiUni))
    # define global regression values
    mVals = np.zeros(6)
    bVals = np.zeros(6)
    errVals = np.zeros(6)
    errValsStar = 0
    # perform linear regressions for one peak
    sinpsi2StarSingle = s1Val / hs2Val
    sinpsi2Star = -2 * s1Val / hs2Val
    sinpsi2Plus = 1 + s1Val / hs2Val
    dValsValid = (dVals > 0) & (np.isnan(dVals) == False)
    tauValsValid = (tauVals >= 0) & (tauVals < 1e6)
    ibValsValid = (ibVals > 0) & (ibVals < 1000)
    if maxPsi is None:
        tauMean = np.sum(tauVals[tauValsValid]) / len(tauVals[tauValsValid])
        # tauMean = (max(tauVals[tauValsValid]) + min(tauVals[tauValsValid])) / 2
    else:
        # take only values smaller than or equal to | maxPsi° |
        curTau = tauVals[(psiVals <= maxPsi) & (psiVals >= -maxPsi)
                         & tauValsValid]
        tauMean = np.sum(curTau) / len(curTau)
        # tauMean = (max(curTau) + min(curTau)) / 2
    # determine mean values of IB
    valuesIb = np.zeros(len(phiUni))
    for j in range(len(phiUni)):
        if maxPsi is None:
            valuesIb[j] = np.mean(ibVals[phiVals == phiUni[j] & ibValsValid])
        else:
            valuesIb[j] = np.mean(
                ibVals[(phiVals == phiUni[j]) & (psiVals <= maxPsi) &
                       (psiVals >= -maxPsi) & ibValsValid])
    # perform linear regression of all phi values
    valsAll = np.zeros((len(psiUni), 2))
    vals45 = np.zeros((len(psiUni), 2))
    vals0_180 = np.zeros((len(psiUni), 3))
    vals90_270 = np.zeros((len(psiUni), 3))
    for i in range(len(psiUni)):
        if bf.max(bf.containsItems(psiVals, psiUni[i])[0])[0] and bf.max(
                bf.containsItems(psiVals, -psiUni[i])[0])[0]:
            # positive and negative psi values
            val0 = np.array([
                dVals[(psiVals == psiUni[i]) & (phiVals == 0) & dValsValid],
                dErrVals[(psiVals == psiUni[i]) & (phiVals == 0) & dValsValid]
            ])
            val90 = np.array([
                dVals[(psiVals == psiUni[i]) & (phiVals == 90) & dValsValid],
                dErrVals[(psiVals == psiUni[i]) & (phiVals == 90) & dValsValid]
            ])
            val180 = np.array([
                dVals[(psiVals == -psiUni[i]) & (phiVals == 0) & dValsValid],
                dErrVals[(psiVals == -psiUni[i]) & (phiVals == 0) & dValsValid]
            ])
            val270 = np.array([
                dVals[(psiVals == -psiUni[i]) & (phiVals == 90) & dValsValid],
                dErrVals[(psiVals == -psiUni[i]) & (phiVals == 90)
                         & dValsValid]
            ])
        else:
            val0 = np.array([
                dVals[(psiVals == psiUni[i]) & (phiVals == 0) & dValsValid],
                dErrVals[(psiVals == psiUni[i]) & (phiVals == 0) & dValsValid]
            ])
            val90 = np.array([
                dVals[(psiVals == psiUni[i]) & (phiVals == 90) & dValsValid],
                dErrVals[(psiVals == psiUni[i]) & (phiVals == 90) & dValsValid]
            ])
            val180 = np.array([
                dVals[(psiVals == psiUni[i]) & (phiVals == 180) & dValsValid],
                dErrVals[(psiVals == psiUni[i]) & (phiVals == 180)
                         & dValsValid]
            ])
            val270 = np.array([
                dVals[(psiVals == psiUni[i]) & (phiVals == 270) & dValsValid],
                dErrVals[(psiVals == psiUni[i]) & (phiVals == 270)
                         & dValsValid]
            ])
        val45 = np.array([
            dVals[(psiVals == psiUni[i]) & (phiVals == 45) & dValsValid],
            dErrVals[(psiVals == psiUni[i]) & (phiVals == 45) & dValsValid]
        ])
        if val45.size > 0:
            vals45[i, :] = val45
        if val0.size > 0 and val180.size > 0:
            vals0_180[i, 0] = 0.5 * (val0[0] + val180[0])
            vals0_180[i, 1] = 0.5 * (val0[0] - val180[0])
            vals0_180[i, 2] = 0.5 * (val0[1] + val180[1])
        elif val0.size > 0:
            vals0_180[i, 0] = val0[0]
            vals0_180[i, 1] = 0
            vals0_180[i, 2] = val0[1]
        elif val180.size > 0:
            vals0_180[i, 0] = val180[0]
            vals0_180[i, 1] = 0
            vals0_180[i, 2] = val180[1]
        if val90.size > 0 and val270.size > 0:
            vals90_270[i, 0] = 0.5 * (val90[0] + val270[0])
            vals90_270[i, 1] = 0.5 * (val90[0] - val270[0])
            vals90_270[i, 2] = 0.5 * (val90[1] + val270[1])
        elif val90.size > 0:
            vals90_270[i, 0] = val90[0]
            vals90_270[i, 1] = 0
            vals90_270[i, 2] = val90[1]
        elif val270.size > 0:
            vals90_270[i, 0] = val270[0]
            vals90_270[i, 1] = 0
            vals90_270[i, 2] = val270[1]
        if vals0_180[i, 0] != 0 and vals90_270[i, 0] != 0:
            valsAll[i, :] = 0.5 * (vals0_180[i, [0, 2]] +
                                   vals90_270[i, [0, 2]])
    # check for validity
    # perform linear regression for all phi values
    usedIndex = np.array(range(len(psiUni)))
    used = usedIndex[valsAll[:, 0] != 0]
    if len(used) > 0:
        if np.all(valsAll[used, 1] != 0):
            [m, b, CoD, yD, err, err33,
             bErr] = dm.linRegWeighted(sinpsi2Uni[used], valsAll[used, 0],
                                       1 / valsAll[used, 1])
        else:
            [m, b, CoD, yD, err, err33,
             bErr] = dm.linReg(sinpsi2Uni[used], valsAll[used, 0])
        mVals[5] = m
        bVals[5] = b
        errVals[5] = err33
        errValsStar = bErr
    # perform linear regression of phi values with phi = 45° (if exist)
    used12 = usedIndex[vals45[:, 0] != 0]
    if len(used12) > 0:
        if len(used) > 0:
            if np.all(vals45[used12, 1] != 0):
                [m12, b12, CoD12, yD12, err, err12,
                 bErr] = dm.linRegWeighted(sinpsi2Uni[used12], vals45[used12,
                                                                      0],
                                           1 / vals45[used12, 1], 'b', b)
            else:
                [m12, b12, CoD12, yD12, err, err12,
                 bErr] = dm.linReg(sinpsi2Uni[used12], vals45[used12, 0], 'b',
                                   b)
        else:
            if np.all(vals45[used12, 1] != 0):
                [m12, b12, CoD12, yD12, err, err12,
                 bErr] = dm.linRegWeighted(sinpsi2Uni[used12], vals45[used12,
                                                                      0],
                                           1 / vals45[used12, 1])
            else:
                [m12, b12, CoD12, yD12, err, err12,
                 bErr] = dm.linReg(sinpsi2Uni[used12], vals45[used12, 0])
            mVals[5] = m12
            bVals[5] = b12
        mVals[4] = m12
        bVals[4] = b12
        errVals[4] = err12
    # perform linear regression of phi values with phi = 0°/180°
    used1 = usedIndex[vals0_180[:, 0] != 0]
    if len(used1) > 0:
        if len(used) > 0:
            if np.all(vals0_180[used1, 2] != 0):
                [m1, b1, CoD1, yD1, err, err1,
                 bErr] = dm.linRegWeighted(sinpsi2Uni[used1], vals0_180[used1,
                                                                        0],
                                           1 / vals0_180[used1, 2], 'b', b)
            else:
                [m1, b1, CoD1, yD1, err, err1,
                 bErr] = dm.linReg(sinpsi2Uni[used1], vals0_180[used1, 0], 'b',
                                   b)
        else:
            if np.all(vals0_180[used1, 2] != 0):
                [m1, b1, CoD1, yD1, err, err1,
                 bErr] = dm.linRegWeighted(sinpsi2Uni[used1], vals0_180[used1,
                                                                        0],
                                           1 / vals0_180[used1, 2])
            else:
                [m1, b1, CoD1, yD1, err, err1,
                 bErr] = dm.linReg(sinpsi2Uni[used1], vals0_180[used1, 0])
            mVals[5] = m1
            bVals[5] = b1
        # [m1f, err1f] = lsqcurvefit(linM,m1,sinpsi2Uni(used1),vals0_180(used1,1));
        mVals[0] = m1
        bVals[0] = b1
        errVals[0] = err1
        if np.all(vals0_180[used1, 2] != 0):
            [m13, b13, CoD13, yD13, err, err13,
             bErr] = dm.linRegWeighted(sin2psiUni[used1], vals0_180[used1, 1],
                                       1 / vals0_180[used1, 2], 'b', 0)
        else:
            [m13, b13, CoD13, yD13, err, err13,
             bErr] = dm.linReg(sin2psiUni[used1], vals0_180[used1, 1], 'b', 0)
        # [m1f, err1f] = lsqcurvefit(linBase,m1,sin2psiUni(used1),vals0_180(used1,2));
        mVals[2] = m13
        bVals[2] = b13
        errVals[2] = err13
    # perform linear regression of phi values with phi = 90°/270°
    used2 = usedIndex[vals90_270[:, 0] != 0]
    if len(used2) > 0:
        if len(used) > 0:
            if np.all(vals90_270[used2, 2] != 0):
                [m2, b2, CoD2, yD2, err, err2,
                 bErr] = dm.linRegWeighted(sinpsi2Uni[used2], vals90_270[used2,
                                                                         0],
                                           1 / vals90_270[used2, 2], 'b', b)
            else:
                [m2, b2, CoD2, yD2, err, err2,
                 bErr] = dm.linReg(sinpsi2Uni[used2], vals90_270[used2, 0],
                                   'b', b)
        else:
            if np.all(vals90_270[used2, 2] != 0):
                [m2, b2, CoD2, yD2, err, err2,
                 bErr] = dm.linRegWeighted(sinpsi2Uni[used2], vals90_270[used2,
                                                                         0],
                                           1 / vals90_270[used2, 2])
            else:
                [m2, b2, CoD2, yD2, err, err2,
                 bErr] = dm.linReg(sinpsi2Uni[used2], vals90_270[used2, 0])
            mVals[5] = m2
            bVals[5] = b2
        # [m2f, err2f] = lsqcurvefit(linM,m2,sinpsi2Uni(used2),vals90_270(used2,1));
        mVals[1] = m2
        bVals[1] = b2
        errVals[1] = err2
        if np.all(vals90_270[used2, 2] != 0):
            [m23, b23, CoD23, yD23, err, err23,
             bErr] = dm.linRegWeighted(sin2psiUni[used2], vals90_270[used2, 1],
                                       1 / vals90_270[used2, 2], 'b', 0)
        else:
            [m23, b23, CoD23, yD23, err, err23,
             bErr] = dm.linReg(sin2psiUni[used2], vals90_270[used2, 1], 'b', 0)
        # [m2f, err2f] = lsqcurvefit(linBase,m2,sin2psiUni(used2),vals90_270(used2,2));
        mVals[3] = m23
        bVals[3] = b23
        errVals[3] = err23
    # determine stresses
    dStarVal0 = mVals[0] * sinpsi2StarSingle + bVals[0]
    dStarVal90 = mVals[1] * sinpsi2StarSingle + bVals[1]
    dStarVal = mVals[5] * sinpsi2Star + bVals[5]
    dPlusVal = mVals[5] * sinpsi2Plus + bVals[5]
    dComVal = mVals[5] * 2 / 3 + bVals[5]
    s11_s33 = mVals[0] / (hs2Val * dStarVal)
    ds11 = 2 * errVals[0]**0.5 / (hs2Val * dStarVal)
    s22_s33 = mVals[1] / (hs2Val * dStarVal)
    ds22 = 2 * errVals[1]**0.5 / (hs2Val * dStarVal)
    s13 = mVals[2] / (hs2Val * dStarVal)
    ds13 = 2 * errVals[2]**0.5 / (hs2Val * dStarVal)
    s23 = mVals[3] / (hs2Val * dStarVal)
    ds23 = 2 * errVals[3]**0.5 / (hs2Val * dStarVal)
    if np.any((phiVals == 45) | (phiVals == -45) | (phiVals == 225)):
        s12 = mVals[4] / (hs2Val * dStarVal) - 0.5 * (s11_s33 + s22_s33)
    else:
        s12 = mVals[4] / (hs2Val * dStarVal)
    ds12 = 2 * errVals[4]**0.5 / (hs2Val * dStarVal)
    aStarVal = conv.latticeDists2aVals2(dStarVal, hklVal)
    if a0Val is None or a0Val == 0:
        s33 = 0
        ds33 = 0
    else:
        s33 = calcSigma33(aStarVal, a0Val, s1Val, hs2Val)
        ds33 = 2 * errVals[5]**0.5 / (hs2Val * dStarVal)
    # d0_1 = regVals(:,2) ./ (dekList(:,2) .* (s11_s33 + s22_s33) + s33 .* f33 + 1);
    #     [sMain, d0, quality] = stressesWithS33(dekList, dStarVals, [s11_s33 s22_s33 s12 s13 s23], ...
    #         [data(:,1) dVals error data(:,[7 8])], plotData);
    # combine results
    stresses = np.array([s11_s33, s22_s33, s13, s23, s12, s33])
    accuracy = np.array([ds11, ds22, ds13, ds23, ds12, ds33])
    # resData: tauMean, dStar, stresses, accuracy, mVals???, bVals???
    resData = {
        'tauMean': tauMean,
        'dStar100': aStarVal,
        'dStar100Err': 2 * errValsStar**0.5,
        'stresses': stresses,
        'accuracy': accuracy,
        'meanIB': valuesIb
    }
    plotData = {
        'dVals': data['dVals'],
        'dErr': data['dErr'],
        'phiVals': data['phiVals'],
        'psiVals': data['psiVals'],
        'hklVal': hklVal,
        'mVals': mVals,
        'bVals': bVals,
        'errVals': errVals,
        'sinpsi2Star': sinpsi2Star,
        'meanVals': valsAll[used, :]
    }
    if len(used) > 0:
        plotData['xMean'] = sinpsi2Uni[used]
        plotData['yMean'] = yD
    if len(used12) > 0:
        plotData['x12'] = sinpsi2Uni[used12]
        plotData['y12'] = yD12
    if len(used1) > 0:
        plotData['x1'] = sinpsi2Uni[used1]
        plotData['y1'] = yD1
    if len(used2) > 0:
        plotData['x2'] = sinpsi2Uni[used2]
        plotData['y2'] = yD2
    return resData, plotData