def with_units(self, attr): if attr == "tout": return self.tout * get_derived_unit(self.rd.unit_registry, "time") if attr == "Cout": return self.Cout * get_derived_unit(self.rd.unit_registry, "concentration") else: raise ValueError("Unknown attr: %s" % attr)
def with_units(self, attr): if attr == 'tout': return self.tout * get_derived_unit(self.rd.unit_registry, 'time') elif attr == 'Cout': return self.Cout * get_derived_unit(self.rd.unit_registry, 'concentration') elif attr == 'x': return self.rd.x * get_derived_unit(self.rd.unit_registry, 'length') else: raise ValueError("Unknown attr: %s" % attr)
def _dedim(arg, key, unit_registry): return to_unitless(arg, get_derived_unit(unit_registry, key))
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 plot_solver_linear_excess_error(integration, Cref, ax=None, x=None, ti=slice(None), bi=0, si=0, **kwargs): """ Plots the excess error commited by the intergrator, divided by the span of the tolerances (atol + rtol*|y_i|). Parameters ---------- integration: chemreac.integrate.Integration result from integration. Cref: array or float analytic solution to compare with ax: Axes instance or dict if ax is a dict it is used as \*\*kwargs passed to matplotlib.pyplot.axes (default: None) x: array (optional) x-values, when None it is deduced to be either t or x (when ti or bi are slices repecitvely) (default: None) ti: slice time indices bi: slice bin indices si: integer specie index plot_kwargs: dict keyword arguments passed to matplotlib.pyplot.plot (default: None) fill_between_kwargs: dict keyword arguments passed to matplotlib.pyplot.fill_between (default: None) scale_err: float value with which errors are scaled. (default: 1.0) fill: bool whether or not to fill error span \*\*kwargs: common keyword arguments of plot_kwargs and fill_between_kwargs, e.g. 'color', (default: None). See Also -------- plot_solver_linear_error """ if x is None: if isinstance(ti, slice): x = integration.tout[ti] elif isinstance(bi, slice): x = integration.rd.xcenters[bi] else: raise NotImplementedError("Failed to deduce x-axis.") ax = _init_axes(ax) le_l, le_u = solver_linear_error_from_integration(integration, ti, bi, si) Eexcess_l = Cref[ti, bi, si] - le_l # Excessive if negative Eexcess_u = Cref[ti, bi, si] - le_u # Excessive if positive u_conc = get_derived_unit(integration.rd.unit_registry, "concentration") Eexcess_l[np.argwhere(Eexcess_l >= 0)] = 0 * u_conc Eexcess_u[np.argwhere(Eexcess_u <= 0)] = 0 * u_conc fused = np.concatenate((Eexcess_l[..., np.newaxis], Eexcess_u[..., np.newaxis]), axis=-1) indices = np.argmax(abs(fused), axis=-1) Eexcess = fused[np.indices(indices.shape), indices][0, ...] le_span = le_u - le_l ax.plot(np.asarray(integration.tout), np.asarray(Eexcess / le_span), **kwargs) return ax
def plot_solver_linear_error( integration, Cref=0, ax=None, x=None, ti=slice(None), bi=0, si=0, plot_kwargs=None, fill_between_kwargs=None, scale_err=1.0, fill=True, **kwargs ): """ Parameters ---------- integration: chemreac.integrate.Integration result from integration. Cref: array or float analytic solution to compare with ax: Axes instance or dict if ax is a dict it is used as key word arguments passed to matplotlib.pyplot.axes (default: None) x: array (optional) x-values, when None it is deduced to be either t or x (when ti or bi are slices repecitvely) (default: None) ti: slice time indices bi: slice bin indices si: integer specie index plot_kwargs: dict keyword arguments passed to matplotlib.pyplot.plot (default: None) fill_between_kwargs: dict keyword arguments passed to matplotlib.pyplot.fill_between (default: None) scale_err: float value with which errors are scaled. (default: 1.0) fill: bool whether or not to fill error span \*\*kwargs common keyword arguments of plot_kwargs and fill_between_kwargs, e.g. 'color', (default: None). See Also -------- plot_solver_linear_excess_error """ ax = _init_axes(ax) Cerr = integration.Cout - to_unitless(Cref, get_derived_unit(integration.rd.unit_registry, "concentration")) if x is None: if isinstance(ti, slice): x = integration.tout[ti] elif isinstance(bi, slice): x = integration.rd.xcenters[bi] else: raise NotImplementedError("Failed to deduce x-axis.") plot_kwargs = plot_kwargs or {} set_dict_defaults_inplace(plot_kwargs, kwargs) plt.plot(np.asarray(x), np.asarray(scale_err * Cerr[ti, bi, si]), **plot_kwargs) if fill: le_l, le_u = solver_linear_error_from_integration(integration, ti=ti, bi=bi, si=si) Cerr_u = le_u - Cref[ti, bi, si] Cerr_l = le_l - Cref[ti, bi, si] fill_between_kwargs = fill_between_kwargs or {} set_dict_defaults_inplace(fill_between_kwargs, {"alpha": 0.2}, kwargs) plt.fill_between( np.asarray(x), np.asarray(scale_err * Cerr_l), np.asarray(scale_err * Cerr_u), **fill_between_kwargs ) return ax