def main(tend=10.0, N=25, nt=30, nstencil=3, linterpol=False, rinterpol=False, num_jacobian=False, plot=False, savefig='None', verbose=False): x = np.linspace(0.1, 1.0, N+1) def f(x): return 2*x**2/(x**4+1) # f(0)=0, f(1)=1, f'(0)=0, f'(1)=0 y0 = f(x[1:])+x[0] # (x[0]/2+x[1:])**2 t0 = 1e-10 tout = np.linspace(t0, tend, nt) res, systems = [], [] for g in 'fcs': rd = ReactionDiffusion(1, [], [], [], N=N, D=[0.02], x=x, geom=g, nstencil=nstencil, lrefl=not linterpol, rrefl=not rinterpol) integr = run(rd, y0, tout, with_jacobian=(not num_jacobian)) res.append(integr.yout) systems.append(rd) if plot: # matplotlib from mpl_toolkits.mplot3d import Axes3D assert Axes3D # silence pyflakes from matplotlib import cm from matplotlib import pyplot as plt fig = plt.figure() # Plot spatio-temporal conc. evolution for i, g in enumerate('fcs'): yout = res[i] ax = fig.add_subplot(2, 3, 'fcs'.index(g)+1, projection='3d') plot_C_vs_t_and_x(rd, tout, yout, 0, ax, rstride=1, cstride=1, cmap=cm.gist_earth) ax.set_title(Geom_names[g]) # Plot mass conservation ax = fig.add_subplot(2, 3, 4) for j in range(3): ax.plot(tout, np.apply_along_axis( systems[j].integrated_conc, 1, res[j][:, :, 0]), label=str(j)) ax.legend(loc='best') ax.set_title('Mass conservation') # Plot difference from flat evolution (not too informative..) for i, g in enumerate('fcs'): yout = res[i] # only one specie if i != 0: yout = yout - res[0] # difference (1 specie) ax = fig.add_subplot(2, 3, 3+'fcs'.index(g)+1, projection='3d') plot_C_vs_t_and_x(rd, tout, yout, 0, ax, rstride=1, cstride=1, cmap=cm.gist_earth) ax.set_title(Geom_names[g] + ' minus ' + Geom_names[0]) save_and_or_show_plot(savefig)
def main(tend=3.0, N=30, nt=30, plot=False, logy=False, logt=False, savefig='None', verbose=False): def mod1(x): return x/(x**2+1) # decay A->B is modulated with x rd = load( os.path.join(os.path.dirname( __file__), 'four_species.json'), N=N, modulation=[[mod1(x/3+0.1) for x in range(N)]], modulated_rxns=[1], logt=logt, logy=logy) y0 = np.array([1.3, 1e-4, 0.7, 1e-4]) # y0 = np.concatenate([y0/(i+1)*(0.25*i**2+1) for i in range(N)]) y0 = np.concatenate([y0 for i in range(N)]) t0 = 1e-10 tout = np.linspace(t0, tend, nt) integr = run(rd, y0, tout) if verbose: pprint(integr.info) cx = rd.x[:-1]+np.diff(rd.x)/2 # center x if plot: # Using matplotlib for 3D plots: from mpl_toolkits.mplot3d import Axes3D assert Axes3D # silence pyflakes from matplotlib import cm from matplotlib import pyplot as plt from chemreac.util.plotting import save_and_or_show_plot fig = plt.figure() for i, l in enumerate('ABCD'): ax = fig.add_subplot(3, 2, i+1, projection='3d') T, X = np.meshgrid(cx, tout) ax.plot_surface(T, X, integr.Cout[:, :, i], rstride=1, cstride=1, cmap=cm.YlGnBu_r) ax.set_xlabel('x / m') ax.set_ylabel('time / s') ax.set_zlabel(r'C / mol*m**-3') ax.set_title(l) ax.legend(loc='best') ax = fig.add_subplot(3, 2, 5) print(np.array(rd.modulation).shape) ax.plot(cx, np.array(rd.modulation)[0, :]) save_and_or_show_plot(savefig=savefig)
def plot_with_matplotlib( savefig='None', dpi=300, errorbar=False, linx=False, categorical='geom,nspecies,nstencil', continuous='N', liny=False, ylims='None', nfits='7,6,4', prop='rmsd_over_atol', prop_label='RMSD/atol'): c = 'rbkg' m = 'osdx' categorical = categorical.split(',') results, varied = read(__file__) arr = to_array(results, varied, categorical, continuous, prop, lambda x: np.average(x)) if ylims != 'None': ylims = [[float(_) for _ in ylim.split(',')] for ylim in ylims.split(';')] nfits = [int(_) for _ in nfits.split(',')] nrows, ncols, nseries = [len(varied[categorical[idx]]) for idx in range(3)] xdata = varied[continuous] plt.figure(figsize=(4*ncols, 10)) for ri, ci in product(range(nrows), range(ncols)): geom_name = Geom_names[varied['geom'][ri]] ax = plt.subplot(nrows, ncols, ri*ncols + ci + 1) ax.set_xlabel(continuous) ax.set_ylabel(prop_label) ax.set_xscale('log', basex=2) ax.set_yscale('log', basey=2) if ci == 0: # nspecies == 1 plt.title('{}'.format(geom_name)) else: plt.title('{}, {} decay(s)'.format(geom_name, ci)) for si in range(nseries): ydata = arr[ri, ci, si] plt.plot(xdata, ydata, marker=m[si], ls='None', c=c[si]) nfit = nfits[si] logx, logy = np.log(xdata[:nfit]), np.log(ydata[:nfit]) pf = np.polyfit(logx, logy, 1) ax.plot(xdata[:nfit], np.exp(np.polyval(pf, logx)), ls='--', c=c[si], label='%s: %s' % (str(varied[categorical[2]][si]), str(round(-pf[0], 1)))) if ylims != 'None': ax.set_ylim(ylims[0]) ax.legend(loc='upper right', prop={'size': 10}, numpoints=1) plt.tight_layout() save_and_or_show_plot(savefig=savefig, dpi=dpi)
def integrate_rd(tend=10.0, N=1, nt=500, jac_spy=False, linear_solver='default', logy=False, logt=False, plot=False, savefig='None', verbose=False, graph=False, **kwargs): """ Integrates the reaction system defined by :download:`four_species.json <examples/four_species.json>` """ rd = load(os.path.join(os.path.dirname( __file__), 'four_species.json'), N=N, x=N, logy=logy, logt=logt) y0 = np.array([1.3, 1e-4, 0.7, 1e-4]) y0 = np.concatenate([y0/(i+1)*(0.25*i**2+1) for i in range(N)]) t0 = 1e-10 if linear_solver == 'default': if rd.N == 1: linear_solver = 'dense' elif rd.N > 1: linear_solver = 'banded' if linear_solver not in ('dense', 'banded'): raise NotImplementedError("dense or banded linear_solver") import matplotlib.pyplot as plt if jac_spy: fout = np.empty(rd.n*rd.N) rd.f(t0, y0, fout) print(fout) if linear_solver == 'dense': jout = np.zeros((rd.n*rd.N, rd.n*rd.N), order='F') rd.dense_jac_cmaj(t0, y0, jout) coloured_spy(np.log(np.abs(jout))) elif linear_solver == 'banded': # note rd.n*3 needed in call from scipy.integrate.ode jout = np.zeros((rd.n*2+1, rd.n*rd.N), order='F') rd.banded_packed_jac_cmaj(t0, y0, jout) coloured_spy(np.log(np.abs(jout))) print(jout) plt.show() else: tout = np.linspace(t0, tend, nt) integr = run(rd, y0, tout, **kwargs) if verbose: print(integr.info) if plot: plt.figure(figsize=(6, 4)) for i, l in enumerate('ABCD'): plt.plot(integr.tout, integr.Cout[:, 0, i], label=l) plt.title("Time evolution of concentrations") plt.legend() save_and_or_show_plot(savefig=savefig) plt.figure(figsize=(6, 10)) plot_jacobian( rd, np.log(integr.tout) if rd.logt else integr.tout, np.log(integr.Cout) if rd.logy else integr.Cout, 'ABCD', lintreshy=1e-10 ) plt.tight_layout() if savefig != 'None': base, ext = os.path.splitext(savefig) savefig = base + '_jacobian' + ext save_and_or_show_plot(savefig=savefig) plt.figure(figsize=(6, 10)) plot_per_reaction_contribution( integr, 'ABCD' ) plt.tight_layout() if savefig != 'None': savefig = base + '_per_reaction' + ext save_and_or_show_plot(savefig=savefig) if graph: print(rsys2graph(ReactionSystem.from_ReactionDiffusion(rd, 'ABCD'), 'four_species_graph.png', save='.')) return integr
def integrate_rd(N=64, geom='f', nspecies=1, nstencil=3, D=2e-3, t0=3.0, tend=7., x0=0.0, xend=1.0, center=None, nt=42, logt=False, logy=False, logx=False, random=False, p=0, a=0.2, linterpol=False, rinterpol=False, ilu_limit=5.0, n_jac_diags=-1, num_jacobian=False, method='bdf', integrator='cvode', iter_type='undecided', linear_solver='default', atol=1e-8, rtol=1e-10, efield=False, random_seed=42, mobility=0.01, plot=False, savefig='None', verbose=False, yscale='linear', vline_limit=100, use_log2=False, Dexpr='[D]*nspecies', check_conserv=False ): # remember: anayltic_N_scaling.main kwargs # Example: # python3 analytic_diffusion.py --plot --Dexpr "D*np.exp(10*(x[:-1]+np.diff(x)/2))" if t0 == 0.0: raise ValueError("t0==0 => Dirac delta function C0 profile.") if random_seed: np.random.seed(random_seed) # decay = (nspecies > 1) # n = 2 if decay else 1 center = float(center or x0) tout = np.linspace(t0, tend, nt) assert geom in 'fcs' analytic = { 'f': flat_analytic, 'c': cylindrical_analytic, 's': spherical_analytic }[geom] # Setup the grid logx0 = math.log(x0) if logx else None logxend = math.log(xend) if logx else None if logx and use_log2: logx0 /= math.log(2) logxend /= math.log(2) _x0 = logx0 if logx else x0 _xend = logxend 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) def _k(si): return (si+p)*math.log(a+1) k = [_k(i+1) for i in range(nspecies-1)] rd = ReactionDiffusion( nspecies, [[i] for i in range(nspecies-1)], [[i+1] for i in range(nspecies-1)], k, N, D=eval(Dexpr), z_chg=[1]*nspecies, mobility=[mobility]*nspecies, x=x, geom=geom, logy=logy, logt=logt, logx=logx, nstencil=nstencil, lrefl=not linterpol, rrefl=not rinterpol, ilu_limit=ilu_limit, n_jac_diags=n_jac_diags, use_log2=use_log2 ) if efield: if geom != 'f': raise ValueError("Only analytic sol. for flat drift implemented.") rd.efield = _efield_cb(rd.xcenters) # Calc initial conditions / analytic reference values t = tout.copy().reshape((nt, 1)) yref = analytic(rd.xcenters, t, D, center, x0, xend, -mobility if efield else 0, logy, logx, use_log2).reshape(nt, N, 1) if nspecies > 1: from batemaneq import bateman_parent bateman_out = np.array(bateman_parent(k, tout)).T terminal = (1 - np.sum(bateman_out, axis=1)).reshape((nt, 1)) bateman_out = np.concatenate((bateman_out, terminal), axis=1).reshape( (nt, 1, nspecies)) if logy: yref = yref + rd.logb(bateman_out) else: yref = yref * bateman_out # Run the integration integr = run(rd, yref[0, ...], tout, atol=atol, rtol=rtol, with_jacobian=(not num_jacobian), method=method, iter_type=iter_type, linear_solver=linear_solver, C0_is_log=logy, integrator=integrator) info = integr.info if logy: def lin_err(i, j): linref = rd.expb(yref[i, :, j]) linerr = rd.expb(integr.yout[i, :, j])-linref linatol = np.average(yref[i, :, j]) linrtol = linatol return linerr/(linrtol*np.abs(linref)+linatol) if logy: rmsd = np.sum(lin_err(slice(None), slice(None))**2 / N, axis=1)**0.5 else: rmsd = np.sum((yref-integr.yout)**2 / N, axis=1)**0.5 ave_rmsd_over_atol = np.average(rmsd, axis=0)/atol if verbose: # Print statistics from pprint import pprint pprint(info) pprint(ave_rmsd_over_atol) # Plot results if plot: import matplotlib.pyplot as plt plt.figure(figsize=(6, 10)) # colors: (0.5, 0.5, 0.5), (0.5, 0.5, 1), ... base_colors = list(product([.5, 1], repeat=3))[1:-1] def color(ci, ti): return np.array(base_colors[ci % len(base_colors)])*tout[ti]/tend for ti in range(nt): plt.subplot(4, 1, 1) for si in range(nspecies): plt.plot(rd.xcenters, integr.Cout[ti, :, si], c=color(si, ti), label=None if ti < nt - 1 else rd.substance_names[si]) plt.subplot(4, 1, 2) for si in range(nspecies): plt.plot(rd.xcenters, rd.expb(yref[ti, :, si]) if logy else yref[ti, :, si], c=color(si, ti)) plt.subplot(4, 1, 3) if logy: for si in range(nspecies): plt.plot(rd.xcenters, lin_err(ti, si)/atol, c=color(si, ti)) else: for si in range(nspecies): plt.plot( rd.xcenters, (yref[ti, :, si] - integr.yout[ti, :, si])/atol, c=color(si, ti)) if N < vline_limit: for idx in range(1, 4): plt.subplot(4, 1, idx) for bi in range(N): plt.axvline(rd.x[bi], color='gray') plt.subplot(4, 1, 1) plt.title('Simulation (N={})'.format(rd.N)) plt.xlabel('x / m') plt.ylabel('C / M') plt.gca().set_yscale(yscale) plt.legend() plt.subplot(4, 1, 2) plt.title('Analytic solution') plt.gca().set_yscale(yscale) plt.subplot(4, 1, 3) plt.title('Linear rel. error / Abs. tol. (={})'.format(atol)) plt.subplot(4, 1, 4) plt.title('RMS error vs. time'.format(atol)) tspan = [tout[0], tout[-1]] for si in range(nspecies): plt.plot(tout, rmsd[:, si] / atol, c=color(si, -1)) plt.plot(tspan, [ave_rmsd_over_atol[si]]*2, c=color(si, -1), ls='--') plt.xlabel('Time / s') plt.ylabel(r'$\sqrt{\langle E^2 \rangle} / atol$') plt.tight_layout() save_and_or_show_plot(savefig=savefig) if check_conserv: tot_amount = np.zeros(tout.size) for ti in range(tout.size): for si in range(nspecies): tot_amount[ti] += rd.integrated_conc(integr.yout[ti, :, si]) if plot: plt.plot(tout, tot_amount) plt.show() assert np.allclose(tot_amount[0], tot_amount[1:]) return tout, integr.yout, info, ave_rmsd_over_atol, rd, rmsd
def integrate_rd(tend=1e2, A0=1.0, B0=0.0, C0=0.0, k1=0.04, k2=1e4, k3=3e7, t0=1e2, nt=100, N=1, nstencil=3, logt=False, logy=False, plot=False, savefig='None', verbose=False, dump_expr='False', use_chempy=False, D=2e-3): if N == 1: init_conc = (A0, B0, C0) else: init_conc = np.tile((A0, B0, C0), (N, 1)) init_conc /= np.linspace(1, 2, N).reshape((N, 1))**.5 rsys = ReactionSystem(get_reactions((k1, k2, k3)), 'ABC') if verbose: print([str(_) for _ in rsys.rxns]) if use_chempy: from chempy.kinetics.ode import get_odesys odesys = get_odesys(rsys, include_params=True) if N != 1: raise ValueError("ChemPy does not support diffusion") odesys.integrate(np.logspace(log10(t0), log10(tend)), init_conc) if plot: odesys.plot_result(xscale='log', yscale='log') result = None else: rd = ReactionDiffusion.from_ReactionSystem( rsys, N=N, nstencil=1 if N == 1 else nstencil, logt=logt, logy=logy, D=[D/2, D/3, D/5]) if dump_expr.lower() not in ('false', '0'): from chemreac.symbolic import SymRD import sympy as sp cb = {'latex': sp.latex, 'ccode': sp.ccode}.get(dump_expr.lower(), str) srd = SymRD.from_rd(rd, k=sp.symbols('k:3')) print('dydx:') print('\n'.join(map(cb, srd._f))) print('jac:') for ri, row in enumerate(srd.jacobian.tolist()): for ci, expr in enumerate(row): if expr == 0: continue print(ri, ci, cb(expr)) return None if t0 == 0 and logt: t0 = 1e-3*suggest_t0(rd, init_conc) if verbose: print("Using t0 = %12.5g" % t0) t = np.logspace(np.log10(t0), np.log10(tend), nt) print(t[0], t[-1]) integr = run(rd, init_conc, t) if verbose: import pprint pprint.pprint(integr.info) if plot: if N == 1: plot_C_vs_t(integr, xscale='log', yscale='log') else: import matplotlib.pyplot as plt for idx, name in enumerate('ABC', 1): plt.subplot(1, 3, idx) rgb = [.5, .5, .5] rgb[idx-1] = 1 plot_faded_time(integr, name, rgb=rgb, log_color=True) result = integr if plot: save_and_or_show_plot(savefig=savefig) return result
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
def integrate_rd(D=-3e-1, t0=0.0, tend=7., x0=0.1, xend=1.0, N=1024, base=0.5, offset=0.25, 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, savefig='None', atol=1e-6, rtol=1e-6, random_seed=42, surf_chg=(0.0, 0.0), sigma_q=101, sigma_skew=0.5, verbose=False, eps_rel=80.10, use_log2=False): """ A negative D (diffusion coefficent) denotes: mobility := -D D := 0 A positive D calculates mobility from Einstein-Smoluchowski relation """ assert 0 <= base and base <= 1 assert 0 <= offset and offset <= 1 if random_seed: np.random.seed(random_seed) n = 2 if D < 0: mobility = -D D = 0 else: mobility = electrical_mobility_from_D(D, 1, 298.15) print(D, mobility) # 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) # Setup the system stoich_active = [] stoich_prod = [] k = [] rd = ReactionDiffusion( n, stoich_active, stoich_prod, k, N, D=[D, D], z_chg=[1, -1], mobility=[mobility, -mobility], x=x, geom=geom, logy=logy, logt=logt, logx=logx, nstencil=nstencil, lrefl=lrefl, rrefl=rrefl, auto_efield=True, surf_chg=surf_chg, eps_rel=eps_rel, # water at 20 deg C faraday_const=1, vacuum_permittivity=1, use_log2=use_log2 ) # Initial conditions sigma = (xend-x0)/sigma_q sigma = [(1-sigma_skew)*sigma, sigma_skew*sigma] y0 = np.vstack(pair_of_gaussians( rd.xcenters, [base+offset, base-offset], sigma, logy, logx, geom, use_log2)).transpose() if logy: y0 = sigm(y0) if plot: # Plot initial E-field import matplotlib.pyplot as plt plt.figure(figsize=(6, 10)) rd.calc_efield((rd.expb(y0) if logy else y0).flatten()) plt.subplot(4, 1, 3) plt.plot(rd.xcenters, rd.efield, label="E at t=t0") plt.plot(rd.xcenters, rd.xcenters*0, label="0") # Run the integration tout = np.linspace(t0, tend, nt) integr = run(rd, y0, tout, atol=atol, rtol=rtol, sigm_damp=True, C0_is_log=logy, with_jacobian=(not num_jacobian), method=method) Cout = integr.Cout if verbose: print(integr.info) # Plot results if plot: def _plot(y, ttl=None, **kwargs): plt.plot(rd.xcenters, y, **kwargs) plt.xlabel((('log_%s({})' % ('2' if use_log2 else 'e')) if logx else '{}').format('x / m')) plt.ylabel('C / M') if ttl: plt.title(ttl) for i in range(nt): plt.subplot(4, 1, 1) c = 1-tout[i]/tend c = (1.0-c, .5-c/2, .5-c/2) _plot(Cout[i, :, 0], 'Simulation (N={})'.format(rd.N), c=c, label='$z_A=1$' if i == nt-1 else None) _plot(Cout[i, :, 1], c=c[::-1], label='$z_B=-1$' if i == nt-1 else None) plt.legend() plt.subplot(4, 1, 2) delta_y = Cout[i, :, 0] - Cout[i, :, 1] _plot(delta_y, 'Diff'.format(rd.N), c=[c[2], c[0], c[1]], label='A-B (positive excess)' if i == nt-1 else None) plt.legend(loc='best') plt.xlabel("$x~/~m$") plt.ylabel(r'Concentration / M') ylim = plt.gca().get_ylim() if N < 100: plt.vlines(rd.x, ylim[0], ylim[1], linewidth=1.0, alpha=0.2, colors='gray') plt.subplot(4, 1, 3) plt.plot(rd.xcenters, rd.efield, label="E at t=tend") plt.xlabel("$x~/~m$") plt.ylabel("$E~/~V\cdot m^{-1}$") plt.legend() for i in range(3): plt.subplot(4, 1, i+1) ylim = plt.gca().get_ylim() for d in (-1, 1): center_loc = [x0+(base+d*offset)*(xend-x0)]*2 plt.plot(rd.logb(center_loc) if logx else center_loc, ylim, '--k') plt.subplot(4, 1, 4) for i in range(n): amount = [rd.integrated_conc(Cout[j, :, i]) for j in range(nt)] plt.plot(tout, amount, c=c[::(1, -1)[i]], label=chr(ord('A')+i)) plt.xlabel('Time / s') plt.ylabel('Amount / mol') plt.legend(loc='best') plt.tight_layout() save_and_or_show_plot(savefig=savefig) return tout, Cout, integr.info, rd
def integrate_rd(D=2e-3, t0=1., tend=13., x0=1e-10, xend=1.0, N=256, nt=42, logt=False, logy=False, logx=False, random=False, k=1.0, nstencil=3, linterpol=False, rinterpol=False, num_jacobian=False, method='bdf', integrator='scipy', iter_type='default', linear_solver='default', atol=1e-8, rtol=1e-10, factor=1e5, random_seed=42, plot=False, savefig='None', verbose=False, scaling=1.0, ilu_limit=1000.0, first_step=0.0, n_jac_diags=0, use_log2=False): """ Solves the time evolution of diffusion from a constant (undepletable) source term. Optionally plots the results. In the plots time is represented by color scaling from black (:math:`t_0`) to red (:math:`t_{end}`) """ if t0 == 0.0: raise ValueError("t0==0 => Dirac delta function C0 profile.") if random_seed: np.random.seed(random_seed) tout = np.linspace(t0, tend, nt) units = defaultdict(lambda: 1) units['amount'] = 1.0/scaling # Setup the grid x = generate_grid(x0, xend, N, logx, random=random) modulation = [1 if (i == 0) else 0 for i in range(N)] rd = ReactionDiffusion.nondimensionalisation( 2, [[0], [1]], [[1], [0]], [k, factor*k], N=N, D=[0, D], x=x, logy=logy, logt=logt, logx=logx, nstencil=nstencil, lrefl=not linterpol, rrefl=not rinterpol, modulated_rxns=[0, 1], modulation=[modulation, modulation], unit_registry=units, ilu_limit=ilu_limit, n_jac_diags=n_jac_diags, faraday_const=1, vacuum_permittivity=1, use_log2=use_log2 ) # Calc initial conditions / analytic reference values Cref = analytic(rd.xcenters, tout, D, x0, xend, logx, use_log2=use_log2).reshape( nt, N, 1) source = np.zeros_like(Cref[0, ...]) source[0, 0] = factor y0 = np.concatenate((source, Cref[0, ...]), axis=1) # Run the integration integr = Integration( rd, y0, tout, integrator=integrator, atol=atol, rtol=rtol, with_jacobian=(not num_jacobian), method=method, iter_type=iter_type, linear_solver=linear_solver, first_step=first_step) Cout = integr.with_units('Cout') if verbose: import pprint pprint.pprint(integr.info) spat_ave_rmsd_over_atol = spat_ave_rmsd_vs_time( Cout[:, :, 1], Cref[:, :, 0]) / atol tot_ave_rmsd_over_atol = np.average(spat_ave_rmsd_over_atol) if plot: # Plot results import matplotlib.pyplot as plt plt.figure(figsize=(6, 10)) def _plot(C, c, ttl=None, vlines=False, smooth=True): if vlines: plt.vlines(rd.x, 0, np.ones_like(rd.x)*max(C), linewidth=1, colors='gray') if smooth: plt.plot(rd.xcenters, C, c=c) else: for i, _C in enumerate(C): plt.plot([rd.x[i], rd.x[i+1]], [_C, _C], c=c) plt.xlabel('x / m') plt.ylabel('C / M') if ttl: plt.title(ttl) for i in range(nt): kwargs = dict(smooth=(N >= 20), vlines=(i == 0 and N < 20)) c = 1-tout[i]/tend c = (1.0-c, .5-c/2, .5-c/2) plt.subplot(4, 1, 1) _plot(Cout[i, :, 1], c, 'Simulation (N={})'.format(rd.N), **kwargs) plt.subplot(4, 1, 2) _plot(Cref[i, :, 0], c, 'Analytic', **kwargs) plt.subplot(4, 1, 3) _plot((Cout[i, :, 1]-Cref[i, :, 0]), c, "Error".format(atol), **kwargs) plt.subplot(4, 1, 4) plt.plot(integr.tout, spat_ave_rmsd_over_atol) plt.plot([integr.tout[0], integr.tout[-1]], [tot_ave_rmsd_over_atol]*2, '--') plt.title("RMSD / atol") plt.tight_layout() save_and_or_show_plot(savefig=savefig) return tout, Cout, integr.info, rd, tot_ave_rmsd_over_atol
def integrate_rd(tend=2.0, A0=1.0, nt=67, t0=0.0, rates='3.40715,4.0', logy=False, logt=False, plot=False, savefig='None', method='bdf', atol='1e-7,1e-6,1e-5', rtol='1e-6', sigm_damp=False, num_jac=False, scale_err=1.0, small='None', use_log2=False, plotlogy=False, plotlogt=False, verbose=False): """ Analytic solution through Bateman equation => ensure :math:`|k_i - k_j| \gg eps` """ k = list(map(float, rates.split(','))) n = len(k)+1 if n > 4: raise ValueError("Max 3 consequtive decays supported at the moment.") atol = list(map(float, atol.split(','))) if len(atol) == 1: atol = atol[0] rtol = float(rtol) rd = ReactionDiffusion( n, [[i] for i in range(n-1)], [[i] for i in range(1, n)], k, logy=logy, logt=logt, use_log2=use_log2) y0 = np.zeros(n) y0[0] = A0 if small == 'None': tiny = None else: tiny = 0 y0 += float(small) tout = np.linspace(t0, tend, nt) integr = run(rd, y0, tout, atol=atol, rtol=rtol, method=method, with_jacobian=not num_jac, sigm_damp=sigm_damp, tiny=tiny) Cout, yout, info = integr.Cout, integr.yout, integr.info Cref = get_Cref(k, y0, tout - tout[0]).reshape((nt, 1, n)) if verbose: print('rate: ', k) print(info) if plot: nshow = min(n, 3) try: min_atol = min(info['atol']) except: min_atol = info['atol'] import matplotlib.pyplot as plt plt.figure(figsize=(6, 10)) c = 'rgb' for i, l in enumerate('ABC'[:nshow]): ax = plt.subplot(nshow+1, 1, 1) if plotlogy: ax.set_yscale('log') if plotlogt: ax.set_xscale('log') ax.plot(tout, Cout[:, 0, i], label=l, color=c[i]) ax = plt.subplot(nshow+1, 1, 2+i) if plotlogy: ax.set_yscale('symlog') # abs error might be < 0 if plotlogt: ax.set_xscale('log') ax.plot(tout, (Cout[:, 0, i]-Cref[:, 0, i])/min_atol, label=l, color=c[i]) try: atol = info['atol'][i] except: atol = info['atol'] try: rtol = info['rtol'][i] except: rtol = info['rtol'] le_l, le_u = solver_linear_error( yout[:, 0, i], rtol, atol, rd.logy, scale_err=scale_err, expb=rd.expb) plt.fill_between(tout, (le_l - Cout[:, 0, i])/min_atol, (le_u - Cout[:, 0, i])/min_atol, color=c[i], alpha=0.2) # Print indices and values of violations of (scaled) error bounds def _print(violation): print(violation) print(le_l[violation], Cref[violation, 0, i], le_u[violation]) l_viols = np.where(le_l > Cref[:, 0, i])[0] u_viols = np.where(le_u < Cref[:, 0, i])[0] if verbose and (len(l_viols) > 0 or len(u_viols) > 0): print("Outside error bounds for rtol, atol:", rtol, atol) # for violation in chain(l_viols, u_viols): # _print(violation) plt.subplot(nshow+1, 1, 1) plt.title('Concentration vs. time') plt.legend(loc='best', prop={'size': 11}) plt.xlabel('t') plt.ylabel('[X]') for i in range(nshow): plt.subplot(nshow+1, 1, 2+i) plt.title('Absolute error in [{}](t) / min(atol)'.format('ABC'[i])) plt.legend(loc='best') plt.xlabel('t') plt.ylabel('|E[{0}]| / {1:7.0g}'.format('ABC'[i], min_atol)) plt.tight_layout() save_and_or_show_plot(savefig=savefig) return integr.yout, Cref, rd, info
def integrate_rd(tend=1.0, k1=7e-1, k2=3e2, k3=7.0, A0=1.0, B0=0.0, C0=0.0, nt=512, plot=False, savefig='None'): """ Runs integration and (optionally) generates plots. Examples -------- :: $ python steady_state_approx.py --plot --savefig steady_state_approx.png .. image:: ../_generated/steady_state_approx.png :: $ python steady_state_approx.py --plot --savefig \ steady_state_approx.html :download:`steady_state_approx.html\ <../_generated/steady_state_approx.html>` """ def f(t): return A0/(k3/k2*A0 + 1)*np.exp(-k1*t) y = [A0, B0, C0] k = [k1, k2, k3] rd = ReactionDiffusion( 3, [[0], [1], [0, 1]], [[1], [2], [1, 2]], k) t = np.linspace(0, tend, nt) integr = run(rd, y, t) A_ssB = 1/(1/f(t) - k3/k2) A_ssB_2fast = f(t) if plot: import matplotlib.pyplot as plt plt.figure(figsize=(6, 10)) ax = plt.subplot(3, 1, 1) plot_C_vs_t_in_bin(rd, t, integr.Cout, ax=ax) plt.subplot(3, 1, 2) plt.plot(t, integr.Cout[:, 0, 0] - A_ssB, label="Abs. err. in A assuming steady state of B") plt.legend(loc='best', prop={'size': 11}) plt.subplot(3, 1, 3) plt.plot(t, integr.Cout[:, 0, 0] - A_ssB_2fast, label="Abs. err. in A when also assuming k2 >> k3") plt.legend(loc='best', prop={'size': 11}) plt.tight_layout() save_and_or_show_plot(savefig=savefig) def ydot(x, y): return (-k1*y[0] - k3*y[0]*y[1], k1*y[0] - k2*y[1], k2*y[1] + k3*y[0]*y[1]) return t, integr.Cout, A_ssB, A_ssB_2fast, ydot