def fit_func(tout, k_fw, tdelay, eps_l): pconv.append((k_fw, tdelay, eps_l)) rd.k = [k_fw, k_fw/Keq] if tdelay > 0.0: integr = run(rd, c0, [0, tdelay]) c1 = integr.Cout[1, 0, :] else: c1 = c0 integr = run(rd, c1, tdelay+tout) return integr.Cout[:, 0, 2]*eps_l
def test_integrate__only_1_species_diffusion__mass_conservation(N_wjac_geom_varying): N, wjac, geom, varying = N_wjac_geom_varying # Test that mass convervation is fulfilled wrt diffusion. x = np.linspace(0.01*N, N, N+1) y0 = (x[0]/2/N+x[1:]/N)**2 if varying: D = np.linspace(0, (0.02*N)**0.5, N)**2 else: D = [0.02*N] sys = ReactionDiffusion(1, [], [], [], N=N, D=D, x=x, geom=geom, nstencil=3, lrefl=True, rrefl=True) tout = np.linspace(0, 10.0, 50) atol, rtol = 1e-6, 1e-8 integr = run(sys, y0, tout, atol=atol, rtol=rtol, with_jacobian=wjac, method='adams') yout = integr.yout[:, :, 0] x /= N if geom == 'f': yprim = yout*(x[1:]**1 - x[:-1]**1) elif geom == 'c': yprim = yout*(x[1:]**2 - x[:-1]**2) elif geom == 's': yprim = yout*(x[1:]**3 - x[:-1]**3) else: raise ybis = np.sum(yprim, axis=1) assert np.allclose(np.average(ybis), ybis, atol=atol*(1e2 if varying else 1), rtol=rtol*(1e2 if varying else 1))
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 test_get_decay_Cref(): 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) assert np.allclose(Cref, integr.yout)
def test_plot_C_vs_x(): 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) ax = plot_C_vs_x(rd, tout, integr.yout, [0, 1], 6) assert isinstance(ax, matplotlib.axes.Axes)
def test_plot_per_reaction_contribution(): rd = _get_decay_rd(1) tout = np.linspace(0, 3.0, 7) y0 = [3.0, 1.0] integr = run(rd, y0, tout) axes = plot_per_reaction_contribution(integr, [0, 1]) for ax in axes: assert isinstance(ax, matplotlib.axes.Axes)
def test_ReactionDiffusion_fields_and_g_values(log_geom): # modulation in x means x_center # A -> B # mod1 (x**2) # C -> D # mod1 (x**2) # E -> F # mod2 (sqrt(x)) # G -> H # no modulation (logy, logt, use_log2), geom = log_geom k = np.array([3.0, 7.0, 13.0, 22.0]) N = 5 n = 8 D = np.zeros(n) x = np.linspace(3, 7, N+1) xc = x[:-1] + np.diff(x)/2 fields = [[xc[i]*xc[i] for i in range(N)], [xc[i]*xc[i] for i in range(N)], [xc[i]**0.5 for i in range(N)]] g_values = [[-k[0], k[0], 0, 0, 0, 0, 0, 0], [0, 0, -k[1], k[1], 0, 0, 0, 0], [0, 0, 0, 0, -k[2], k[2], 0, 0]] g_value_parents = [0, 2, 4] rd = ReactionDiffusion( n, [[i] for i in range(n-2, n, 2)], [[i] for i in range(n-1, n, 2)], k=k[3:], N=N, D=D, x=x, fields=fields, g_values=g_values, g_value_parents=g_value_parents, logy=logy, logt=logt, geom=geom, use_log2=use_log2 ) assert rd.n == n assert rd.N == N assert np.allclose(rd.D[:n], D) assert np.allclose(rd.k, k[3:]) assert np.allclose(rd.xcenters, xc) assert np.allclose(rd.fields, fields) assert np.allclose(rd.g_values, g_values) y0 = np.array([[13.0, 23.0, 32.0, 43.0, 12.0, 9.5, 17.0, 27.5]*N]) y0 = y0.flatten() t0, tend, nt = 1.0, 1.1, 42 tout = np.linspace(t0, tend, nt+1) integr = run(rd, y0, tout, atol=1e-11, rtol=1e-11, with_jacobian=True) def _get_bkf(bi, ri): if ri < 2: return xc[bi]*xc[bi] elif ri < 3: return xc[bi]**0.5 else: return 1.0 yref = np.hstack([ np.hstack([ np.array([ y0[i]*np.exp(-_get_bkf(bi, i//2)*k[i//2]*(tout-t0)), y0[i+1]+y0[i]*(1-np.exp(-_get_bkf(bi, i//2)*k[i//2]*(tout-t0))) ]).transpose() for i in range(0, n, 2) ]) for bi in range(N)]) assert np.allclose(integr.Cout.flatten(), yref.flatten())
def test_plot_C_vs_t_and_x(): import mpl_toolkits 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) ax = plot_C_vs_t_and_x(rd, tout, integr.yout, 0) assert isinstance(ax, mpl_toolkits.mplot3d.Axes3D)
def test_plot_jacobian(): # A -> B rd = _get_decay_rd(1) y0 = [3.0, 1.0] tout = np.linspace(0, 3.0, 7) integr = run(rd, y0, tout) axes = plot_jacobian(rd, integr.tout, integr.yout, [0, 1]) for ax in axes: assert isinstance(ax, matplotlib.axes.Axes)
def test_plot_solver_linear_excess_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_excess_error(integr, Cref) assert isinstance(ax, matplotlib.axes.Axes)
def simulate_stopped_flow(rd, t, c0, k, noiselvl, eps_l, tdelay=None): if tdelay is None: tdelay = np.abs(np.random.normal( t[-1]/20, scale=t[-1]/20)) integr = run(rd, c0, t) ytrue = eps_l*integr.Cout[:, 0, 2] skip_nt = np.argwhere(t >= tdelay)[0] tinp = t[:-skip_nt] if skip_nt > 0 else t yinp = ytrue[skip_nt:] + noiselvl*np.random.normal( size=len(t)-skip_nt) return tinp, yinp
def test_decay(log): # A -> B n = 2 logy, logt, use_log2 = log k0 = 0.13 rd = ReactionDiffusion(n, [[0]], [[1]], k=[k0], logy=logy, logt=logt, use_log2=use_log2) y0 = [3.0, 1.0] t0, tend, nt = 5.0, 17.0, 42 tout = np.linspace(t0, tend, nt+1) integr = run(rd, y0, tout) yref = np.array([y0[0]*np.exp(-k0*(tout-t0)), y0[1]+y0[0]*(1-np.exp(-k0*(tout-t0)))]).transpose() assert np.allclose(integr.Cout[:, 0, :], yref)
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 _test_integrate(params): logy, logt, N, geom, use_log2 = params rd = load(JSON_PATH, N=N, logy=logy, logt=logt, geom=geom, use_log2=use_log2) assert rd.geom == geom y0 = np.array(C0*N) ref = np.genfromtxt(BLESSED_PATH) ref_t = ref[:, 0] ref_y = ref[:, 1:rd.n+1] t0 = 3.0 tend = 5.0+t0 nt = 137 tout = np.linspace(t0, tend, nt+1) assert np.allclose(tout-t0, ref_t) _test_f_and_dense_jac_rmaj(rd, t0, rd.logb(y0) if logy else y0) integr = run(rd, y0, tout, with_jacobian=True) for i in range(N): assert np.allclose(integr.Cout[:, i, :], ref_y, atol=1e-4)
def main(tdelay=1.0, B0=0.6, noiselvl=3e-4, nt=200, eps_l=4200.0, plot=False): """ Solution: 1. non-linear fit to: A) if approx equal conc A+A -> C B) else: A+B -> C 2. Use as guess for guess and shoot. A + B <-> C """ Keq = 10.0 k_fw_true = 1.3 ktrue = [1.3, k_fw_true/Keq] c0 = [1.0, B0, 0.0] ttrue = np.linspace(0, 10, nt) rd_eq = ReactionDiffusion(3, [[0, 1], [2]], [[2], [0, 1]], k=ktrue) tinp, yinp = simulate_stopped_flow( rd_eq, ttrue, c0, ktrue, noiselvl, eps_l, tdelay) k_fw_opt, d_opt, eps_l_opt = fit_binary_eq_from_temporal_abs_data( tinp, yinp, c0, Keq, True) rd_eq.k = [k_fw_opt, k_fw_opt/Keq] integr = run(rd_eq, c0, ttrue) yopt = integr.Cout[:, 0, 2]*eps_l_opt if plot: import matplotlib.pyplot as plt # Plot plt.subplot(2, 1, 1) plt.plot(tinp, yinp, label='Input data') plt.plot(ttrue-tdelay, yopt, label='Shooting Opt (k={})'.format(k_fw_opt)) plt.legend(loc='best') plt.subplot(2, 1, 2) plt.plot(tinp, yinp, label='Input data') # TODO: this needs to be improved... yquad = (B0-1/(1/B0+k_fw_opt*ttrue))*eps_l_opt plt.plot(ttrue-tdelay, yquad, label='Equal initial conc treatment') plt.legend(loc='best') plt.show()
def integrate_rd(t0=1e-7, tend=.1, doserate=15, N=1000, nt=512, nstencil=3, logy=False, logt=False, num_jacobian=False, savefig='None', verbose=False, plot=False, plot_jacobians=False): null_conc = 1e-24 mu = 1.0 # linear attenuation rho = 1.0 # kg/dm3 rd = load(os.path.join(os.path.dirname(__file__), 'aqueous_radiolysis.json'), ReactionDiffusion, N=N, logy=logy, logt=logt, bin_k_factor=[[doserate * rho * exp(-mu * i / N)] for i in range(N)], nstencil=nstencil) y0_by_name = json.load( open( os.path.join(os.path.dirname(__file__), 'aqueous_radiolysis.y0.json'), 'rt')) # y0 with a H2 gradient y0 = np.array([[ y0_by_name.get(k, null_conc) if k != 'H2' else 1e-3 / (i + 2) for k in rd.substance_names ] for i in range(rd.N)]) tout = np.logspace(log(t0), log(tend), nt + 1, base=e) integr = run(rd, y0, tout, with_jacobian=(not num_jacobian))
def test_autodimerization(arrhenius): # A + A -> B from chemreac.chemistry import ( Reaction, ReactionSystem, mk_sn_dict_from_names ) sbstncs = mk_sn_dict_from_names('AB') if arrhenius: from chempy.kinetics.arrhenius import ArrheniusParam k = ArrheniusParam(7e11, -8.314472*298.15*(np.log(3) - np.log(7) - 11*np.log(10))) variables = {'temperature': 298.15} param = 3.0 else: param = k = 3.0 variables = None r1 = Reaction({'A': 2}, {'B': 1}, k) rsys = ReactionSystem([r1], sbstncs) rd = ReactionDiffusion.from_ReactionSystem(rsys, variables=variables) t = np.linspace(0, 5, 3) A0, B0 = 1.0, 0.0 integr = run(rd, [A0, B0], t) Aref = 1/(1/A0+2*param*t) yref = np.vstack((Aref, (A0-Aref)/2)).transpose() assert np.allclose(integr.yout[:, 0, :], yref)
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(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=3., tend=7., x0=0.0, xend=1.0, mu=None, N=64, nt=42, geom='f', logt=False, logy=False, logx=False, random=False, k=0.0, nstencil=3, linterpol=False, rinterpol=False, num_jacobian=False, method='bdf', scale_x=False, atol=1e-6, rtol=1e-6, efield=False, random_seed=42): if t0 == 0.0: raise ValueError("t0==0 => Dirac delta function C0 profile.") if random_seed: np.random.seed(random_seed) decay = (k != 0.0) n = 2 if decay else 1 mu = float(mu or x0) tout = np.linspace(t0, tend, nt) assert geom in 'fcs' geom = {'f': FLAT, 'c': CYLINDRICAL, 's': SPHERICAL}[geom] analytic = { FLAT: flat_analytic, CYLINDRICAL: cylindrical_analytic, SPHERICAL: spherical_analytic }[geom] # Setup the grid _x0 = log(x0) if logx else x0 _xend = log(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) rd = ReactionDiffusion( 2 if decay else 1, [[0]] if decay else [], [[1]] if decay else [], [k] if decay else [], N, D=[D]*(2 if decay else 1), z_chg=[1]*(2 if decay else 1), mobility=[0.01]*(2 if decay else 1), x=x, geom=geom, logy=logy, logt=logt, logx=logx, nstencil=nstencil, lrefl=not linterpol, rrefl=not rinterpol, xscale=1/(x[1]-x[0]) if scale_x else 1.0 ) if efield: if geom != FLAT: 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, mu, x0, xend, 0.01 if efield else 0, logy, logx).reshape(nt, N, 1) if decay: yref = np.concatenate((yref, yref), axis=2) if logy: yref[:, :, 0] += -k*t yref[:, :, 1] += np.log(1-np.exp(-k*t)) else: yref[:, :, 0] *= np.exp(-k*t) yref[:, :, 1] *= 1-np.exp(-k*t) # Run the integration integr = run(rd, yref[0, ...], tout, atol=atol, rtol=rtol, with_jacobian=(not num_jacobian), method=method, C0_is_log=logy)
def integrate_rd(D=2e-3, t0=3., tend=7., x0=0.0, xend=1.0, mu=None, N=64, nt=42, geom='f', logt=False, logy=False, logx=False, random=False, k=0.0, nstencil=3, linterpol=False, rinterpol=False, num_jacobian=False, method='bdf', scale_x=False, atol=1e-6, rtol=1e-6, efield=False, random_seed=42): if t0 == 0.0: raise ValueError("t0==0 => Dirac delta function C0 profile.") if random_seed: np.random.seed(random_seed) decay = (k != 0.0) n = 2 if decay else 1 mu = float(mu or x0) tout = np.linspace(t0, tend, nt) assert geom in 'fcs' geom = {'f': FLAT, 'c': CYLINDRICAL, 's': SPHERICAL}[geom] analytic = { FLAT: flat_analytic, CYLINDRICAL: cylindrical_analytic, SPHERICAL: spherical_analytic }[geom] # Setup the grid _x0 = log(x0) if logx else x0 _xend = log(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) rd = ReactionDiffusion(2 if decay else 1, [[0]] if decay else [], [[1]] if decay else [], [k] if decay else [], N, D=[D] * (2 if decay else 1), z_chg=[1] * (2 if decay else 1), mobility=[0.01] * (2 if decay else 1), x=x, geom=geom, logy=logy, logt=logt, logx=logx, nstencil=nstencil, lrefl=not linterpol, rrefl=not rinterpol, xscale=1 / (x[1] - x[0]) if scale_x else 1.0) if efield: if geom != FLAT: 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, mu, x0, xend, 0.01 if efield else 0, logy, logx).reshape(nt, N, 1) if decay: yref = np.concatenate((yref, yref), axis=2) if logy: yref[:, :, 0] += -k * t yref[:, :, 1] += np.log(1 - np.exp(-k * t)) else: yref[:, :, 0] *= np.exp(-k * t) yref[:, :, 1] *= 1 - np.exp(-k * t) # Run the integration integr = run(rd, yref[0, ...], tout, atol=atol, rtol=rtol, with_jacobian=(not num_jacobian), method=method, C0_is_log=logy)
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=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(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(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
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