def test_stencil_pxci_lbounds(): b = stencil_pxci_lbounds(5, 7) assert b == [2, 2, 2, 3, 4, 4, 4] b = stencil_pxci_lbounds(3, 5, lrefl=True) assert b == [0, 1, 2, 3, 3] b = stencil_pxci_lbounds(3, 5, rrefl=True) assert b == [1, 1, 2, 3, 4] b = stencil_pxci_lbounds(5, 7, lrefl=True, rrefl=True) assert b == [0, 1, 2, 3, 4, 5, 6] b = stencil_pxci_lbounds(3, 4, rrefl=True) # [y0, y0, y1, y2, y3, y3] assert b == [1, 1, 2, 3]
def test_ReactionDiffusion__only_1_species_diffusion_7bins(log): # Diffusion without reaction N = 7 nstencil = 5 nsidep = (nstencil-1)//2 t0 = 3.0 logy, logt = log D = 2.0 y0 = np.array([12, 8, 11, 5, 7, 4, 9], dtype=np.float64) x = np.array([3, 5, 13, 17, 23, 25, 35, 37], dtype=np.float64) rd = ReactionDiffusion(1, [], [], [], D=[D], x=x, logy=logy, logt=logt, nstencil=nstencil, lrefl=False, rrefl=False) weights = [ [951/8800, -716/2475, 100/297, -75/352, 311/5400], [321/8800, -161/2475, 7/297, 3/352, -19/5400], [-39/8800, 109/2475, -127/1485, 87/1760, -19/5400], [-2/693, 38/675, -129/1100, 7/108, -1/1050], [0, 9/160, -7/72, 2/45, -1/288], [-8/1575, 9/400, 0, -19/450, 25/1008], [16/315, -9/32, 31/72, -13/45, 179/2016] ] assert np.allclose(rd.D_weight, np.array(weights).flatten()) lb = stencil_pxci_lbounds(nstencil, N) yi = pxci_to_bi(nstencil, N) fref = np.array([sum([D*weights[i][j]*y0[yi[j+lb[i]]] for j in range(nstencil)]) for i in range(N)]) if logy: fref /= y0 if logt: fref *= t0 jref = np.zeros((N, N)) for i in range(N): for j in range(max(0, i-1), min(N, i+2)): if logy: if j == i+1 or j == i-1: for k in range(nstencil): if yi[k+lb[i]] == j: jref[i, j] += D*weights[i][k]*y0[j]/y0[i] else: # j == i assert i == j for k in range(nstencil): cyi = yi[k+lb[i]] if i == cyi: continue jref[i, i] -= D*weights[i][k]*y0[cyi]/y0[i] else: if i-1 <= j and j <= i+1: jref[i, j] = D*weights[i][j-lb[i]+nsidep] if logt: jref *= t0 t = rd.logb(t0) if logt else t0 y = rd.logb(y0) if logy else y0 _test_f_and_dense_jac_rmaj(rd, t, y, fref, jref) jout_bnd = np.zeros((4, N), order='F') rd.banded_jac_cmaj(t, y, jout_bnd) jref_bnd = get_banded(jref, 1, N) assert np.allclose(jout_bnd[1:, :], jref_bnd) # compressed_jac_cmaj actually use all diagonals rd = ReactionDiffusion(1, [], [], [], D=[D], x=x, logy=logy, logt=logt, nstencil=nstencil, lrefl=False, rrefl=False, n_jac_diags=2) jout_cmprs = rd.alloc_jout_compressed() rd.compressed_jac_cmaj(t, y, jout_cmprs) from block_diag_ilu import Compressed_from_dense jref2 = np.zeros((N, N)) for i in range(N): for j in range(max(0, i-2), min(N, i+3)): if logy: if i-2 <= j <= i+2: if i == j: for k in range(nstencil): cyi = yi[k+lb[i]] if i == cyi: continue jref2[i, i] -= D*weights[i][k]*y0[cyi]/y0[i] else: for k in range(nstencil): if yi[k+lb[i]] == j: jref2[i, j] += D*weights[i][k]*y0[j]/y0[i] else: if i-2 <= j <= i+2: jref2[i, j] = D*weights[i][j-lb[i]+nsidep] if logt: jref2 *= t0 jref_cmprs = Compressed_from_dense(jref2, N, 1, nsidep).data assert np.allclose(jout_cmprs, jref_cmprs)
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)
def test_ReactionDiffusion__lrefl_7(log): # Diffusion without reaction (7 bins) from sympy import finite_diff_weights lrefl, rrefl = True, False N = 7 t0 = 3.0 logy, logt = log D = 17.0 nstencil = 5 nsidep = (nstencil-1)//2 x = np.array([3, 5, 13, 17, 23, 25, 35, 37], dtype=np.float64) y0 = np.array([12, 8, 11, 5, 7, 4, 9], dtype=np.float64) xc_ = padded_centers(x, nsidep) lb = stencil_pxci_lbounds(nstencil, N, lrefl=lrefl, rrefl=rrefl) rd = ReactionDiffusion(1, [], [], [], D=[D], x=x, logy=logy, logt=logt, N=N, nstencil=nstencil, lrefl=lrefl, rrefl=rrefl) y = rd.logb(y0) if logy else y0 t = rd.logb(t0) if logt else t0 le = nsidep if lrefl else 0 D_weight_ref = np.array([ finite_diff_weights( 2, xc_[lb[i]:lb[i]+nstencil], x0=xc_[le+i])[-1][-1] for i in range(N)], dtype=np.float64) assert np.allclose(rd.D_weight, D_weight_ref.flatten()) yi = pxci_to_bi(nstencil, N) fref = D*np.array([ sum([rd.D_weight[i*nstencil+j]*y0[yi[lb[i]+j]] for j in range(nstencil)]) for i in range(N) ]) if logy: fref /= y0 if logt: fref *= t0 _test_f(rd, t, y, fref) if logy: def cb(i, j): if abs(i-j) > 1: return 0 # imperfect Jacobian elif i == j: res = 0 for k in range(nstencil): cyi = yi[lb[i] + k] # current y index if cyi != i: res -= y0[cyi]/y0[i]*rd.D_weight[i*nstencil + k] return res else: res = 0 for k in range(nstencil): cyi = yi[lb[i] + k] # current y index if cyi == j: res += y0[j]/y0[i]*rd.D_weight[i*nstencil + k] return res else: def cb(i, j): if abs(i-j) > 1: return 0 # imperfect Jacobian res = 0 for k in range(nstencil): if yi[lb[i]+k] == j: res += rd.D_weight[i*nstencil + k] return res jref = D*np.array([cb(i, j) for i, j in product( range(N), range(N))]).reshape(N, N) if logt: jref *= t0 _test_dense_jac_rmaj(rd, t, y, jref)