CLI Workflow: Catalyst Screening Study#

This walkthrough demonstrates the complete jaxsr command-line workflow — from study creation through adaptive experiments to final reporting — without writing any Python code.

Scenario#

You’re screening three factors for a heterogeneous catalysis reaction:

Factor

Range

Type

Temperature

300–500 K

Continuous

Pressure

1–10 bar

Continuous

Catalyst

Pt, Pd, Rh

Categorical

The response is conversion (%).

Step 1: Create the Study#

!jaxsr init catalyst_screening \
    -f "temperature:300:500" \
    -f "pressure:1:10" \
    -f "catalyst:Pt,Pd,Rh" \
    -d "Screen catalyst type, temperature, and pressure for max conversion"
Created study 'catalyst_screening' with 3 factors.
Saved to: catalyst_screening.jaxsr

The .jaxsr file is a portable ZIP archive containing the study metadata.

Step 2: Generate an Experimental Design#

Create a 20-point Latin Hypercube design and export to an Excel template for lab use:

!jaxsr design catalyst_screening.jaxsr \
    -m latin_hypercube \
    -n 20 \
    -s 42 \
    --format xlsx \
    -o lab_template.xlsx
WARNING:2026-02-25 10:29:08,374:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Generated 20 design points using latin_hypercube.
Excel template written to: lab_template.xlsx

You can also preview the design as a table:

!jaxsr design catalyst_screening.jaxsr -n 20 -s 42
WARNING:2026-02-25 10:29:09,446:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Generated 20 design points using latin_hypercube.
  Run    temperature    pressure    catalyst
--------------------------------------------
    1        482.260       3.053          Rh
    2        303.026       1.408          Pd
    3        452.389       6.046          Pt
    4        395.496       3.983          Pd
    5        353.561       5.580          Pt
    6        317.728       9.300          Pd
    7        341.724       4.766          Rh
    8        496.455       1.913          Pt
    9        422.216       5.412          Rh
   10        389.562       4.531          Pd
   11        462.552       7.315          Pd
   12        326.295       1.689          Pt
   13        408.701       9.786          Pt
   14        333.302       8.903          Rh
   15        412.997       8.059          Pd
   16        431.952       7.126          Rh
   17        473.175       6.787          Pd
   18        379.926       2.446          Pd
   19        442.948       3.349          Pd
   20        364.313       8.587          Pd

Or export to CSV for scripting:

!jaxsr design catalyst_screening.jaxsr -n 20 --format csv -o design.csv
WARNING:2026-02-25 10:29:10,493:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Generated 20 design points using latin_hypercube.
Written to: design.csv

Simulating Lab Responses#

In a real workflow you would run actual experiments and fill in the Response column in the Excel template. Here we simulate synthetic conversion data so the notebook runs end-to-end.

# PREREQUISITES: This cell requires from earlier cells:
#   - simulate_response(): function to simulate lab measurements (cell 11)
#   - cat_map: dict mapping catalyst names to numeric codes (cell 11)
# For standalone execution, you would import these from a shared module.

# NOTE: This simulation function is used throughout the notebook
# to generate synthetic lab responses (cells 25, 26, 34).
# In practice, these would be actual experimental measurements.

import numpy as np
from openpyxl import load_workbook

rng = np.random.default_rng(42)

def simulate_response(T, P, catalyst_idx):
    """Synthetic conversion (%) for catalyst screening."""
    base = 50 + 0.15 * (T - 300) + 2.5 * P - 0.0003 * (T - 400) ** 2
    cat_effect = [0, 5, -3][catalyst_idx]
    return base + cat_effect + rng.normal(0, 2)

cat_map = {"Pt": 0, "Pd": 1, "Rh": 2}

# Fill in lab_template.xlsx with synthetic responses
wb = load_workbook("lab_template.xlsx")
ws = wb["Design"]
for row in range(2, ws.max_row + 1):
    T = float(ws.cell(row=row, column=2).value)
    P = float(ws.cell(row=row, column=3).value)
    cat = ws.cell(row=row, column=4).value
    y = simulate_response(T, P, cat_map[cat])
    ws.cell(row=row, column=5, value=round(y, 2))
wb.save("lab_template.xlsx")
print(f"Filled {ws.max_row - 1} response values in lab_template.xlsx")

Step 3: Run Experiments in the Lab#

  1. Open lab_template.xlsx

  2. For each row, run the experiment at the specified conditions

  3. Fill in the Response column with the measured conversion (%)

  4. Save the file

The cell above simulated this step. In practice you would fill in real measurements.

Step 4: Import Results#

!jaxsr add catalyst_screening.jaxsr lab_template.xlsx \
    --notes "Batch 1: initial screening, 2024-01-15"
Added 20 observations. Total: 20
20 design points still pending.

If you have a CSV instead:

jaxsr add catalyst_screening.jaxsr results.csv --notes "From CSV"

CSV format: columns must match factor names, with the last column as the response:

temperature,pressure,catalyst,Response
347.5,3.25,Pd,62.1
421.25,7.75,Pt,78.3

Step 5: Fit a Model#

!jaxsr fit catalyst_screening.jaxsr \
    --max-terms 5 \
    --strategy greedy_forward \
    --criterion bic
WARNING:2026-02-25 10:29:12,739:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
/Users/jkitchin/Dropbox/projects/jaxsr/src/jaxsr/study.py:572: UserWarning: Removing 1 basis functions with non-finite values
  model.fit(self._X_observed, self._y_observed)
Model: y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2
  MSE:   2.52649
  AIC:   85.2941
  BIC:   90.2728
  Terms: 5

Choosing --criterion:#

Data Size

Recommendation

< 40 observations

--criterion aicc (corrected for small samples)

40–200 observations

--criterion bic (sparser models)

> 200 observations

--criterion aic or --criterion bic

Choosing --strategy:#

Library Size

Recommendation

< 20 basis functions

--strategy exhaustive (globally optimal)

20–200

--strategy greedy_forward (default, fast)

200+

--strategy lasso_path (regularized screening)

Step 6: Check Study Status#

!jaxsr status catalyst_screening.jaxsr
WARNING:2026-02-25 10:29:16,311:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
============================================================
DOE Study: catalyst_screening
============================================================
Description: Screen catalyst type, temperature, and pressure for max conversion
Factors: temperature, pressure, catalyst
Bounds: [(300.0, 500.0), (1.0, 10.0), (0, 2)]
Feature types: ['continuous', 'continuous', 'categorical']
Categories: {2: ['Pt', 'Pd', 'Rh']}

Design: 20 points (0 completed, 20 pending)
Design method: latin_hypercube
Observations: 20

Model: y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2
  MSE: 2.52649
  AIC: 85.2941
  Terms: 5

Iterations: 1
  Round 1: +20 points → y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2 (Batch 1: initial screening, 2024-01-15)

Created: 2026-02-25T15:29:07.312830+00:00
Modified: 2026-02-25T15:29:15.190420+00:00
============================================================

Step 7: Suggest Next Experiments#

The model identifies where to measure next for maximum information gain:

!jaxsr suggest catalyst_screening.jaxsr \
    -n 5 \
    --strategy uncertainty
WARNING:2026-02-25 10:29:17,408:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Suggested 5 next experiments:
  Run    temperature    pressure    catalyst
--------------------------------------------
    1        333.326       9.936          Pd
    2        316.223       9.819          Rh
    3        313.755       9.800          Pt
    4        314.454       9.841          Pd
    5        308.075       9.809          Pd

Suggestion strategies:#

Strategy

When to use

space_filling

No model yet, or want uniform coverage

uncertainty

Reduce prediction uncertainty everywhere

error

Fix regions where the model fits poorly

leverage

Stabilize coefficient estimates

Export as CSV for automation:

jaxsr suggest catalyst_screening.jaxsr -n 5 --format csv > next_batch.csv

Step 8: Add More Data and Refit#

After running the suggested experiments:

# PREREQUISITES: This cell requires from earlier cells:
#   - simulate_response(): function to simulate lab measurements (cell 11)
#   - cat_map: dict mapping catalyst names to numeric codes (cell 11)
# For standalone execution, you would import these from a shared module.

import csv, subprocess, io

# Get suggestions as CSV
result = subprocess.run(
    ["jaxsr", "suggest", "catalyst_screening.jaxsr", "-n", "5",
     "--strategy", "uncertainty", "--format", "csv"],
    capture_output=True, text=True
)
# Skip the "Suggested N next experiments:" header line
csv_lines = result.stdout.strip().split("\n")
csv_text = "\n".join(line for line in csv_lines if line.startswith("temperature") or "," in line and not line.startswith("Suggested"))
print("Suggested points (CSV):")
print(csv_text)

# Parse suggestions and simulate responses
try:
    reader = csv.DictReader(io.StringIO(csv_text))
    rows = list(reader)
    if not rows or "temperature" not in rows[0]:
        raise ValueError("Invalid CSV format from jaxsr suggest")
except (ValueError, KeyError) as e:
    print(f"Error parsing suggestions: {e}")
    print("CLI output format may have changed. Please check 'jaxsr suggest --help'")
    raise

with open("batch2.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["temperature", "pressure", "catalyst", "Response"])
    for row in rows:
        T = float(row["temperature"])
        P = float(row["pressure"])
        cat = row["catalyst"]
        y = simulate_response(T, P, cat_map[cat])
        writer.writerow([T, P, cat, round(y, 2)])
print(f"\nWrote {len(rows)} simulated responses to batch2.csv")
!jaxsr add catalyst_screening.jaxsr batch2.csv --notes "Batch 2: uncertainty-guided"
!jaxsr fit catalyst_screening.jaxsr --max-terms 5 --criterion bic
!jaxsr status catalyst_screening.jaxsr
WARNING:2026-02-25 10:29:23,092:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Added 5 observations. Total: 25
20 design points still pending.
WARNING:2026-02-25 10:29:24,206:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
/Users/jkitchin/Dropbox/projects/jaxsr/src/jaxsr/study.py:572: UserWarning: Removing 1 basis functions with non-finite values
  model.fit(self._X_observed, self._y_observed)
Model: y = 2.42*sqrt(temperature) + 0.005878*temperature*pressure + 0.01705*I(catalyst=Pd)*temperature + 8.6992e-05*temperature^2 + 0.0003076*exp(pressure)
  MSE:   4.61599
  AIC:   119.1851
  BIC:   125.2794
  Terms: 5
WARNING:2026-02-25 10:29:27,810:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
============================================================
DOE Study: catalyst_screening
============================================================
Description: Screen catalyst type, temperature, and pressure for max conversion
Factors: temperature, pressure, catalyst
Bounds: [(300.0, 500.0), (1.0, 10.0), (0, 2)]
Feature types: ['continuous', 'continuous', 'categorical']
Categories: {2: ['Pt', 'Pd', 'Rh']}

Design: 20 points (0 completed, 20 pending)
Design method: latin_hypercube
Observations: 25

Model: y = 2.42*sqrt(temperature) + 0.005878*temperature*pressure + 0.01705*I(catalyst=Pd)*temperature + 8.6992e-05*temperature^2 + 0.0003076*exp(pressure)
  MSE: 4.61599
  AIC: 119.1851
  Terms: 5

Iterations: 2
  Round 1: +20 points → y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2 (Batch 1: initial screening, 2024-01-15)
  Round 2: +5 points → y = 2.42*sqrt(temperature) + 0.005878*temperature*pressure + 0.01705*I(catalyst=Pd)*temperature + 8.6992e-05*temperature^2 + 0.0003076*exp(pressure) (Batch 2: uncertainty-guided)

Created: 2026-02-25T15:29:07.312830+00:00
Modified: 2026-02-25T15:29:26.675524+00:00
============================================================

Repeat Steps 7–8 until the model is satisfactory.

When to stop:#

  • R² > 0.95 and model is physically sensible

  • Adding data doesn’t change the model expression

  • Prediction intervals are narrow enough for your application

  • Budget is exhausted

Step 9: Generate Reports#

Excel Report#

!jaxsr report catalyst_screening.jaxsr -o report.xlsx
WARNING:2026-02-25 10:29:28,923:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Excel report written to: report.xlsx

The Excel workbook includes:

  • Study summary sheet

  • Design matrix with responses

  • Model coefficients and metrics

  • Pareto front (complexity vs. accuracy)

Word Report#

!jaxsr report catalyst_screening.jaxsr -o report.docx
WARNING:2026-02-25 10:29:30,106:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Word report written to: report.docx

The Word document includes:

  • Formatted model equation

  • Coefficient table with standard errors

  • Diagnostic discussion

  • Embedded figures

Complete Session#

Here’s the entire workflow as a single script:

# Catalyst screening study — complete CLI workflow
import numpy as np, csv, io, subprocess, os
from openpyxl import load_workbook

# NOTE: Fresh RNG (rng2) for standalone execution of this complete workflow.
# Using separate state ensures results are reproducible when running this cell
# independently, without depending on earlier cells' RNG state progression.
rng2 = np.random.default_rng(42)

def sim_response(T, P, catalyst_idx):
    base = 50 + 0.15 * (T - 300) + 2.5 * P - 0.0003 * (T - 400) ** 2
    cat_effect = [0, 5, -3][catalyst_idx]
    return base + cat_effect + rng2.normal(0, 2)

cmap = {"Pt": 0, "Pd": 1, "Rh": 2}

# 1. Setup
!jaxsr init catalyst_screening -f "temperature:300:500" -f "pressure:1:10" -f "catalyst:Pt,Pd,Rh" -d "Catalyst screening"

# 2. Design → Excel template
!jaxsr design catalyst_screening.jaxsr -n 20 -s 42 --format xlsx -o template.xlsx

# 3. Simulate lab responses
wb = load_workbook("template.xlsx")
ws = wb["Design"]
for row in range(2, ws.max_row + 1):
    T = float(ws.cell(row=row, column=2).value)
    P = float(ws.cell(row=row, column=3).value)
    cat = ws.cell(row=row, column=4).value
    ws.cell(row=row, column=5, value=round(sim_response(T, P, cmap[cat]), 2))
wb.save("template.xlsx")
print("Filled template with simulated responses")

# 4. Import results
!jaxsr add catalyst_screening.jaxsr template.xlsx --notes "Initial batch"

# 5. Fit
!jaxsr fit catalyst_screening.jaxsr --max-terms 5 --criterion bic
!jaxsr status catalyst_screening.jaxsr

# 6. Adaptive round — get suggestions and simulate
result = subprocess.run(
    ["jaxsr", "suggest", "catalyst_screening.jaxsr", "-n", "5",
     "--strategy", "uncertainty", "--format", "csv"],
    capture_output=True, text=True
)
csv_lines = result.stdout.strip().split("\n")
csv_text = "\n".join(line for line in csv_lines if "," in line and not line.startswith("Suggested"))
reader = csv.DictReader(io.StringIO(csv_text))
with open("batch2.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["temperature", "pressure", "catalyst", "Response"])
    for r in reader:
        T, P, cat = float(r["temperature"]), float(r["pressure"]), r["catalyst"]
        writer.writerow([T, P, cat, round(sim_response(T, P, cmap[cat]), 2)])

# 7. Import batch 2 and refit
!jaxsr add catalyst_screening.jaxsr batch2.csv --notes "Adaptive batch"
!jaxsr fit catalyst_screening.jaxsr --max-terms 5 --criterion bic

# 8. Reports
!jaxsr report catalyst_screening.jaxsr -o final_report.xlsx
!jaxsr report catalyst_screening.jaxsr -o final_report.docx
Created study 'catalyst_screening' with 3 factors.
Saved to: catalyst_screening.jaxsr
WARNING:2026-02-25 10:29:32,908:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Generated 20 design points using latin_hypercube.
Excel template written to: template.xlsx
Filled template with simulated responses
Added 20 observations. Total: 20
All design points completed!
WARNING:2026-02-25 10:29:35,090:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
/Users/jkitchin/Dropbox/projects/jaxsr/src/jaxsr/study.py:572: UserWarning: Removing 1 basis functions with non-finite values
  model.fit(self._X_observed, self._y_observed)
Model: y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2
  MSE:   2.52649
  AIC:   85.2941
  BIC:   90.2728
  Terms: 5
WARNING:2026-02-25 10:29:38,795:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
============================================================
DOE Study: catalyst_screening
============================================================
Description: Catalyst screening
Factors: temperature, pressure, catalyst
Bounds: [(300.0, 500.0), (1.0, 10.0), (0, 2)]
Feature types: ['continuous', 'continuous', 'categorical']
Categories: {2: ['Pt', 'Pd', 'Rh']}

Design: 20 points (20 completed, 0 pending)
Design method: latin_hypercube
Observations: 20

Model: y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2
  MSE: 2.52649
  AIC: 85.2941
  Terms: 5

Iterations: 1
  Round 1: +20 points → y = 4.073*sqrt(temperature) + 0.003166*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature - 6442*1/temperature + 0.1201*pressure^2 (Initial batch)

Created: 2026-02-25T15:29:31.865401+00:00
Modified: 2026-02-25T15:29:37.593133+00:00
============================================================
WARNING:2026-02-25 10:29:42,418:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Added 5 observations. Total: 25
All design points completed!
WARNING:2026-02-25 10:29:43,514:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
/Users/jkitchin/Dropbox/projects/jaxsr/src/jaxsr/study.py:572: UserWarning: Removing 1 basis functions with non-finite values
  model.fit(self._X_observed, self._y_observed)
Model: y = 2.53*sqrt(temperature) + 0.001786*temperature*pressure + 0.01653*I(catalyst=Pd)*temperature + 1.5746e-07*temperature^3 + 2.045*pressure
  MSE:   4.86897
  AIC:   120.5190
  BIC:   126.6133
  Terms: 5
WARNING:2026-02-25 10:29:47,160:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Excel report written to: final_report.xlsx
WARNING:2026-02-25 10:29:48,341:jax._src.xla_bridge:905: Platform 'mps' is experimental and not all JAX functionality may be correctly supported!
Word report written to: final_report.docx

Tips#

  1. Always use --notes when adding data. It creates an audit trail inside the .jaxsr file.

  2. The .jaxsr file is self-contained. Share it with collaborators — they can run jaxsr status, jaxsr fit, or jaxsr report on their own machine.

  3. Seed the design with -s 42 (or any integer) for reproducibility.

  4. Start with fewer points. 15–20 points is enough for a first pass. Active learning (Step 7) tells you exactly where to measure next.

  5. Don’t over-specify --max-terms. Start with 5 and increase only if the model R² is poor. More terms = harder to interpret.