%22%22%22Batch%20reactor%20mechanistic%20model%20%E2%80%94%20Sobol%20global%20sensitivity%20analysis%20with%20gsax.%0A%0AWalks%20through%20Sobol%20global%20sensitivity%20analysis%20on%20a%20batch%20reactor%20running%20a%0Afirst-order%20liquid-phase%20reaction%20A%20-%3E%20B.%20The%20rate%20constant%20k(T%2C%20pH)%20combines%0Aa%20centred%20Arrhenius%20temperature%20dependence%20with%20a%20Hill-type%20pH%20saturation%0Acurve%2C%20and%20the%20inlet%20concentration%20Ca0%20enters%20linearly%20through%20the%20mass%0Abalance.%20The%20mechanistic%20model%20is%20treated%20as%20already%20fitted%3A%20the%20notebook%20is%0Aabout%20variance%20attribution%20across%20the%20operating%20envelope%2C%20not%20parameter%0Aestimation.%0A%0ARun%20interactively%3A%20%60%60uv%20run%20marimo%20edit%20examples%2Fbatch_reactor_gsa.py%60%60%0ARun%20as%20script%3A%20%20%20%20%20%60%60uv%20run%20python%20examples%2Fbatch_reactor_gsa.py%60%60%0A%22%22%22%0A%0A%23%20ruff%3A%20noqa%3A%20F722%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_intro(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Batch%20reactor%20sensitivity%20analysis%20with%20**gsax**%0A%0A%20%20%20%20A%20batch%20reactor%20running%20a%20first-order%20liquid-phase%20reaction%0A%20%20%20%20%24A%20%5Cto%20B%24.%20The%20rate%20constant%20%24k(T%2C%20%5Cmathrm%7BpH%7D)%24%20combines%20a%20centred%0A%20%20%20%20Arrhenius%20temperature%20dependence%20with%20a%20Hill-type%20pH%20saturation%0A%20%20%20%20curve%2C%20and%20the%20inlet%20concentration%20%24C_%7BA%2C0%7D%24%20feeds%20the%20mass%0A%20%20%20%20balance%20directly.%20We%20treat%20the%20mechanistic%20model%20as%20already%0A%20%20%20%20fitted%20%E2%80%94%20the%20notebook%20asks%20the%20classical%20global-sensitivity%20question%0A%20%20%20%20instead%3A%20across%20the%20operating%20envelope%2C%20how%20much%20of%20the%20variance%20in%0A%20%20%20%20the%20outlet%20concentration%20is%20explained%20by%20each%20input%2C%20and%20which%20pairs%0A%20%20%20%20interact%3F%0A%0A%20%20%20%20The%20batch%20reactor%20mass%20balance%20for%20the%20reactant%20is%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cfrac%7BdC_A%7D%7Bdt%7D%20%5C%3B%3D%5C%3B%20%5Cfrac%7B1%7D%7B%5Ctau%7D%5C%2C(C_%7BA%2C0%7D%20-%20C_A)%0A%20%20%20%20%20%20%20%20%5C%3B-%5C%3B%20k(T%2C%20%5Cmathrm%7BpH%7D)%5C%2CC_A%2C%0A%20%20%20%20%24%24%0A%0A%20%20%20%20where%20%24%5Ctau%20%3D%20V%2Fq%24%20is%20the%20residence%20time.%20Starting%20from%20a%20clean%0A%20%20%20%20reactor%20at%20%24C_A(0)%20%3D%200%24%2C%20the%20closed-form%20solution%20is%0A%0A%20%20%20%20%24%24%0A%20%20%20%20C_A(t)%20%5C%3B%3D%5C%3B%20%5Cfrac%7BC_%7BA%2C0%7D%7D%7B1%20%2B%20k%5Ctau%7D%5C%2C%0A%20%20%20%20%20%20%20%20%5Cbigl(1%20-%20e%5E%7B-(1%2F%5Ctau%20%2B%20k)%5C%2Ct%7D%5Cbigr).%0A%20%20%20%20%24%24%0A%0A%20%20%20%20The%20notebook%20treats%20%24C_A(t)%24%20as%20a%20time-series%20multi-output%2C%20runs%0A%20%20%20%20Sobol%20with%20bootstrap%20resampling%20**once**%2C%20and%20reads%20the%20indices%20off%0A%20%20%20%20as%20bar%20plots%2C%20a%20time-resolved%20sensitivity%20profile%2C%20and%20a%20pairwise%0A%20%20%20%20interaction%20heatmap.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_imports()%3A%0A%20%20%20%20import%20jax%0A%20%20%20%20import%20jax.numpy%20as%20jnp%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%0A%20%20%20%20import%20gsax%0A%0A%20%20%20%20return%20gsax%2C%20jax%2C%20jnp%2C%20mo%2C%20np%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_model_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Mechanistic%20model%0A%0A%20%20%20%20The%20kinetics%20are%20factored%20into%20a%20temperature%20term%20and%20a%20pH%20term%20and%0A%20%20%20%20multiplied%20together.%20Concretely%2C%0A%0A%20%20%20%20%24%24%0A%20%20%20%20k(T%2C%20%5Cmathrm%7BpH%7D)%20%5C%3B%3D%5C%3B%20k_%7B%5Cmathrm%7Bsat%7D%7D(%5Cmathrm%7BpH%7D)%20%5C%2C%0A%20%20%20%20%20%20%20%20%5Cexp%5C!%5CBig(-%5Ctfrac%7BE_a%7D%7BR%7D%5CBig(%5Ctfrac%7B1%7D%7BT_K%7D%20-%20%5Ctfrac%7B1%7D%7BT_%7B%5Cmathrm%7Bref%7D%7D%7D%5CBig)%5CBig)%2C%0A%20%20%20%20%5Cqquad%0A%20%20%20%20k_%7B%5Cmathrm%7Bsat%7D%7D(%5Cmathrm%7BpH%7D)%20%5C%3B%3D%5C%3B%20b%20%2B%20%5Cfrac%7Ba%7D%7B1%20%2B%20(%5Cmathrm%7BpH%7D%2F%5Cmathrm%7BpH%7D_%7B50%7D)%5En%7D.%0A%20%20%20%20%24%24%0A%0A%20%20%20%20Centring%20at%20%24T_%7B%5Cmathrm%7Bref%7D%7D%20%3D%20298.15%5C%2C%5Cmathrm%7BK%7D%24%20keeps%0A%20%20%20%20%24k(T_%7B%5Cmathrm%7Bref%7D%7D%2C%20%5Ccdot)%20%3D%20k_%7B%5Cmathrm%7Bsat%7D%7D(%5Ccdot)%24%2C%20so%20the%20pH%0A%20%20%20%20curve%20has%20a%20clean%20physical%20meaning%20at%20room%20temperature.%20An%0A%20%20%20%20activation%20energy%20of%20%24E_a%20%3D%2030%5C%2C%5Cmathrm%7BkJ%2Fmol%7D%24%20produces%20roughly%20a%0A%20%20%20%20two-fold%20rate%20change%20per%20ten%20Kelvin%20around%20the%20centring%20point.%20The%0A%20%20%20%20residence%20time%20is%20fixed%20at%20%24%5Ctau%20%3D%202%24%20in%20dimensionless%20time%20units%2C%0A%20%20%20%20chosen%20so%20that%20the%20half-life%20of%20the%20start-up%20transient%20is%0A%20%20%20%20comparable%20to%20the%20kinetic%20e-folding%20scale%20across%20the%20operating%20box.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_model(jnp)%3A%0A%20%20%20%20T_REF%20%3D%20298.15%0A%20%20%20%20R_GAS%20%3D%208.314e-3%0A%20%20%20%20EA%20%3D%2030.0%0A%0A%20%20%20%20K_BASELINE%20%3D%200.14%0A%20%20%20%20K_AMPLITUDE%20%3D%201.05%0A%20%20%20%20PH50%20%3D%205.85%0A%20%20%20%20HILL%20%3D%205.0%0A%0A%20%20%20%20TAU%20%3D%202.0%0A%0A%20%20%20%20def%20k_rate(temperature_C%2C%20pH)%3A%0A%20%20%20%20%20%20%20%20T_K%20%3D%20temperature_C%20%2B%20273.15%0A%20%20%20%20%20%20%20%20arrhenius%20%3D%20jnp.exp(-EA%20%2F%20R_GAS%20*%20(1.0%20%2F%20T_K%20-%201.0%20%2F%20T_REF))%0A%20%20%20%20%20%20%20%20sat%20%3D%20K_BASELINE%20%2B%20K_AMPLITUDE%20%2F%20(1.0%20%2B%20(pH%20%2F%20PH50)%20**%20HILL)%0A%20%20%20%20%20%20%20%20return%20sat%20*%20arrhenius%0A%0A%20%20%20%20def%20batch_reactor_trajectory(Ca0%2C%20temperature_C%2C%20pH%2C%20ts)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Closed-form%20batch%20reactor%20concentration%20starting%20from%20Ca(0)%20%3D%200.%22%22%22%0A%20%20%20%20%20%20%20%20k%20%3D%20k_rate(temperature_C%2C%20pH)%0A%20%20%20%20%20%20%20%20Ca_ss%20%3D%20Ca0%20%2F%20(1.0%20%2B%20k%20*%20TAU)%0A%20%20%20%20%20%20%20%20decay%20%3D%20jnp.exp(-(1.0%20%2F%20TAU%20%2B%20k)%20*%20ts)%0A%20%20%20%20%20%20%20%20return%20Ca_ss%20*%20(1.0%20-%20decay)%0A%0A%20%20%20%20return%20(batch_reactor_trajectory%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_doe_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Problem%20and%20Saltelli%20sampling%0A%0A%20%20%20%20The%20three%20inputs%20are%20uniform%20on%20physically%20reasonable%20boxes%3A%0A%0A%20%20%20%20%7C%20input%20%7C%20range%20%7C%20units%20%7C%0A%20%20%20%20%7C---%7C---%7C---%7C%0A%20%20%20%20%7C%20%24C_%7BA%2C0%7D%24%20%7C%20%24%5B0.75%2C%5C%2C%201.5%5D%24%20%7C%20mol%20%2F%20L%20%7C%0A%20%20%20%20%7C%20%24T%24%20%7C%20%24%5B15%2C%5C%2C%2035%5D%24%20%7C%20%C2%B0C%20%7C%0A%20%20%20%20%7C%20%24%5Cmathrm%7BpH%7D%24%20%7C%20%24%5B4.5%2C%5C%2C%207.5%5D%24%20%7C%20%E2%80%94%20%7C%0A%0A%20%20%20%20%60gsax.sample(...)%60%20returns%20a%20%60SamplingResult%60%20whose%20%60.samples%60%0A%20%20%20%20attribute%20is%20the%20deduplicated%20unique-row%20sample%20matrix%3B%20gsax%0A%20%20%20%20reconstructs%20the%20expanded%20Saltelli%20ordering%20internally%20inside%0A%20%20%20%20%60analyze%60.%20Setting%20%60calc_second_order%3DTrue%60%20activates%20the%20extra%0A%20%20%20%20cross-matrix%20needed%20for%20%24S_%7Bij%7D%24.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_problem(gsax)%3A%0A%20%20%20%20problem%20%3D%20gsax.Problem.from_dict(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Ca0%22%3A%20(0.75%2C%201.5)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22temperature_C%22%3A%20(15.0%2C%2035.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22pH%22%3A%20(4.5%2C%207.5)%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20output_names%3D(%22Ca%22%2C)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20sampling_result%20%3D%20gsax.sample(%0A%20%20%20%20%20%20%20%20problem%2C%0A%20%20%20%20%20%20%20%20n_samples%3D4096%2C%0A%20%20%20%20%20%20%20%20seed%3D0%2C%0A%20%20%20%20%20%20%20%20calc_second_order%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20print(f%22unique%20Saltelli%20rows%3A%20%7Bsampling_result.samples.shape%7D%22)%0A%20%20%20%20return%20problem%2C%20sampling_result%0A%0A%0A%40app.cell%0Adef%20_evaluate(batch_reactor_trajectory%2C%20jnp%2C%20np%2C%20sampling_result)%3A%0A%20%20%20%20ts%20%3D%20jnp.asarray(np.linspace(0.05%2C%206.0%2C%2040))%0A%0A%20%20%20%20X%20%3D%20jnp.asarray(sampling_result.samples)%0A%20%20%20%20Ca0%20%3D%20X%5B%3A%2C%200%3A1%5D%0A%20%20%20%20temperature_C%20%3D%20X%5B%3A%2C%201%3A2%5D%0A%20%20%20%20pH%20%3D%20X%5B%3A%2C%202%3A3%5D%0A%0A%20%20%20%20Y%20%3D%20batch_reactor_trajectory(Ca0%2C%20temperature_C%2C%20pH%2C%20ts%5BNone%2C%20%3A%5D)%0A%20%20%20%20Y%20%3D%20Y%5B...%2C%20None%5D%0A%20%20%20%20print(f%22output%20shape%3A%20%7BY.shape%7D%20%20(N%2C%20T%2C%20K)%22)%0A%20%20%20%20return%20Y%2C%20ts%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_trajectory_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Trajectory%20preview%0A%0A%20%20%20%20A%20handful%20of%20trajectories%20drawn%20at%20random%20from%20the%20Saltelli%20sample.%0A%20%20%20%20The%20asymptote%20%24C_A(%5Cinfty)%20%3D%20C_%7BA%2C0%7D%2F(1%20%2B%20k%5Ctau)%24%20varies%20by%20an%20order%0A%20%20%20%20of%20magnitude%20across%20the%20input%20box%3B%20the%20time%20to%20reach%20asymptote%20is%0A%20%20%20%20set%20by%20%241%2F%5Ctau%20%2B%20k%24%2C%20so%20warm%20and%20acidic%20combinations%20saturate%0A%20%20%20%20fastest%20while%20cool%20and%20basic%20ones%20drag.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_trajectory_plot(Y%2C%20np%2C%20plt%2C%20ts)%3A%0A%20%20%20%20rng%20%3D%20np.random.default_rng(0)%0A%20%20%20%20idx%20%3D%20rng.choice(Y.shape%5B0%5D%2C%20size%3D24%2C%20replace%3DFalse)%0A%20%20%20%20fig_traj%2C%20ax_traj%20%3D%20plt.subplots(figsize%3D(7.5%2C%204.0))%0A%20%20%20%20for%20_i%20in%20idx%3A%0A%20%20%20%20%20%20%20%20ax_traj.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20np.asarray(ts)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20np.asarray(Y%5B_i%2C%20%3A%2C%200%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22C0%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20alpha%3D0.35%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D1.0%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20ax_traj.set_xlabel(%22t%22)%0A%20%20%20%20ax_traj.set_ylabel(%22Ca%22)%0A%20%20%20%20ax_traj.set_title(%22Batch%20reactor%20concentration%20trajectories%20%E2%80%94%2024%20sampled%20inputs%22)%0A%20%20%20%20ax_traj.grid(alpha%3D0.3)%0A%20%20%20%20fig_traj.tight_layout()%0A%20%20%20%20fig_traj%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_analyze_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Sobol%20analysis%20with%20bootstrap%0A%0A%20%20%20%20%60gsax.analyze(...)%60%20accepts%20the%20%60(N%2C%20T%2C%20K)%60%20output%20array%20and%0A%20%20%20%20returns%20indices%20with%20shape%20%60(T%2C%20K%2C%20D)%60%20for%20first-order%2C%20total-order%0A%20%20%20%20and%20%60(T%2C%20K%2C%20D%2C%20D)%60%20for%20second-order.%20Passing%20%60num_resamples%20%3E%200%60%0A%20%20%20%20together%20with%20a%20PRNG%20%60key%60%20switches%20on%20a%20vectorised%20non-parametric%0A%20%20%20%20bootstrap%20and%20populates%20the%20%60_conf%60%20arrays%20with%20%60%5Blower%2C%20upper%5D%60%0A%20%20%20%20endpoints%20in%20a%20leading%20dimension%20of%20size%202.%0A%0A%20%20%20%20%60prenormalize%3DTrue%60%20matches%20the%20SALib%20output-standardisation%0A%20%20%20%20convention%3A%20the%20cleaned%20output%20array%20is%20centred%20and%20scaled%20to%20unit%0A%20%20%20%20variance%20once%2C%20before%20the%20bootstrap%2C%20not%20per%20resample.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_analyze(Y%2C%20gsax%2C%20jax%2C%20sampling_result)%3A%0A%20%20%20%20result%20%3D%20gsax.analyze(%0A%20%20%20%20%20%20%20%20sampling_result%2C%0A%20%20%20%20%20%20%20%20Y%2C%0A%20%20%20%20%20%20%20%20num_resamples%3D200%2C%0A%20%20%20%20%20%20%20%20conf_level%3D0.95%2C%0A%20%20%20%20%20%20%20%20ci_method%3D%22quantile%22%2C%0A%20%20%20%20%20%20%20%20key%3Djax.random.key(0)%2C%0A%20%20%20%20%20%20%20%20prenormalize%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20print(result)%0A%20%20%20%20return%20(result%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_bar_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Steady-state%20bar%20plot%0A%0A%20%20%20%20At%20the%20final%20time%20step%20the%20system%20is%20essentially%20at%20steady%20state%2C%20so%0A%20%20%20%20%24C_A(%5Cinfty)%20%3D%20C_%7BA%2C0%7D%2F(1%20%2B%20k(T%2C%5Cmathrm%7BpH%7D)%5C%2C%5Ctau)%24%20depends%20on%20all%0A%20%20%20%20three%20inputs.%20The%20bars%20show%20first-order%20%24S_1%24%20and%20total-order%20%24S_T%24%0A%20%20%20%20with%20the%20bootstrap%2095%20%25%20confidence%20intervals%20drawn%20as%20error%20bars.%20A%0A%20%20%20%20visible%20gap%20between%20%24S_1%24%20and%20%24S_T%24%20for%20any%20input%20signals%0A%20%20%20%20interactions%20with%20at%20least%20one%20other%20input.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_bar_plot(np%2C%20plt%2C%20problem%2C%20result)%3A%0A%20%20%20%20_names%20%3D%20list(problem.names)%0A%20%20%20%20_s1%20%3D%20np.asarray(result.S1%5B-1%2C%200%2C%20%3A%5D)%0A%20%20%20%20_st%20%3D%20np.asarray(result.ST%5B-1%2C%200%2C%20%3A%5D)%0A%20%20%20%20_s1_lo%20%3D%20np.asarray(result.S1_conf%5B0%2C%20-1%2C%200%2C%20%3A%5D)%0A%20%20%20%20_s1_hi%20%3D%20np.asarray(result.S1_conf%5B1%2C%20-1%2C%200%2C%20%3A%5D)%0A%20%20%20%20_st_lo%20%3D%20np.asarray(result.ST_conf%5B0%2C%20-1%2C%200%2C%20%3A%5D)%0A%20%20%20%20_st_hi%20%3D%20np.asarray(result.ST_conf%5B1%2C%20-1%2C%200%2C%20%3A%5D)%0A%0A%20%20%20%20_x%20%3D%20np.arange(len(_names))%0A%20%20%20%20_width%20%3D%200.38%0A%20%20%20%20fig_bar%2C%20ax_bar%20%3D%20plt.subplots(figsize%3D(7.5%2C%204.5))%0A%20%20%20%20ax_bar.bar(%0A%20%20%20%20%20%20%20%20_x%20-%20_width%20%2F%202%2C%0A%20%20%20%20%20%20%20%20_s1%2C%0A%20%20%20%20%20%20%20%20_width%2C%0A%20%20%20%20%20%20%20%20yerr%3Dnp.stack(%5B_s1%20-%20_s1_lo%2C%20_s1_hi%20-%20_s1%5D)%2C%0A%20%20%20%20%20%20%20%20color%3D%22C0%22%2C%0A%20%20%20%20%20%20%20%20capsize%3D4%2C%0A%20%20%20%20%20%20%20%20label%3D%22S1%20(first-order)%22%2C%0A%20%20%20%20)%0A%20%20%20%20ax_bar.bar(%0A%20%20%20%20%20%20%20%20_x%20%2B%20_width%20%2F%202%2C%0A%20%20%20%20%20%20%20%20_st%2C%0A%20%20%20%20%20%20%20%20_width%2C%0A%20%20%20%20%20%20%20%20yerr%3Dnp.stack(%5B_st%20-%20_st_lo%2C%20_st_hi%20-%20_st%5D)%2C%0A%20%20%20%20%20%20%20%20color%3D%22C3%22%2C%0A%20%20%20%20%20%20%20%20capsize%3D4%2C%0A%20%20%20%20%20%20%20%20label%3D%22ST%20(total-order)%22%2C%0A%20%20%20%20)%0A%20%20%20%20ax_bar.axhline(0.0%2C%20color%3D%22black%22%2C%20linewidth%3D0.5)%0A%20%20%20%20ax_bar.set_xticks(_x)%0A%20%20%20%20ax_bar.set_xticklabels(_names)%0A%20%20%20%20ax_bar.set_ylabel(%22Sobol%20index%22)%0A%20%20%20%20ax_bar.set_title(%22Steady-state%20sensitivity%20(Ca%20at%20the%20final%20time%20step)%22)%0A%20%20%20%20ax_bar.legend(frameon%3DFalse)%0A%20%20%20%20ax_bar.grid(axis%3D%22y%22%2C%20alpha%3D0.3)%0A%20%20%20%20fig_bar.tight_layout()%0A%20%20%20%20fig_bar%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_timeseries_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Time-resolved%20sensitivity%0A%0A%20%20%20%20Each%20input's%20importance%20evolves%20through%20the%20start-up%20transient.%0A%20%20%20%20Right%20after%20start-up%2C%20%24C_A(t)%20%5Capprox%20(C_%7BA%2C0%7D%2F%5Ctau)%5C%2Ct%24%20and%20the%0A%20%20%20%20inlet%20concentration%20explains%20essentially%20all%20variance.%20As%20the%0A%20%20%20%20reactor%20approaches%20steady%20state%20the%20kinetic%20terms%20%E2%80%94%20and%20therefore%0A%20%20%20%20%24T%24%20and%20%24%5Cmathrm%7BpH%7D%24%20%E2%80%94%20take%20over%20a%20growing%20share.%20The%20shaded%20bands%0A%20%20%20%20are%20the%20bootstrap%2095%20%25%20envelopes.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_timeseries_plot(np%2C%20plt%2C%20problem%2C%20result%2C%20ts)%3A%0A%20%20%20%20_names%20%3D%20list(problem.names)%0A%20%20%20%20_s1%20%3D%20np.asarray(result.S1%5B%3A%2C%200%2C%20%3A%5D)%0A%20%20%20%20_s1_lo%20%3D%20np.asarray(result.S1_conf%5B0%2C%20%3A%2C%200%2C%20%3A%5D)%0A%20%20%20%20_s1_hi%20%3D%20np.asarray(result.S1_conf%5B1%2C%20%3A%2C%200%2C%20%3A%5D)%0A%20%20%20%20_st%20%3D%20np.asarray(result.ST%5B%3A%2C%200%2C%20%3A%5D)%0A%20%20%20%20_st_lo%20%3D%20np.asarray(result.ST_conf%5B0%2C%20%3A%2C%200%2C%20%3A%5D)%0A%20%20%20%20_st_hi%20%3D%20np.asarray(result.ST_conf%5B1%2C%20%3A%2C%200%2C%20%3A%5D)%0A%0A%20%20%20%20_t_np%20%3D%20np.asarray(ts)%0A%20%20%20%20_colors%20%3D%20(%22C0%22%2C%20%22C2%22%2C%20%22C3%22)%0A%20%20%20%20fig_ts%2C%20axes_ts%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(11.0%2C%204.5)%2C%20sharey%3DTrue)%0A%20%20%20%20for%20_d%2C%20(_name%2C%20_color)%20in%20enumerate(zip(_names%2C%20_colors%2C%20strict%3DTrue))%3A%0A%20%20%20%20%20%20%20%20axes_ts%5B0%5D.plot(_t_np%2C%20_s1%5B%3A%2C%20_d%5D%2C%20color%3D_color%2C%20linewidth%3D1.6%2C%20label%3D_name)%0A%20%20%20%20%20%20%20%20axes_ts%5B0%5D.fill_between(_t_np%2C%20_s1_lo%5B%3A%2C%20_d%5D%2C%20_s1_hi%5B%3A%2C%20_d%5D%2C%20color%3D_color%2C%20alpha%3D0.18)%0A%20%20%20%20%20%20%20%20axes_ts%5B1%5D.plot(_t_np%2C%20_st%5B%3A%2C%20_d%5D%2C%20color%3D_color%2C%20linewidth%3D1.6%2C%20label%3D_name)%0A%20%20%20%20%20%20%20%20axes_ts%5B1%5D.fill_between(_t_np%2C%20_st_lo%5B%3A%2C%20_d%5D%2C%20_st_hi%5B%3A%2C%20_d%5D%2C%20color%3D_color%2C%20alpha%3D0.18)%0A%0A%20%20%20%20axes_ts%5B0%5D.set_title(%22First-order%20S1(t)%22)%0A%20%20%20%20axes_ts%5B1%5D.set_title(%22Total-order%20ST(t)%22)%0A%20%20%20%20for%20_ax%20in%20axes_ts%3A%0A%20%20%20%20%20%20%20%20_ax.set_xlabel(%22t%22)%0A%20%20%20%20%20%20%20%20_ax.set_ylim(-0.05%2C%201.1)%0A%20%20%20%20%20%20%20%20_ax.grid(alpha%3D0.3)%0A%20%20%20%20%20%20%20%20_ax.legend(frameon%3DFalse)%0A%20%20%20%20axes_ts%5B0%5D.set_ylabel(%22Sobol%20index%22)%0A%20%20%20%20fig_ts.tight_layout()%0A%20%20%20%20fig_ts%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_interaction_md(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Pairwise%20interactions%0A%0A%20%20%20%20The%20second-order%20matrix%20%24S_%7Bij%7D%24%20measures%20the%20share%20of%20variance%0A%20%20%20%20explained%20jointly%20by%20inputs%20%24i%24%20and%20%24j%24%20but%20not%20by%20either%20alone.%20For%0A%20%20%20%20the%20steady-state%20outlet%20concentration%20the%20dominant%20pair%20is%0A%20%20%20%20temperature%20and%20pH%2C%20since%20both%20inputs%20enter%20only%20through%20the%20rate%0A%20%20%20%20constant%20and%20combine%20multiplicatively%20inside%20%24k(T%2C%20%5Cmathrm%7BpH%7D)%5Ctau%24.%0A%20%20%20%20The%20diagonal%20is%20left%20blank%20because%20%24S_%7Bii%7D%24%20is%20not%20defined.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_interaction_plot(np%2C%20plt%2C%20problem%2C%20result)%3A%0A%20%20%20%20_names%20%3D%20list(problem.names)%0A%20%20%20%20_s2%20%3D%20np.asarray(result.S2%5B-1%2C%200%2C%20%3A%2C%20%3A%5D)%0A%0A%20%20%20%20_finite%20%3D%20_s2%5Bnp.isfinite(_s2)%5D%0A%20%20%20%20_vmin%20%3D%20float(_finite.min())%20if%20_finite.size%20else%200.0%0A%20%20%20%20_vmax%20%3D%20float(_finite.max())%20if%20_finite.size%20else%201.0%0A%0A%20%20%20%20fig_s2%2C%20ax_s2%20%3D%20plt.subplots(figsize%3D(5.5%2C%204.5))%0A%20%20%20%20_im%20%3D%20ax_s2.imshow(_s2%2C%20cmap%3D%22viridis%22%2C%20origin%3D%22lower%22%2C%20vmin%3D_vmin%2C%20vmax%3D_vmax)%0A%20%20%20%20_threshold%20%3D%20_vmin%20%2B%200.5%20*%20(_vmax%20-%20_vmin)%0A%20%20%20%20for%20_i%20in%20range(_s2.shape%5B0%5D)%3A%0A%20%20%20%20%20%20%20%20for%20_j%20in%20range(_s2.shape%5B1%5D)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20np.isfinite(_s2%5B_i%2C%20_j%5D)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ax_s2.text(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_j%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_i%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7B_s2%5B_i%2C%20_j%5D%3A.2f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ha%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20va%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22white%22%20if%20_s2%5B_i%2C%20_j%5D%20%3C%20_threshold%20else%20%22black%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontsize%3D9%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20ax_s2.set_xticks(range(len(_names)))%0A%20%20%20%20ax_s2.set_yticks(range(len(_names)))%0A%20%20%20%20ax_s2.set_xticklabels(_names)%0A%20%20%20%20ax_s2.set_yticklabels(_names)%0A%20%20%20%20ax_s2.set_title(%22Steady-state%20second-order%20indices%20S2%5Bi%2C%20j%5D%22)%0A%20%20%20%20fig_s2.colorbar(_im%2C%20ax%3Dax_s2%2C%20label%3D%22S2%22)%0A%20%20%20%20fig_s2.tight_layout()%0A%20%20%20%20fig_s2%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_outro(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Take-aways%0A%0A%20%20%20%201.%20**Inlet%20concentration%20dominates%20the%20transient%2C%20kinetics%20dominate%0A%20%20%20%20%20%20%20the%20asymptote.**%20Right%20after%20start-up%20the%20variance%20is%20almost%0A%20%20%20%20%20%20%20purely%20a%20function%20of%20%24C_%7BA%2C0%7D%24%20because%20the%20reaction%20has%20not%20had%0A%20%20%20%20%20%20%20time%20to%20bite.%20By%20steady%20state%2C%20all%20three%20inputs%20contribute%2C%20and%0A%20%20%20%20%20%20%20the%20gap%20between%20%24S_T%24%20and%20%24S_1%24%20on%20%24T%24%20and%20%24%5Cmathrm%7BpH%7D%24%20is%20wide%0A%20%20%20%20%20%20%20enough%20to%20read%20off%20without%20help%20from%20the%20confidence%20intervals.%0A%20%20%20%202.%20**Temperature%20and%20pH%20interact%20strongly.**%20Both%20enter%20the%20outlet%0A%20%20%20%20%20%20%20concentration%20only%20through%20the%20product%20%24k(T%2C%20%5Cmathrm%7BpH%7D)%5Ctau%24%2C%0A%20%20%20%20%20%20%20so%20their%20joint%20variance%20is%20inseparable%3B%20the%20heatmap%20surfaces%20it%0A%20%20%20%20%20%20%20as%20the%20largest%20off-diagonal%20%24S_%7Bij%7D%24.%0A%20%20%20%203.%20**Bootstrap%20is%20one%20extra%20argument.**%20Passing%20%60num_resamples%3D200%60%0A%20%20%20%20%20%20%20and%20a%20PRNG%20key%20fills%20the%20%60_conf%60%20arrays%20at%20the%20same%20broadcast%0A%20%20%20%20%20%20%20%60(T%2C%20K%2C%20D)%60%20shape%20as%20the%20point%20estimates%2C%20so%20plotting%20code%20stays%0A%20%20%20%20%20%20%20the%20same%20with%20or%20without%20uncertainty%20bars.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
37027043ebcaeacd58a055a265cd9312