示例#1
0
def test_plot_solver_linear_error():
    N = 3
    rd = _get_decay_rd(N)
    tout = np.linspace(0, 3.0, 7)
    y0 = [3.0, 1.0] * N
    integr = run(rd, y0, tout)
    Cref = _get_decay_Cref(N, y0, tout)
    ax = plot_solver_linear_error(integr, Cref)
    assert isinstance(ax, matplotlib.axes.Axes)
示例#2
0
def integrate_rd(D=2e-3, t0=3., tend=7., x0=0.0, xend=1.0, mu=None, N=32,
                 nt=25, geom='f', logt=False, logy=False, logx=False,
                 random=False, nstencil=3, lrefl=False, rrefl=False,
                 num_jacobian=False, method='bdf', plot=False,
                 atol=1e-6, rtol=1e-6, efield=False, random_seed=42,
                 verbose=False, use_log2=False):
    if random_seed:
        np.random.seed(random_seed)
    n = 1
    mu = float(mu or x0)
    tout = np.linspace(t0, tend, nt)

    assert geom in 'fcs'

    # Setup the grid
    logb = (lambda arg: log(arg)/log(2)) if use_log2 else log

    _x0 = logb(x0) if logx else x0
    _xend = logb(xend) if logx else xend
    x = np.linspace(_x0, _xend, N+1)
    if random:
        x += (np.random.random(N+1)-0.5)*(_xend-_x0)/(N+2)

    mob = 0.3
    # Initial conditions
    y0 = {
        'f': y0_flat_cb,
        'c': y0_cylindrical_cb,
        's': y0_spherical_cb
    }[geom](x, logx)

    # Setup the system
    stoich_active = []
    stoich_prod = []
    k = []

    assert not lrefl
    assert not rrefl

    rd = ReactionDiffusion(
        n, stoich_active, stoich_prod, k, N,
        D=[D],
        z_chg=[1],
        mobility=[mob],
        x=x,
        geom=geom,
        logy=logy,
        logt=logt,
        logx=logx,
        nstencil=nstencil,
        lrefl=lrefl,
        rrefl=rrefl,
        use_log2=use_log2
    )

    if efield:
        if geom != 'f':
            raise ValueError("Only analytic sol. for flat drift implemented.")
        rd.efield = efield_cb(rd.xcenters, logx)

    # Analytic reference values
    t = tout.copy().reshape((nt, 1))
    Cref = np.repeat(y0[np.newaxis, :, np.newaxis], nt, axis=0)
    if efield:
        Cref += t.reshape((nt, 1, 1))*mob

    # Run the integration
    integr = run(rd, y0, tout, atol=atol, rtol=rtol,
                 with_jacobian=(not num_jacobian), method=method)
    Cout, info = integr.Cout, integr.info

    if verbose:
        print(info)

    def lin_err(i=slice(None), j=slice(None)):
        return integr.Cout[i, :, j] - Cref[i, :, j]

    rmsd = np.sum(lin_err()**2 / N, axis=1)**0.5
    ave_rmsd_over_atol = np.average(rmsd, axis=0)/info['atol']

    # Plot results
    if plot:
        import matplotlib.pyplot as plt

        def _plot(y, c, ttl=None, apply_exp_on_y=False):
            plt.plot(rd.xcenters, rd.expb(y) if apply_exp_on_y else y, c=c)
            if N < 100:
                plt.vlines(rd.x, 0, np.ones_like(rd.x)*max(y), linewidth=.1,
                           colors='gray')
            plt.xlabel('x / m')
            plt.ylabel('C / M')
            if ttl:
                plt.title(ttl)

        for i in range(nt):
            c = 1-tout[i]/tend
            c = (1.0-c, .5-c/2, .5-c/2)  # over time: dark red -> light red

            plt.subplot(4, 1, 1)
            _plot(Cout[i, :, 0], c, 'Simulation (N={})'.format(rd.N),
                  apply_exp_on_y=logy)

            plt.subplot(4, 1, 2)
            _plot(Cref[i, :, 0], c, 'Analytic', apply_exp_on_y=logy)

            ax_err = plt.subplot(4, 1, 3)
            plot_solver_linear_error(integr, Cref, ax_err, ti=i,
                                     bi=slice(None),
                                     color=c, fill=(i == 0))
            plt.title('Linear rel error / Log abs. tol. (={})'.format(
                      info['atol']))

        plt.subplot(4, 1, 4)
        tspan = [tout[0], tout[-1]]
        plt.plot(tout, rmsd[:, 0] / info['atol'], 'r')
        plt.plot(tspan, [ave_rmsd_over_atol[0]]*2, 'r--')

        plt.xlabel('Time / s')
        plt.ylabel(r'$\sqrt{\langle E^2 \rangle} / atol$')
        plt.tight_layout()
        plt.show()
    return tout, Cout, info, ave_rmsd_over_atol, rd
示例#3
0
def integrate_rd(
        tend=1.9, A0=4.2, B0=3.1, C0=1.4, nt=100, t0=0.0, kf=0.9, kb=0.23,
        atol='1e-7,1e-6,1e-5', rtol='1e-6', integrator='scipy', method='bdf',
        logy=False, logt=False, num_jac=False, plot=False, savefig='None',
        splitplots=False, plotlogy=False, plotsymlogy=False, plotlogt=False,
        scale_err=1.0, scaling=1.0, verbose=False):
    """
    Runs the integration and (optionally) plots:

    - Individual concentrations as function of time
    - Reaction Quotient vs. time (with equilibrium constant as reference)
    - Numerical error commited (with tolerance span plotted)
    - Excess error committed (deviation outside tolerance span)

    Concentrations (A0, B0, C0) are taken to be in "M" (molar),
    kf in "M**-1 s**-1" and kb in "s**-1", t0 and tend in "s"
    """

    rtol = float(rtol)
    atol = list(map(float, atol.split(',')))
    if len(atol) == 1:
        atol = atol[0]
    registry = SI_base_registry.copy()
    registry['amount'] = 1.0/scaling*registry['amount']
    registry['length'] = registry['length']/10  # decimetre

    kf = kf/molar/second
    kb = kb/second

    rd = ReactionDiffusion.nondimensionalisation(
        3, [[0, 1], [2]], [[2], [0, 1]], [kf, kb], logy=logy, logt=logt,
        unit_registry=registry)

    C0 = np.array([A0, B0, C0])*molar
    if plotlogt:
        eps = 1e-16
        tout = np.logspace(np.log10(t0+eps), np.log10(tend+eps), nt)*second
    else:
        tout = np.linspace(t0, tend, nt)*second

    integr = Integration(
        rd, C0, tout, integrator=integrator, atol=atol, rtol=rtol,
        with_jacobian=not num_jac, method=method)
    Cout = integr.with_units('Cout')
    yout, info = integr.yout, integr.info
    try:
        import mpmath
        assert mpmath  # silence pyflakes
    except ImportError:
        use_mpmath = False
    else:
        use_mpmath = True
    time_unit = get_derived_unit(registry, 'time')
    conc_unit = get_derived_unit(registry, 'concentration')
    Cref = _get_Cref(
        to_unitless(tout - tout[0], time_unit),
        to_unitless(C0, conc_unit),
        [to_unitless(kf, 1/time_unit/conc_unit),
         to_unitless(kb, 1/time_unit)],
        use_mpmath
    ).reshape((nt, 1, 3))*conc_unit
    if verbose:
        print(info)

    if plot:
        npltcols = 3 if splitplots else 1
        import matplotlib.pyplot as plt
        plt.figure(figsize=(18 if splitplots else 6, 10))

        def subplot(row=0, idx=0, adapt_yscale=True, adapt_xscale=True,
                    span_all_x=False):
            offset = idx if splitplots else 0
            ax = plt.subplot(4, 1 if span_all_x else npltcols,
                             1 + row*npltcols + offset)
            if adapt_yscale:
                if plotlogy:
                    ax.set_yscale('log')
                elif plotsymlogy:
                    ax.set_yscale('symlog')
            if adapt_xscale and plotlogt:
                ax.set_xscale('log')
            return ax

        tout_unitless = to_unitless(tout, second)
        c = 'rgb'
        for i, l in enumerate('ABC'):
            # Plot solution trajectory for i:th species
            ax_sol = subplot(0, i)
            ax_sol.plot(tout_unitless, to_unitless(Cout[:, 0, i], molar),
                        label=l, color=c[i])

            if splitplots:
                # Plot relative error
                ax_relerr = subplot(1, 1)
                ax_relerr.plot(
                    tout_unitless, Cout[:, 0, i]/Cref[:, 0, i] - 1.0,
                    label=l, color=c[i])
                ax_relerr.set_title("Relative error")
                ax_relerr.legend(loc='best', prop={'size': 11})

                # Plot absolute error
                ax_abserr = subplot(1, 2)
                ax_abserr.plot(tout_unitless, Cout[:, 0, i]-Cref[:, 0, i],
                               label=l, color=c[i])
                ax_abserr.set_title("Absolute error")
                ax_abserr.legend(loc='best', prop={'size': 11})

            # Plot absolute error
            linE = Cout[:, 0, i] - Cref[:, 0, i]
            try:
                atol_i = atol[i]
            except:
                atol_i = atol
            wtol_i = (atol_i + rtol*yout[:, 0, i])*get_derived_unit(
                rd.unit_registry, 'concentration')

            if np.any(np.abs(linE/wtol_i) > 1000):
                # Plot true curve in first plot when deviation is large enough
                # to be seen visually
                ax_sol.plot(tout_unitless, to_unitless(Cref[:, 0, i], molar),
                            label='true '+l, color=c[i], ls='--')

            ax_err = subplot(2, i)
            plot_solver_linear_error(integr, Cref, ax_err, si=i,
                                     scale_err=1/wtol_i, color=c[i], label=l)
            ax_excess = subplot(3, i, adapt_yscale=False)
            plot_solver_linear_excess_error(integr, Cref, ax_excess,
                                            si=i, color=c[i], label=l)

        # Plot Reaction Quotient vs time
        ax_q = subplot(1, span_all_x=False, adapt_yscale=False,
                       adapt_xscale=False)
        Qnum = Cout[:, 0, 2]/(Cout[:, 0, 0]*Cout[:, 0, 1])
        Qref = Cref[:, 0, 2]/(Cref[:, 0, 0]*Cref[:, 0, 1])
        ax_q.plot(tout_unitless, to_unitless(Qnum, molar**-1),
                  label='Q', color=c[i])
        if np.any(np.abs(Qnum/Qref-1) > 0.01):
            # If more than 1% error in Q, plot the reference curve too
            ax_q.plot(tout_unitless, to_unitless(Qref, molar**-1),
                      '--', label='Qref', color=c[i])
        # Plot the
        ax_q.plot((tout_unitless[0], tout_unitless[-1]),
                  [to_unitless(kf/kb, molar**-1)]*2,
                  '--k', label='K')
        ax_q.set_xlabel('t')
        ax_q.set_ylabel('[C]/([A][B]) / M**-1')
        ax_q.set_title("Transient towards equilibrium")
        ax_q.legend(loc='best', prop={'size': 11})

        for i in range(npltcols):
            subplot(0, i, adapt_yscale=False)
            plt.title('Concentration vs. time')
            plt.legend(loc='best', prop={'size': 11})
            plt.xlabel('t')
            plt.ylabel('[X]')

            subplot(2, i, adapt_yscale=False)
            plt.title('Absolute error in [{}](t) / wtol'.format('ABC'[i]))
            plt.legend(loc='best')
            plt.xlabel('t')
            ttl = '|E_i[{0}]|/(atol_i + rtol*(y0_i+yf_i)/2'
            plt.ylabel(ttl.format('ABC'[i]))
            plt.tight_layout()

            subplot(3, i, adapt_yscale=False)
            ttl = 'Excess error in [{}](t) / integrator linear error span'
            plt.title(ttl.format(
                'ABC'[i]))
            plt.legend(loc='best')
            plt.xlabel('t')
            plt.ylabel('|E_excess[{0}]| / e_span'.format('ABC'[i]))

        plt.tight_layout()
        save_and_or_show_plot(savefig=savefig)

    return yout, to_unitless(Cref, conc_unit), rd, info