from opyrability import dynamic_operability
import numpy as np
AIS = [[-1, 1], [-1, 1]]
DOS = [[-2, 2], [-2, 2]]
model = {'A': 0.9*np.eye(2),
'B': np.eye(2),
'C': np.eye(2)}
results = dynamic_operability(model, # LTI dict or step fn
x0=np.zeros(2), # initial state
AIS_bound=AIS, # input set
DOS=DOS, # target outputs
k_max=4) # horizon
print(results['dOI']) # [25. 90.25 100. 100.]

Upgrade methane to benzene and hydrogen; the membrane pulls out to push conversion past equilibrium.
Transient mole balances on the molar holdups of each species (tube and shell) along the reactor length. Two equilibrium-limited reactions, with membrane removal shifting them forward (Le Chatelier):
Discretized into elements, 4 tube + 4 shell species: a 160-state model. Opyrability needs only a one-minute step, dma_mr_cocurrent_step(x, u, d) returning (x_next, y).

Identified first-order model:
ZOH-discretized ( min) to a four-state .

Same funnel, three ways to compute it:
dynamic_operability picks one by state dimension and model type, or you force it with method=.
Build one funnel per distrubance scenario, then take their intersection: the outputs guaranteed reachable no matter which disturbance hits the process.
scenario_results = dynamic_operability_scenarios(
make_step, # step(x, u) for a feed d
make_initial_state, # steady state for feed d
AIS_bounds,
scenarios={'Low quality well (92% CH4)': 92.0,
'High quality well (96% CH4)': 96.0},
DOS=DOS_bounds, k_max=6, engine='plotly')
Empty for two minutes, then the robust region grows: adjusting inlet flowrates compensates for feed quality.
Before, running the inverse mapping meant installing IPOPT through conda first.
Now, one command:
pip install opyrability
That is it. It includes everything you need, the solver included. cyipopt stays optional, only for method='ipopt'.
Pounce is a pure-Rust interior-point NLP solver from John Kitchin's group at CMU, now the default for Opyrability's inverse mapping and design tools. Why is this important?
pip install gives a fast, self-contained solver on Windows, macOS, and LinuxPounce is the default, so nothing changes in your call:
fDIS, fDOS, msg = nlp_based_approach(model, # process model
DOS, # target outputs
resolution, # grid density
u0, # initial guess
lb, ub) # input bounds
# method='pounce' by default; 'ipopt' still available
Score two or more designs by their Operability Index, from the output or the input side, and see which is more operable before committing.
from opyrability import rank_designs
ranking = rank_designs(models={'Tall': model_a, # {label: model}
'Compact': model_b},
AIS_bound=AIS, # input set(s)
DOS_bound=DOS, # target outputs
resolution=[10, 10], # grid density
perspective='outputs') # or 'inputs'
fDIS, fDOS, msg = nlp_based_approach(
model, # process model
DOS, # target outputs
resolution, # grid density
u0, lb, ub, # guess, bounds
problem='P2', # 'P1' / 'P2' / 'P3'
PI_target=objective) # metric to intensify
The multimodel polytopes turn the search for an optimal modular design into a single mixed-integer linear program, solved once per layer instead of many nonlinear solves.
u_opt, y_opt, phi, *_ = milp_based_approach(
model, # process model
AIS_bound=AIS, # input bounds
PI_target=cost, # metric to minimize
DOS_bounds=DOS, # target outputs
AIS_resolution=5) # grid density
Opyrability maps whatever you can evaluate:
The plots show operability analyses from several modeling environments, all through the same API.




Fit a Gaussian process surrogate, then propagate its uncertainty through the dynamic analysis.
For a CSTR under a GP-NARX surrogate:
Opyrability ships with OPYRABILITY.md: one file that teaches an AI coding assistant the vocabulary, function signatures and best practices, the expected model (M) syntax, when to use each function, and the mistakes to avoid.
→
</>
→
→
→
OPYRABILITY.md, writes the model, and runs the operability analysis, from inverse maps to intensified designs.Read the documentation and the operability fundamentals.
# every API page runs code like this
from opyrability import (
multimodel_rep, OI_eval)
AOS = multimodel_rep(model, AIS,
resolution=[10, 10])
OI = OI_eval(AOS, DOS) # -> 60.2 %
147
Run in continuous integration on Python 3.11, 3.12, 3.13, and 3.14, and on every pull request. They cover:
Opyrability is open source and welcomes contributions. It will always be that way.
method='ipopt' and install cyipopt from condaOPYRABILITY.md agent skill



pip install opyrability
Development began in the CODES Group at West Virginia University, with San Dinh and under Fernando V. Lima, and continues at Carnegie Mellon University. Pounce is the work of John Kitchin and his group.