Perform Guinier analysis on the samples.

    Inputs:
        samplenames: list of sample names
        qranges: dictionary of q ranges for each sample. The keys are sample names. The special '__default__' key
            corresponds to all samples which do not have a key in the dict.
        qmax_from_shanum: use the qmax determined by the shanum program for the GNOM input.
        prfunctions_postfix: The figure showing the P(r) functions will be saved as
            prfunctions_<prfunctions_postfix><graph_extension>
        dist: the sample-to-detector distance to use.
        plotguinier: if Guinier plots are needed.
        graph_extension: the extension of the saved graph image files.
        dmax: Dict of Dmax parameters. If not found or None, determine automatically using DATGNOM. If found,
            GNOM is used. The special key '__default__' works in a similar fashion as for `qranges`.

def guinieranalysis(samplenames, qranges=None, qmax_from_shanum=True, prfunctions_postfix='', dist=None,
                    plotguinier=True, graph_extension='.png', dmax=None, dmax_from_shanum=False):
    """Perform Guinier analysis on the samples.

    Inputs:
        samplenames: list of sample names
        qranges: dictionary of q ranges for each sample. The keys are sample names. The special '__default__' key
            corresponds to all samples which do not have a key in the dict.
        qmax_from_shanum: use the qmax determined by the shanum program for the GNOM input.
        prfunctions_postfix: The figure showing the P(r) functions will be saved as
            prfunctions_<prfunctions_postfix><graph_extension>
        dist: the sample-to-detector distance to use.
        plotguinier: if Guinier plots are needed.
        graph_extension: the extension of the saved graph image files.
        dmax: Dict of Dmax parameters. If not found or None, determine automatically using DATGNOM. If found,
            GNOM is used. The special key '__default__' works in a similar fashion as for `qranges`."""
    figpr = plt.figure()
    ip = get_ipython()
    axpr = figpr.add_subplot(1, 1, 1)
    if qranges is None:
        qranges = {'__default__': (0, 1000000)}
    if dmax is None:
        dmax = {'__default__': None}
    if '__default__' not in qranges:
        qranges['__default__'] = (0, 1000000)
    if '__default__' not in dmax:
        dmax['__default__'] = None
    table_autorg = [['Name', 'Rg (nm)', 'I$_0$ (cm$^{-1}$ sr$^{-1}$)',
                     'q$_{min}$ (nm$^{-1}$)', 'q$_{max}$ (nm$^{-1}$)',
                     'qmin*Rg', 'qmax*Rg', 'quality', 'aggregation',
                     'Dmax (nm)', 'q$_{shanum}$ (nm$^{-1}$)']]
    table_gnom = [['Name', 'Rg (nm)', 'I$_0$ (cm$^{-1}$ sr$^{-1}$)',
                   'qmin (nm$^{-1}$)', 'qmax (nm$^{-1}$)',
                   'Dmin (nm)', 'Dmax (nm)', 'Total estimate', 'Porod volume (nm$^3$)']]
    results = {}
    for sn in samplenames:
        if sn not in qranges:
            print('Q-range not given for sample {}: using default one'.format(sn))
            qrange = qranges['__default__']
        else:
            qrange = qranges[sn]
        if sn not in dmax:
            dmax_ = dmax['__default__']
        else:
            dmax_ = dmax[sn]
        print('Using q-range for sample {}: {} <= q <= {}'.format(sn, qrange[0], qrange[1]))
        curve = getsascurve(sn, dist)[0].trim(*qrange).sanitize()

        curve.save(sn + '.dat')
        try:
            Rg, I0, qmin, qmax, quality, aggregation = autorg(sn + '.dat')
        except ValueError:
            print('Error running autorg on %s' % sn)
            continue
        dmax_shanum, nsh, nopt, qmaxopt = shanum(sn + '.dat')
        if qmax_from_shanum:
            curve_trim = curve.trim(qmin, qmaxopt)
        else:
            curve_trim = curve.trim(qmin, qrange[1])
        if dmax_from_shanum:
            dmax_ = dmax_from_shanum
        curve_trim.save(sn + '_optrange.dat')
        if dmax_ is None:
            print('Calling DATGNOM for sample {} with Rg={}, q-range from {} to {}'.format(
                sn, Rg.val, curve_trim.q.min(), curve_trim.q.max()))
            gnompr, metadata = datgnom(sn + '_optrange.dat', Rg=Rg.val, noprint=True)
        else:
            print('Calling GNOM for sample {} with Rmax={}, q-range from {} to {}'.format(
                sn, dmax_, curve_trim.q.min(), curve_trim.q.max()))
            gnompr, metadata = gnom(curve_trim, dmax_)
        rg, i0, vporod = datporod(sn + '_optrange.out')
        axpr.errorbar(gnompr[:, 0], gnompr[:, 1], gnompr[:, 2], None, label=sn)
        if plotguinier:
            figsample = plt.figure()
            axgnomfit = figsample.add_subplot(1, 2, 1)
            curve.errorbar('b.', axes=axgnomfit, label='measured')
            axgnomfit.errorbar(metadata['qj'], metadata['jexp'], metadata['jerror'], None, 'g.', label='gnom input')
            axgnomfit.loglog(metadata['qj'], metadata['jreg'], 'r-', label='regularized by GNOM')
            figsample.suptitle(sn)
            axgnomfit.set_xlabel('q (nm$^{-1}$)')
            axgnomfit.set_ylabel('$d\Sigma/d\Omega$ (cm$^{-1}$ sr$^{-1}$)')
            axgnomfit.axvline(qmaxopt, 0, 1, linestyle='dashed', color='black', lw=2)
            axgnomfit.grid(True, which='both')
            axgnomfit.axis('tight')
            axgnomfit.legend(loc='best')
            axguinier = figsample.add_subplot(1, 2, 2)
            axguinier.errorbar(curve.q, curve.Intensity, curve.Error, curve.qError, '.', label='Measured')
            q = np.linspace(qmin, qmax, 100)
            axguinier.plot(q, I0.val * np.exp(-q ** 2 * Rg.val ** 2 / 3), label='AutoRg')
            axguinier.plot(q, metadata['I0_gnom'].val * np.exp(-q ** 2 * metadata['Rg_gnom'].val ** 2 / 3),
                           label='Gnom')
            axguinier.set_xscale('power', exponent=2)
            axguinier.set_yscale('log')
            axguinier.set_xlabel('q (nm$^{-1}$)')
            axguinier.set_ylabel('$d\Sigma/d\Omega$ (cm$^{-1}$ sr$^{-1}$)')
            axguinier.legend(loc='best')
        idxmin = np.arange(len(curve))[curve.q <= qmin].max()
        idxmax = np.arange(len(curve))[curve.q >= qmax].min()
        idxmin = max(0, idxmin - 5)
        idxmax = min(len(curve) - 1, idxmax + 5)
        if plotguinier:
            curveguinier = curve.trim(curve.q[idxmin], curve.q[idxmax])
            axguinier.axis(xmax=curve.q[idxmax], xmin=curve.q[idxmin], ymin=curveguinier.Intensity.min(),
                           ymax=curveguinier.Intensity.max())
            axguinier.grid(True, which='both')
        table_gnom.append(
                [sn, metadata['Rg_gnom'].tostring(extra_digits=2), metadata['I0_gnom'].tostring(extra_digits=2),
                 metadata['qmin'], metadata['qmax'],
                 metadata['dmin'], metadata['dmax'], metadata['totalestimate_corrected'], vporod])
        table_autorg.append([sn, Rg.tostring(extra_digits=2), I0, '%.3f' % qmin, '%.3f' % qmax, qmin * Rg, qmax * Rg,
                             '%.1f %%' % (quality * 100), aggregation, '%.3f' % dmax_shanum, '%.3f' % qmaxopt])
        if plotguinier:
            figsample.tight_layout()
            figsample.savefig(os.path.join(ip.user_ns['auximages_dir'], 'guinier_%s%s' % (sn, graph_extension)),
                              dpi=600)
        results[sn] = {
            'Rg_autorg'  : Rg, 'I0_autorg': I0,
            'qmin_autorg': qmin, 'qmax_autorg': qmax,
            'quality'    : quality, 'aggregation': aggregation,
            'dmax_autorg': dmax_shanum, 'qmax_shanum': qmaxopt,
            'Rg_gnom'    : metadata['Rg_gnom'],
            'I0_gnom'    : metadata['I0_gnom'],
            'qmin_gnom'  : metadata['qmin'],
            'qmax_gnom'  : metadata['qmax'],
            'dmin_gnom'  : metadata['dmin'],
            'dmax_gnom'  : metadata['dmax'],
            'VPorod'     : vporod,
        }
    axpr.set_xlabel('r (nm)')
    axpr.set_ylabel('P(r)')
    axpr.legend(loc='best')
    axpr.grid(True, which='both')
    writemarkdown('## Results from autorg and shanum')
    tab = ipy_table.IpyTable(table_autorg)
    tab.apply_theme('basic')
    display(tab)
    writemarkdown('## Results from gnom')
    tab = ipy_table.IpyTable(table_gnom)
    tab.apply_theme('basic')
    if prfunctions_postfix and prfunctions_postfix[0] != '_':
        prfunctions_postfix = '_' + prfunctions_postfix
    figpr.tight_layout()
    figpr.savefig(os.path.join(ip.user_ns['auximages_dir'], 'prfunctions%s%s' % (prfunctions_postfix, graph_extension)),
                  dpi=600)
    display(tab)
    return results