def test_n_jac_diags(n_jac_diags): N, n, nstencil = 10, 1, 7 rd = ReactionDiffusion(n, [], [], [], N=N, nstencil=nstencil, n_jac_diags=n_jac_diags, D=[9]) assert np.allclose(rd.xcenters, [.05, .15, .25, .35, .45, .55, .65, .75, .85, .95]) y0 = np.ones(N) # Dense jref_cdns = np.zeros((n*N, n*N), order='F') jout_cdns = np.zeros((n*N, n*N), order='F') sm = SymRD.from_rd(rd) sm.dense_jac(0.0, y0.flatten(), jref_cdns) rd.dense_jac_cmaj(0.0, y0.flatten(), jout_cdns) assert np.allclose(jout_cdns, jref_cdns) # Banded jref_cbnd = rd.alloc_jout(order='F', pad=0) jout_cbnd = rd.alloc_jout(order='F') sm.banded_jac(0.0, y0.flatten(), jref_cbnd) rd.banded_jac_cmaj(0.0, y0.flatten(), jout_cbnd) assert np.allclose(jout_cbnd[rd.n*rd.n_jac_diags:, :], jref_cbnd) # Compressed jref_cmprs = rd.alloc_jout_compressed() jout_cmprs = rd.alloc_jout_compressed() sm.compressed_jac(0.0, y0.flatten(), jref_cmprs) rd.compressed_jac_cmaj(0.0, y0.flatten(), jout_cmprs) assert np.allclose(jout_cmprs, jref_cmprs)
def _test_f(rd, t, y, fref=None): fout = rd.alloc_fout() rd.f(t, np.asarray(y, dtype=np.float64), fout) if fref is None: fref = fout else: assert np.allclose(fout, fref) if isinstance(rd, SymRD): return _test_f(SymRD.from_rd(rd), t, y, fref)
def test_SymRD(): rd = SymRD(2, [[0]], [[1]], k=[5.0]) fout = rd.alloc_fout() rd.f(0, [7, 3], fout) assert np.allclose(fout, [-5*7, 5*7]) jout = rd.alloc_jout(banded=False) rd.dense_jac_rmaj(0, [7, 3], jout) assert np.allclose(jout, [[-5.0, 0.0], [5.0, 0.0]])
def _test_dense_jac_rmaj(rd, t, y, jref=None): jout = rd.alloc_jout(banded=False, order='C') rd.dense_jac_rmaj(t, np.asarray(y, dtype=np.float64), jout) atol = 2e-13 rtol = 2e-13 if jref is None: jref = jout else: # Not perfect Jacobian # only nearest neighbour # assert np.allclose(jout, jref) # ...hence: for ri in range(rd.ny): for ci in range(max(0, ri-rd.n), min(rd.ny, ri+rd.n+1)): out, ref = jout[ri, ci], jref[ri, ci] assert abs(out - ref) < atol + rtol*abs(ref) if isinstance(rd, SymRD): return _test_dense_jac_rmaj(SymRD.from_rd(rd), t, y, jref)
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 test_ReactionDiffusion__3_reactions_4_species_5_bins_k_factor( geom_refl): # UNSUPPORTED since `bin_k_factor` was replaced with `fields` # if a real world scenario need per bin modulation of binary # reactions and the functionality is reintroduced, this test # is useful from sympy import finite_diff_weights geom, refl = geom_refl lrefl, rrefl = refl # r[0]: A + B -> C # r[1]: D + C -> D + A + B # r[2]: B + B -> D # r[0] r[1] r[2] stoich_active = [[0, 1], [2, 3], [1, 1]] stoich_prod = [[2], [0, 1, 3], [3]] n = 4 N = 5 D = np.array([2.6, 3.7, 5.11, 7.13])*213 y0 = np.array([ 2.5, 1.2, 3.2, 4.3, 2.7, 0.8, 1.6, 2.4, 3.1, 0.3, 1.5, 1.8, 3.3, 0.6, 1.6, 1.4, 3.6, 0.9, 1.7, 1.2 ]).reshape((5, 4)) x = np.array([11.0, 13.3, 17.0, 23.2, 29.8, 37.2]) xc_ = x[:-1]+np.diff(x)/2 xc_ = [x[0]-(xc_[0]-x[0])]+list(xc_)+[x[-1]+(x[-1]-xc_[-1])] assert len(xc_) == 7 k = [31.0, 37.0, 41.0] # (r[0], r[1]) modulations over bins modulated_rxns = [0, 1] modulation = [[i+3 for i in range(N)], [i+4 for i in range(N)]] nstencil = 3 nsidep = 1 rd = ReactionDiffusion( 4, stoich_active, stoich_prod, k, N, D=D, x=x, geom=geom, nstencil=nstencil, lrefl=lrefl, rrefl=rrefl, modulated_rxns=modulated_rxns, modulation=modulation) assert np.allclose(xc_, rd.xc) lb = stencil_pxci_lbounds(nstencil, N, lrefl, rrefl) if lrefl: if rrefl: assert lb == [0, 1, 2, 3, 4] else: assert lb == [0, 1, 2, 3, 3] else: if rrefl: assert lb == [1, 1, 2, 3, 4] else: assert lb == [1, 1, 2, 3, 3] assert lb == list(map(rd._stencil_bi_lbound, range(N))) pxci2bi = pxci_to_bi(nstencil, N) assert pxci2bi == [0, 0, 1, 2, 3, 4, 4] assert pxci2bi == list(map(rd._xc_bi_map, range(N+2))) D_weight = [] for bi in range(N): local_x_serie = xc_[lb[bi]:lb[bi]+nstencil] local_x_around = xc_[nsidep+bi] w = finite_diff_weights( 2, local_x_serie, x0=local_x_around ) D_weight.append(w[-1][-1]) if geom == 'f': pass elif geom == 'c': for wi in range(nstencil): # first order derivative D_weight[bi][wi] += w[-2][-1][wi]*1/local_x_around elif geom == 's': for wi in range(nstencil): # first order derivative D_weight[bi][wi] += w[-2][-1][wi]*2/local_x_around else: raise RuntimeError assert np.allclose(rd.D_weight, np.array(D_weight, dtype=np.float64).flatten()) def cflux(si, bi): f = 0.0 for k in range(nstencil): f += rd.D_weight[bi*nstencil+k]*y0[pxci2bi[lb[bi]+k], si] return D[si]*f r = [ [k[0]*modulation[0][bi]*y0[bi, 0]*y0[bi, 1] for bi in range(N)], [k[1]*modulation[1][bi]*y0[bi, 3]*y0[bi, 2] for bi in range(N)], [k[2]*y0[bi, 1]**2 for bi in range(N)], ] fref = np.array([[ -r[0][bi] + r[1][bi] + cflux(0, bi), -r[0][bi] + r[1][bi] - 2*r[2][bi] + cflux(1, bi), r[0][bi] - r[1][bi] + cflux(2, bi), r[2][bi] + cflux(3, bi) ] for bi in range(N)]).flatten() # Now let's check that the Jacobian is correctly computed. def dfdC(bi, lri, lci): v = 0.0 for ri in range(len(stoich_active)): totl = (stoich_prod[ri].count(lri) - stoich_active[ri].count(lri)) if totl == 0: continue actv = stoich_active[ri].count(lci) if actv == 0: continue v += actv*totl*r[ri][bi]/y0[bi, lci] return v def jac_elem(ri, ci): bri, bci = ri // n, ci // n lri, lci = ri % n, ci % n elem = 0.0 def _diffusion(): _elem = 0.0 for k in range(nstencil): if pxci2bi[lb[bri]+k] == bci: _elem += D[lri]*rd.D_weight[bri*nstencil+k] return _elem if bri == bci: # on block diagonal elem += dfdC(bri, lri, lci) if lri == lci: elem += _diffusion() elif bri == bci - 1: if lri == lci: elem = _diffusion() elif bri == bci + 1: if lri == lci: elem = _diffusion() return elem jref = np.zeros((n*N, n*N), order='C') for ri, ci in np.ndindex(n*N, n*N): jref[ri, ci] = jac_elem(ri, ci) # Compare to what is calculated using our C++ callback _test_f_and_dense_jac_rmaj(rd, 0, y0.flatten(), fref, jref) jout_cmaj = np.zeros((n*N, n*N), order='F') rd.dense_jac_cmaj(0.0, y0.flatten(), jout_cmaj) assert np.allclose(jout_cmaj, jref) ref_banded_j = get_banded(jref, n, N) ref_banded_j_symbolic = rd.alloc_jout(order='F', pad=0) symrd = SymRD.from_rd(rd) symrd.banded_jac(0.0, y0.flatten(), ref_banded_j_symbolic) assert np.allclose(ref_banded_j_symbolic, ref_banded_j) jout_bnd_packed_cmaj = np.zeros((3*n+1, n*N), order='F') rd.banded_jac_cmaj(0.0, y0.flatten(), jout_bnd_packed_cmaj) if os.environ.get('plot_tests', False): import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt from chemreac.util.plotting import coloured_spy fig = plt.figure() ax = fig.add_subplot(3, 1, 1) coloured_spy(ref_banded_j, ax=ax) plt.title('ref_banded_j') ax = fig.add_subplot(3, 1, 2) coloured_spy(jout_bnd_packed_cmaj[n:, :], ax=ax) plt.title('jout_bnd_packed_cmaj') ax = fig.add_subplot(3, 1, 3) coloured_spy(ref_banded_j-jout_bnd_packed_cmaj[n:, :], ax=ax) plt.title('diff') plt.savefig(__file__+'.png') assert np.allclose(jout_bnd_packed_cmaj[n:, :], ref_banded_j)