OGR Slip2D is a Python program, and its calculations can be driven from a script. There are two levels: the engine, whose objects (Project, the searches and the methods) give full control and leave the checking to you, and the operations layer ogr_api, the named, validated and undoable operations that the MCP server publishes as tools. The program's window also has a Python terminal connected to the live project. A short example of each follows, all checked against v0.1.235.
5.3.1Two levels#
| Engine | Operations layer | |
|---|---|---|
| Packages | ogr_core (model, settings, files), ogr_slip2d (methods, searches), ogr_fem2d (seepage) | ogr_api |
| You hold | Python objects: a Project, a search, its result | handles: project_id, job_id, result_id |
| Validation | what each object checks; a misspelt setting is accepted in silence | every argument and setting checked, with the closest valid names |
| Undo | no | every edit is one undo step |
| Analyses | run in your process | background jobs in a child process that can be cancelled |
| Answers | full result objects (every surface, every slice) | JSON-safe summaries; full results on request |
| Use it for | research, parametric studies, new post-processing | scripts that should behave as the program does; what an agent does |
Both need the package installed (Section 1.1); neither needs the window. The repository's examples/quick_slope.py uses the engine and examples/api_acads.py the operations layer; from the root of the repository they run as python -m examples.quick_slope and python -m examples.api_acads.
5.3.2The engine#
Projects#
A model is a Project (ogr_core.project). Project.load(path) reads a .ogr file and project.save(path) writes one (Section 5.4); Project(name) starts an empty one, to which boundaries and materials are added with add_boundary and add_material, as examples/quick_slope.py does. ogr_core.project.demo. returns the demo slope.
The analysis the project describes#
run_analysis(project, method_ids=None, progress_cb=None, *, allow_unconfigured=False), in ogr_slip2d.analysis_runner, is the analysis that Analysis → Compute, the command line and the operations layer run: one search per method, built from the project's settings. It applies the design-standard partial factors to a copy (the project passed in is not modified) and takes into account rapid drawdown, slope limits, surface filters, focus objects, the seismic mode, the convergence settings, the seed and the admissibility checks.
from ogr_core.project import Project
from ogr_slip2d.analysis_runner import run_analysis
def main():
project = Project.load("slope.ogr")
outcome = run_analysis(project) # the methods enabled in the project
for note in outcome.warnings:
print("Note:", note)
for method_id, result in outcome.results.items():
critical = result.critical
if critical is None:
print(f"{method_id}: no valid surface")
else:
print(f"{method_id}: {critical.fos:.4f} "
f"({result.valid_count} valid of {result.attempts})")
if __name__ == "__main__":
main()- It returns an
AnalysisOutcomewithresults(method id → search result),factor_report(the design-standard report),warnings(the notes the window shows) andproject(the factored copy that was analysed).results, report = run_analysis(project)also works. - If the project cannot be analysed as configured, it raises
AnalysisNotConfigured, aValueErrorwhoseproblemslists every reason;allow_unconfigured=Trueruns anyway, for a caller that has read them. - With a design standard active,
critical.fosis an over-design factor, which must exceed 1, not a factor of safety. progress_cb(done, total)is called across the whole run, all methods included.
A search built by hand#
The searches (GridSearch, SlopeSearch, AutoRefineSearch, BlockSearch, PathSearch, SimulatedAnnealingSearch; the particle swarm is in ogr_slip2d.particle_swarm) and the nine methods (BishopSimplified, JanbuSimplified, JanbuCorrected, OrdinaryFellenius, Spencer, GLEMorgensternPrice, LoweKarafiath, CorpsOfEngineers1, CorpsOfEngineers2) can be built directly; method_registry() maps each method id to its class.
from ogr_core.project import Project, save_results
from ogr_slip2d import BishopSimplified, GridSearch
project = Project.load("slope.ogr")
search = GridSearch(method=BishopSimplified(), grid_nx=6, grid_ny=6,
radius_increment=5, num_slices=25)
result = search.run(project) # 7 × 7 centres × 6 radii = 294 circles
print(result.critical.fos, result.attempts)
save_results("slope_bishop.h5", result, project_id=project.id)An object built by hand uses its own defaults and reads from the project only the model itself. A method takes a tolerance of 0.001, 75 iterations and no Steffensen acceleration (the project defaults are 0.005, 50 and Steffensen on); GridSearch takes 10 × 10 intervals, a radius increment of 10, 30 slices and a minimum area of 0.5 m², and places the grid itself when grid_x and grid_y are not given. The project's slope limits, surface filters, focus objects and seismic Ky mode reach a hand-built search only if they are passed to it, and rapid drawdown and the design standard are applied only by the analysis runner. To get what the window computes, use run_analysis. build_search(project, method_id) returns the search the project configures for one method, rapid drawdown included; the design standard is applied by run_analysis, to a copy of the project.
SearchResult) and what each evaluated surface carries (LEMResult).| Attribute | Meaning |
|---|---|
result.critical | The admissible surface with the lowest factor of safety (the lowest critical seismic coefficient in a Ky run), or None when no surface is valid. If every valid surface failed the admissibility checks, the checks are ignored rather than returning nothing, and inadmissible_count says so. |
result.evaluations | Every surface that was sliced and computed, valid or not, in the order generated. |
valid_count, invalid_count | Surfaces with and without a factor of safety; their sum is total_count. |
attempts, focus_rejected | Candidates formed (a Grid Search attempts (nx + 1)(ny + 1)(radius increment + 1)) and candidates removed by focus objects. |
top_n(n), min_fos | The n best valid surfaces; the factor of the critical surface. |
minima, optimized, objective | Distinct local minima (multimodal particle swarm); the surface produced by Optimize Surfaces when it improved on the search; "fos" or "ky". |
fos | Factor of safety of one surface, or None when there is none (never NaN). |
converged, iterations, method_id | Convergence of the iteration, iterations used and method. |
surface, slices | The slip surface (surface.to_dict() gives its geometry) and its slices. |
is_valid | Converged, with a positive factor and no error. |
admissible, admissibility_note, admissibility_reason | Verdict of the post-analysis checks (tensile stress, m-alpha). |
error_message, reason | Why there is no factor, in words and as a code (Section 5.5.2). |
details | Extras such as lambda, ky or newmark_displacement. |
One surface#
evaluate_surfaces(project, surface, method_ids=None) computes the factor of safety of one given surface with each method, through the same checks, factored copy and configured search as run_analysis. Its results map each method id to a result, or to None when the surface does not cut the model.
from ogr_core.project import Project
from ogr_slip2d import SlipCircle
from ogr_slip2d.analysis_runner import evaluate_surfaces
project = Project.load("slope.ogr")
circle = SlipCircle(centre_x=37.5, centre_y=31.7, radius=16.5)
outcome = evaluate_surfaces(project, circle, ["bishop_simplified", "spencer"])
for method_id, res in outcome.results.items():
print(method_id, None if res is None else res.fos)Results files#
save_results(path, search_result, project_id="") writes one search result to HDF5 and load_summary(path) reads back its metadata and summary arrays, without the slices; both are in ogr_core.project. The layout is in Section 5.4.5.
Scripts and worker processes#
A Grid Search of 400 circles or more is evaluated in a pool of worker processes (at most 8, using by default 50 % of the logical processors; Project Settings → Advanced can switch it off). On Windows each worker imports the script again, so a script that runs a search should keep its top-level code under if __name__ == "__main__":, as the examples on this page and in the repository do. A pool that cannot start does not stop the analysis: the search falls back to one process.
5.3.3The operations layer#
ogr_api holds what the program can do to a model as named operations with typed arguments and JSON-safe answers, without Qt and without the MCP SDK. There are 73 operations: the MCP tools except server_info, under the same names, and every parameter of a tool is a parameter of its operation, so the tool tables of Section 5.1.7 describe them too. ogr_api.OPERATIONS is the registry.
from pathlib import Path
from ogr_api import OgrApiError, Workspace, call
def main():
ws = Workspace(workdir=Path.cwd())
try:
pid = call(ws, "project_open", path="slope.ogr")["project_id"]
call(ws, "analysis_configure", project_id=pid,
methods=["bishop_simplified", "spencer"],
grid={"nx": 6, "ny": 6}, radius_increment=5)
run = call(ws, "analysis_run", project_id=pid, wait_seconds=300)
if run["state"] != "done":
print("still running:", run["job_id"])
return
for m in run["summary"]["methods"]:
print(m["method_id"], m["caption"], m["value"], m["counts"]["valid"])
try:
call(ws, "settings_set", project_id=pid,
changes={"search.search_methd": "grid"})
except OgrApiError as exc:
print(exc.code, exc.hint) # E_INVALID_ARGUMENT Did you mean 'search_method'?
finally:
ws.shutdown()
if __name__ == "__main__":
main()Workspace(workdir=None, jobs_root=None, max_concurrent_jobs=1, max_results_per_project=5)holds the open models, their undo history, their results and the job manager.workdiranchors relative paths; without it a relative path is an error. It keeps the last five results of each model.call(ws, "operation", **kwargs)runs one operation. The model is named byproject_id, which may be omitted while only one model is open.- Edits are atomic and each is one undo step (
call(ws, "project_history", action="undo")). - Analyses (
analysis_run,groundwater_run,statistics_run,back_analysis_run,optimize_run,drawdown_sweep_run) run as jobs in a child process, on a detached copy of the model. A call waits up towait_secondsand otherwise answersstate: "running"with ajob_idforjob_get;job_cancelkills the job's process tree. The answer of a finished run carriesresult_id,summaryandstale(whether the model has changed since).ws.shutdown()kills the jobs still running and removes their files. - Errors are
OgrApiErrorsubclasses with a stablecode, amessage, ahintanddetails(Section 5.5.4). An argument name that the operation does not take is not anOgrApiError: Python raisesTypeError. python_execruns a script withproject,api(every operation asapi.<name>(...), withproject_idfilled in),np,ogr_core,ogr_slip2dandresult(result_id)(a stored result in full) as one undo step; it returnsok,stdout,stderr,value(the representation of the last expression),errorandrolled_back.
The operations layer exists because the engine's settings objects accept a misspelt setting in silence and an unknown search id becomes a Grid Search; here both are refused, with a suggestion. It never imports Qt or the MCP SDK, which a test checks, so the same registry serves the MCP server, the window's agent bridge and a plain script.
Source: ogr_api/__init__.py · ogr_api/workspace.py · examples/api_acads.py · examples/quick_slope.py5.3.4The embedded terminal#
Window → Terminal (Ctrl+`) opens a Python console inside the program, connected to the live project. It runs in the program's own process and acts directly on the model on screen.
| Name | What it is |
|---|---|
project | The current Project; it is re-attached when a project is created or opened. |
canvas | The drawing canvas of the main window. |
mainwindow | The main window. |
np | NumPy. |
ogr_core, ogr_slip2d | The model and solver packages. |
help_ogr() | Prints a short list of tips. |
>>> print(len(project.materials), project.bounding_box())
>>> from ogr_slip2d.analysis_runner import run_analysis
>>> outcome = run_analysis(project, ["spencer"])
>>> print(outcome.results["spencer"].critical.fos)- Enter runs the input, Shift+Enter starts a new line, Ctrl+↑ and Ctrl+↓ walk the history and Ctrl+L clears the console.
- The value of a bare expression is not echoed: use
print(), as above. - The console runs on the window's own thread, so a long calculation started there holds the window until it ends.
- Changes made to
projectfrom the console act on the model directly and are not steps of Edit → Undo. For undoable edits from a script, use the operations layer or an agent'spython_exec.
The console executes each input as a block of statements, which is why expressions are not echoed. Its tips (help_ogr()) still show bare expressions such as r.critical.fos, which print nothing as written.