Export to a Tcl or openseespy script¶
Write a standalone, runnable OpenSees deck from the typed bridge instead of solving in-process. Reach for this when you want to version-control the model, hand it to a collaborator, or run it under a different OpenSees binary.
The recipe¶
Build the model through apeSees(fem) exactly as you would for an in-process
run, then call ops.tcl(path) and/or ops.py(path) instead of
ops.analyze(...):
from apeGmsh import apeGmsh
from apeGmsh.opensees import apeSees
L, E = 3.0, 200e9
b, h = 0.10, 0.20
A, Iz = b * h, b * h**3 / 12.0
P = 10_000.0
# --- 1. Geometry + named physical groups ---
with apeGmsh(model_name="cantilever") as g:
p0 = g.model.geometry.add_point(0.0, 0.0, 0.0)
p1 = g.model.geometry.add_point(L, 0.0, 0.0)
beam = g.model.geometry.add_line(p0, p1)
g.model.sync()
g.physical.add(1, [beam], name="Beam")
g.physical.add(0, [p0], name="Fixed")
g.physical.add(0, [p1], name="Tip")
g.mesh.sizing.set_global_size(L / 10.0)
g.mesh.generation.generate(1)
fem = g.mesh.queries.get_fem_data(dim=1)
# --- 2. Declare the model on the typed bridge ---
ops = apeSees(fem)
ops.model(ndm=2, ndf=3)
transf = ops.geomTransf.Linear(vecxz=(0.0, 0.0, 1.0))
ops.element.elasticBeamColumn(pg="Beam", transf=transf, A=A, E=E, Iz=Iz)
ops.fix(pg="Fixed", dofs=(1, 1, 1))
with ops.pattern.Plain(series=ops.timeSeries.Linear()) as pat:
pat.load(pg="Tip", forces=(0.0, -P, 0.0))
# --- 3. Emit instead of solving ---
ops.tcl("cantilever.tcl") # OpenSees Tcl deck
ops.py("cantilever.py") # equivalent openseespy script
Each call writes a complete, self-contained model definition: the model
builder, every node, the materials / sections / transforms, element
connectivity (with physical-group comments), fix commands, nodal masses,
load patterns, and any MP constraints (equalDOF, rigidLink,
rigidDiaphragm, ASDEmbeddedNodeElement). Run the result with
opensees cantilever.tcl or python cantilever.py.
Notes / gotchas¶
- The deck has no analysis chain.
ops.tcl/ops.pyemit the model — notconstraints/numberer/system/integrator/analysis/analyze. Append your solver recipe (or that of your collaborator) to the emitted file. To bake ananalyzeline into the deck, passanalyze_steps=(and optionallyanalyze_dt=). - This is the alternative to in-process capture. In a notebook you'd run
ops.run()/ops.analyze(...)and read results back throughResults. Exporting decouples declaring from running: the model leaves your Python session as a plain text file. - Emit calls are separate statements, not a fluent chain. Write each on
its own line —
ops.tcl(...)thenops.py(...). Each builds the model internally (an implicitops.build()), so order between them doesn't matter. run=Truesubprocesses the deck for you.ops.tcl("m.tcl", run=True)shells out to anopenseesbinary (override withbin=);ops.py(..., run=True)runs the script under Python. Withoutrun=, the call only writes the file.- Loads are opt-in.
g.loads.*do not auto-emit (ADR 0051): bring a session load case into the deck withp.from_model(case)inside a pattern, or author one directly withpat.load(...). The deck is authoritative — a case you don't import is simply not applied. - For a runnable native HDF5 (deck zone plus the broker neutral zone the
viewer /
Resultsread), useapeSees(fem).h5(path)— the session-sideg.save()/fem.to_h5()write the neutral zone only and are not runnable decks.
See also¶
- Concept: OpenSees bridge guide — §6 covers
ops.tcl/ops.py/ops.h5/ops.run, the build step, and the deck contents in depth. - Tutorial: Your first model — builds the same cantilever and solves it in-process (the path this recipe replaces).
- Related: Run a static analysis — the in-process counterpart.