OpenGeoRock
5.3

API de Python

Válido para OGR Slip2D 0.1.235

OGR Slip2D es un programa escrito en Python y sus cálculos pueden dirigirse desde un script. Hay dos niveles: el motor, cuyos objetos (Project, las búsquedas y los métodos) dan control total y dejan las comprobaciones de tu cuenta, y la capa de operaciones ogr_api, formada por operaciones con nombre, validadas y reversibles que el servidor MCP publica como herramientas. Además, la ventana del programa incluye una consola de Python conectada al proyecto abierto. A continuación se da un breve ejemplo de cada uno, todos comprobados con la v0.1.235.

5.3.1Dos niveles#

Tabla 5.3.1 Los dos niveles de Python de OGR Slip2D.
MotorCapa de operaciones
Paquetesogr_core (modelo, ajustes, archivos), ogr_slip2d (métodos, búsquedas), ogr_fem2d (filtración)ogr_api
Trabajas conobjetos de Python: un Project, una búsqueda, su resultadoidentificadores: project_id, job_id, result_id
Validaciónla que hace cada objeto; un ajuste mal escrito se acepta sin dar avisose comprueban todos los argumentos y ajustes, y se sugieren los nombres válidos más parecidos
Deshacernocada edición es un paso de deshacer
Análisisse ejecutan en tu procesotareas en segundo plano, en un proceso hijo que se puede cancelar
Respuestasobjetos de resultado completos (cada superficie, cada dovela)resúmenes compatibles con JSON; resultados completos si se piden
Cuándo usarloinvestigación, estudios paramétricos, posprocesados nuevosscripts que deban comportarse igual que el programa; lo que hace un agente

Ambos niveles requieren tener instalado el paquete (Sección 1.1), pero ninguno necesita la ventana. En el repositorio, examples/quick_slope.py usa el motor y examples/api_acads.py la capa de operaciones; desde la raíz del repositorio se ejecutan como python -m examples.quick_slope y python -m examples.api_acads.

5.3.2El motor#

Proyectos#

Un modelo es un Project (ogr_core.project). Project.load(path) lee un archivo .ogr y project.save(path) guarda el proyecto en uno (Sección 5.4); Project(name) crea un proyecto vacío, al que se añaden contornos y materiales con add_boundary y add_material, como hace examples/quick_slope.py. ogr_core.project.demo.build_demo_project() devuelve el talud de demostración.

El análisis que describe el proyecto#

run_analysis(project, method_ids=None, progress_cb=None, *, allow_unconfigured=False), del módulo ogr_slip2d.analysis_runner, es el análisis que ejecutan Analysis → Compute, la línea de comandos y la capa de operaciones: una búsqueda por método, construida a partir de los ajustes del proyecto. Aplica los coeficientes parciales de la norma de diseño a una copia (el proyecto que se pasa no se modifica) y tiene en cuenta el desembalse rápido, los límites del talud, los filtros de superficies, los objetos de foco, el modo sísmico, los ajustes de convergencia, la semilla y las comprobaciones de admisibilidad.

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()
  • Devuelve un AnalysisOutcome con results (id del método → resultado de la búsqueda), factor_report (el informe de la norma de diseño), warnings (las notas que muestra la ventana) y project (la copia con los coeficientes parciales aplicados, que es la que se analizó). results, report = run_analysis(project) también funciona.
  • Si el proyecto no se puede analizar tal como está configurado, lanza AnalysisNotConfigured, un ValueError cuyo atributo problems enumera todos los motivos; allow_unconfigured=True ejecuta el análisis de todos modos, para quien ya los haya leído.
  • Con una norma de diseño activa, critical.fos es un factor de sobredimensionamiento, que ha de superar 1, y no un factor de seguridad.
  • progress_cb(done, total) se invoca a lo largo de toda la ejecución, con el avance de todos los métodos en conjunto.

Una búsqueda construida a mano#

Las búsquedas (GridSearch, SlopeSearch, AutoRefineSearch, BlockSearch, PathSearch, SimulatedAnnealingSearch; la de enjambre de partículas está en ogr_slip2d.particle_swarm) y los nueve métodos (BishopSimplified, JanbuSimplified, JanbuCorrected, OrdinaryFellenius, Spencer, GLEMorgensternPrice, LoweKarafiath, CorpsOfEngineers1, CorpsOfEngineers2) se pueden crear directamente; method_registry() asocia cada id de método con su clase.

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)
Una búsqueda construida a mano difiere de Compute

Un objeto construido a mano usa sus propios valores por defecto y toma del proyecto únicamente el modelo. Un método parte de una tolerancia de 0.001 y 75 iteraciones, sin aceleración de Steffensen (en el proyecto, los valores por defecto son 0.005, 50 y Steffensen activado); GridSearch parte de 10 × 10 intervalos, un incremento de radio de 10, 30 dovelas y un área mínima de 0.5 m², y coloca la rejilla por su cuenta cuando no se indican grid_x y grid_y. Los límites del talud, los filtros de superficies, los objetos de foco y el modo sísmico Ky del proyecto solo llegan a una búsqueda construida a mano si se le pasan, y el desembalse rápido y la norma de diseño los aplica únicamente el módulo que ejecuta los análisis. Para obtener lo que calcula la ventana, usa run_analysis. build_search(project, method_id) devuelve la búsqueda que el proyecto configura para un método, con el desembalse rápido incluido; la norma de diseño la aplica run_analysis a una copia del proyecto.

Tabla 5.3.2 Qué devuelve una búsqueda (SearchResult) y qué contiene cada superficie evaluada (LEMResult).
AtributoSignificado
result.criticalSuperficie admisible con el menor factor de seguridad (el menor coeficiente sísmico crítico en un análisis Ky), o None si ninguna superficie es válida. Si todas las superficies válidas incumplen las comprobaciones de admisibilidad, estas se ignoran en lugar de no devolver nada, y inadmissible_count lo indica.
result.evaluationsTodas las superficies que se dividieron en dovelas y se calcularon, sean válidas o no, en el orden en que se generaron.
valid_count, invalid_countSuperficies con y sin factor de seguridad; su suma es total_count.
attempts, focus_rejectedSuperficies candidatas generadas (una Grid Search intenta (nx + 1)(ny + 1)(radius increment + 1)) y candidatas descartadas por los objetos de foco.
top_n(n), min_fosLas n mejores superficies válidas; factor de seguridad de la superficie crítica.
minima, optimized, objectiveMínimos locales distintos (enjambre de partículas multimodal); superficie generada por Optimize Surfaces cuando mejoró el resultado de la búsqueda; "fos" o "ky".
fosFactor de seguridad de una superficie, o None si no existe (nunca NaN).
converged, iterations, method_idConvergencia de la iteración, iteraciones empleadas y método.
surface, slicesSuperficie de rotura (surface.to_dict() da su geometría) y sus dovelas.
is_validHa convergido, con un factor positivo y sin error.
admissible, admissibility_note, admissibility_reasonVeredicto de las comprobaciones posteriores al análisis (tracción, m-alfa).
error_message, reasonMotivo por el que no hay factor de seguridad, en texto y como código (Sección 5.5.2).
detailsDatos adicionales, como lambda, ky o newmark_displacement.

Una superficie#

evaluate_surfaces(project, surface, method_ids=None) calcula el factor de seguridad de una superficie dada con cada método, y lo hace con las mismas comprobaciones, la misma copia con coeficientes y la misma búsqueda configurada que run_analysis. Su atributo results asocia a cada id de método un resultado, o None si la superficie no corta el modelo.

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)

Archivos de resultados#

save_results(path, search_result, project_id="") escribe un resultado de búsqueda en HDF5 y load_summary(path) vuelve a leer sus metadatos y sus matrices de resumen, sin las dovelas; ambas funciones están en ogr_core.project. La estructura del archivo se describe en la Sección 5.4.5.

Scripts y procesos de trabajo#

Una Grid Search de 400 círculos o más se evalúa en un grupo de procesos de trabajo (como máximo 8, que por defecto usan el 50 % de los procesadores lógicos; Project Settings → Advanced permite desactivarlo). En Windows cada proceso de trabajo vuelve a importar el script, por lo que un script que ejecute una búsqueda debe mantener su código de nivel superior dentro de if __name__ == "__main__":, como hacen los ejemplos de esta página y los del repositorio. Si el grupo de procesos no puede arrancar, el análisis no se detiene: la búsqueda pasa a ejecutarse en un solo proceso.

5.3.3La capa de operaciones#

ogr_api reúne lo que el programa puede hacer con un modelo, en forma de operaciones con nombre, argumentos tipados y respuestas compatibles con JSON, sin depender de Qt ni del SDK de MCP. Hay 73 operaciones: las herramientas MCP, salvo server_info, con los mismos nombres; cada parámetro de una herramienta es un parámetro de su operación, así que las tablas de herramientas de la Sección 5.1.7 también las describen. ogr_api.OPERATIONS es el registro.

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) guarda los modelos abiertos, su historial de deshacer, sus resultados y el gestor de tareas. workdir sirve de base para las rutas relativas; sin él, una ruta relativa produce un error. Conserva los cinco últimos resultados de cada modelo.
  • call(ws, "operation", **kwargs) ejecuta una operación. El modelo se indica con project_id, que puede omitirse mientras haya un solo modelo abierto.
  • Las ediciones son atómicas y cada una es un paso de deshacer (call(ws, "project_history", action="undo")).
  • Los análisis (analysis_run, groundwater_run, statistics_run, back_analysis_run, optimize_run, drawdown_sweep_run) se ejecutan como tareas en un proceso hijo, sobre una copia independiente del modelo. La llamada espera hasta wait_seconds y, si el análisis no ha terminado, responde state: "running" con un job_id que se pasa a job_get; job_cancel finaliza el árbol de procesos de la tarea. La respuesta de una ejecución terminada incluye result_id, summary y stale (si el modelo ha cambiado desde entonces). ws.shutdown() finaliza las tareas que sigan en ejecución y elimina sus archivos.
  • Los errores son subclases de OgrApiError con un code estable, un message, un hint y details (Sección 5.5.4). Un nombre de argumento que la operación no admite no es un OgrApiError: Python lanza TypeError.
  • python_exec ejecuta un script que dispone de project, api (cada operación como api.<name>(...), con project_id ya rellenado), np, ogr_core, ogr_slip2d y result(result_id) (un resultado almacenado, completo), todo ello como un único paso de deshacer; devuelve ok, stdout, stderr, value (la representación de la última expresión), error y rolled_back.
En OGR Slip2D

La capa de operaciones existe porque los objetos de ajustes del motor aceptan sin dar aviso un ajuste mal escrito y un id de búsqueda desconocido se convierte en una Grid Search; aquí se rechazan ambos, con una sugerencia. Nunca importa Qt ni el SDK de MCP, lo que comprueba un test, de modo que el mismo registro sirve al servidor MCP, al puente para agentes de la ventana y a un script cualquiera.

Código: ogr_api/__init__.py · ogr_api/workspace.py · examples/api_acads.py · examples/quick_slope.py

5.3.4La consola integrada#

Window → Terminal (Ctrl+`) abre una consola de Python dentro del programa, conectada al proyecto abierto. Se ejecuta en el propio proceso del programa y actúa directamente sobre el modelo que se ve en pantalla.

Tabla 5.3.3 Nombres disponibles en la consola integrada.
NombreQué es
projectProject actual; se vuelve a enlazar al crear o abrir un proyecto.
canvasLienzo de dibujo de la ventana principal.
mainwindowVentana principal.
npNumPy.
ogr_core, ogr_slip2dPaquetes del modelo y del motor de cálculo.
help_ogr()Muestra una breve lista de consejos.
>>> 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 ejecuta la entrada, Shift+Enter empieza una nueva línea, Ctrl+↑ y Ctrl+↓ recorren el historial y Ctrl+L borra la consola.
  • El valor de una expresión suelta no se muestra: usa print(), como en el ejemplo anterior.
  • La consola se ejecuta en el propio hilo de la ventana, de modo que un cálculo largo iniciado en ella bloquea la ventana hasta que termina.
  • Los cambios que se hacen en project desde la consola actúan directamente sobre el modelo y no son pasos de Edit → Undo. Para hacer ediciones que se puedan deshacer desde un script, usa la capa de operaciones o el python_exec de un agente.
En OGR Slip2D

La consola ejecuta cada entrada como un bloque de sentencias, por lo que las expresiones no se muestran. Sus consejos (help_ogr()) todavía incluyen expresiones sueltas como r.critical.fos, que tal como están escritas no imprimen nada.

Código: ogr_gui/widgets/terminal.py