paramclass-pyomo
paramclass-pyomo adapts Pyomo's imperative modeling API to declarative,
class-body model definitions.
It is built on top of paramclass and
lets you describe Pyomo components as class attributes, then materialize them
onto a pyomo.environ.Block or ConcreteModel.
import pyomo.environ as pyo
from paramclass_pyomo import AbstractBlock
class ToyBlock(AbstractBlock):
x = pyo.Var(initialize=3)
c = pyo.Constraint(rule=lambda m: m.x >= 1)
e = pyo.Expression(rule=lambda m: m.x + 1)
o = pyo.Objective(rule=lambda m: m.e)
model = pyo.ConcreteModel()
ToyBlock().build(model)
assert pyo.value(model.x) == 3
assert str(model.c.expr) == "1 <= x"
The goal is to make reusable Pyomo model pieces easier to package, override, compose, test, lint, and statically analyze.
Project Timeline
paramclass-pyomo was developed during 2023-2024 and has been used in
production workflows since 2023. Public packaging was added in 2025, and public
documentation was added in 2026 to make the project easier to evaluate, install,
and reuse outside its original environment.
Install
pip install paramclass-pyomo
For local development alongside paramclass:
uv sync --extra dev
uv run --extra dev pytest
The local UV configuration uses ../paramclass as an editable dependency.
Why
Pyomo is powerful, but reusable model structure often ends up split across factory functions, ad hoc builders, and imperative component assignment:
model = pyo.ConcreteModel()
model.capacity = pyo.Param(initialize=100, mutable=True)
model.used = pyo.Var(bounds=(0, None))
model.limit = pyo.Constraint(rule=lambda m: m.used <= m.capacity)
That style is natural for Pyomo, but it gives developer tooling less structure to inspect. Class-based definitions provide a stable surface for code review, linting, static analysis, generated documentation, and model composition.
AbstractBlock keeps Pyomo's component model, while giving each reusable block a
compact Python class definition:
class CapacityBlock(AbstractBlock):
capacity = pyo.Param(initialize=100, mutable=True)
used = pyo.Var(bounds=(0, None))
limit = pyo.Constraint(rule=lambda m: m.used <= m.capacity)
Then build it onto a model:
model = pyo.ConcreteModel()
CapacityBlock().build(model)
This keeps the imperative construction where Pyomo expects it, while moving the authoring experience toward a declarative design.
Examples
Standard Pyomo Keyword Style
Normal Pyomo keyword construction is supported. Use rule= for constraints,
expressions, and objectives, and initialize= for parameters and variables.
class ObjectiveBlock(AbstractBlock):
p = pyo.Param(initialize=7, mutable=True)
x = pyo.Var(initialize=lambda m: pyo.value(m.p) + 1)
objective = pyo.Objective(rule=lambda m: m.x)
Shorthand Rule Calls
paramclass-pyomo also supports a shorthand callable style for rules:
class ConstraintBlock(AbstractBlock):
x = pyo.Var(initialize=3)
c = pyo.Constraint()(lambda m: m.x >= 1)
This is optional. The standard Pyomo rule= form is usually clearer for public
examples and team code.
Nested Blocks
AbstractBlock instances can be assigned as attributes of other
AbstractBlocks. Nested blocks are finalized onto the parent block during
construction.
class Inner(AbstractBlock):
x = pyo.Var(initialize=1)
class Outer(AbstractBlock):
inner = Inner()
cap = pyo.Constraint(rule=lambda m: m.inner.x <= 10)
model = pyo.ConcreteModel()
Outer().build(model)
Indexed Blocks
Pass indexing arguments to the block constructor when defining nested blocks:
class Unit(AbstractBlock):
x = pyo.Var(initialize=1)
class System(AbstractBlock):
units = Unit(["a", "b", "c"])
When a block is indexed, component construction is applied across each block data object.
How It Works
AbstractBlock extends ParamClass and redirects public attribute assignment to
the underlying Pyomo block. During build(model), deferred class-body
definitions are evaluated and attached to the target model or block.
This lets a Pyomo block definition look declarative to developers and tooling, while still executing through Pyomo's normal component lifecycle.
Pyomo components that already belong to another block are wrapped with a small proxy so they can still be referenced safely from generated components.
Current Limitations
This project intentionally stays close to Pyomo's component model. If a Pyomo
constructor requires keyword arguments such as rule= or initialize=, prefer
using those explicit keywords in public code.
As with paramclass, Python boolean operators such as and, or, and not
are not deferred by the tracing layer.
Testing
uv run --extra dev pytest
Release files for paramclass-pyomo 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| paramclass_pyomo-1.1.1.tar.gz | 8.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| paramclass_pyomo-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 15.6 kB
Release files / paramclass_pyomo-1.1.1.tar.gz
| Download URL | paramclass_pyomo-1.1.1.tar.gz |
|---|---|
| Size | 8.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fac82f7bb02f414686ae5ed6887d59b461048e950152683fe01e9294e393de9c
|
|
BLAKE2b-256 checksum How to use checksums |
6d3a8427ed887ac6d78348468bba7692e33dcbd463a2df4a831267ca90a442c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / paramclass_pyomo-1.1.1-py3-none-any.whl
| Download URL | paramclass_pyomo-1.1.1-py3-none-any.whl |
|---|---|
| Size | 7.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1162c6c058fa862aabc785ea13527e8b486b89b510ae7e82dcd043c3ff644d49
|
|
BLAKE2b-256 checksum How to use checksums |
ae29a655df684d8a779764aa76eff51bf5259670f718b69d38c841e4c963fc2f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|