Single-profile workflow#

Open a project, configure layers and motions, run compute, and read basic results using the rsseismic package.

  1"""
  2Single-profile RSSeismic scripting workflow using the installed rsseismic package.
  3
  4  python examples/example_single_profile_workflow.py "E:\\path\\to\\project.rsseismicfile"
  5  python examples/example_single_profile_workflow.py --port 60058 --motion EQ1 project.rsseismicfile
  6  python examples/example_single_profile_workflow.py --no-compute project.rsseismicfile
  7"""
  8
  9from __future__ import annotations
 10
 11import argparse
 12import os
 13import sys
 14
 15from rsseismic import RSSeismicApplication, logger
 16from rsseismic._client import DEFAULT_SCRIPTING_PORT
 17
 18
 19def _select_motion_names(model, requested_names: list[str]) -> list[str]:
 20    model.Motions.refreshMotionsList()
 21    motions = model.Motions.listMotions()
 22    if not motions:
 23        raise RuntimeError(
 24            "No motions available. Configure motion directories in RSSeismic Preferences "
 25            "or ensure the project has motions."
 26        )
 27
 28    if requested_names:
 29        return requested_names
 30
 31    selected = [motion.name for motion in motions if motion.isSelected]
 32    if selected:
 33        return selected
 34
 35    return [motions[0].name]
 36
 37
 38def run_single_profile_workflow(
 39    project_path: str,
 40    port: int = DEFAULT_SCRIPTING_PORT,
 41    motion_names: list[str] | None = None,
 42    run_compute: bool = True,
 43) -> str | None:
 44    """
 45    Open a project, configure motion selection, save, optionally compute, return db3 path.
 46
 47    Returns resultDatabasePath when compute runs successfully, otherwise None.
 48    """
 49    project_path = os.path.abspath(project_path.strip())
 50    if not os.path.isfile(project_path):
 51        raise FileNotFoundError(f"Project file not found: {project_path}")
 52
 53    app = RSSeismicApplication(port=port)
 54    model = None
 55    db3_path: str | None = None
 56
 57    try:
 58        app.ping()
 59        model = app.openFile(project_path)
 60
 61        profile_name = model.Profiles.getActiveProfile().profileName
 62        names_to_select = _select_motion_names(model, motion_names or [])
 63        model.Motions.setMotionSelection(names_to_select, selectOnlyListed=True)
 64        model.save()
 65
 66        if not run_compute:
 67            return None
 68
 69        result = model.runCompute()
 70        if not result.success:
 71            raise RuntimeError(result.errorMessage or "RunCompute failed.")
 72        if result.partialSuccess:
 73            logger.warning("Some profile/motion pairs failed:\n%s", result.errorMessage)
 74
 75        motion_name = names_to_select[0]
 76        entry = model.Results.getResultDatabasePath(profile_name, motion_name)
 77        db3_path = entry.resultDatabasePath
 78        deconv = model.Profiles.getBedrockLayerProxy(profile_name).Data.getBoolValue("Deconvolution")
 79        if not db3_path and not deconv:
 80            raise RuntimeError("Compute succeeded but resultDatabasePath is empty.")
 81        return db3_path
 82    finally:
 83        if model is not None:
 84            model.close(saveProject=False)
 85        app.close()
 86
 87
 88def parse_args() -> argparse.Namespace:
 89    parser = argparse.ArgumentParser(
 90        description="Run a single-profile open → motion → save → compute → db3 workflow."
 91    )
 92    parser.add_argument(
 93        "project",
 94        help="Absolute path to a .rsseismicfile",
 95    )
 96    parser.add_argument(
 97        "--port",
 98        "-p",
 99        type=int,
100        default=int(os.environ.get("RSSEISMIC_SCRIPTING_PORT", DEFAULT_SCRIPTING_PORT)),
101        help=f"gRPC port (default: {DEFAULT_SCRIPTING_PORT}, or RSSEISMIC_SCRIPTING_PORT env)",
102    )
103    parser.add_argument(
104        "--motion",
105        "-m",
106        action="append",
107        default=[],
108        metavar="NAME",
109        help="Motion name to select for compute (repeatable). Default: already-selected, else first motion.",
110    )
111    parser.add_argument(
112        "--no-compute",
113        action="store_true",
114        help="Skip RunCompute (open, motion selection, save, close only)",
115    )
116    return parser.parse_args()
117
118
119def main() -> int:
120    args = parse_args()
121    try:
122        db3_path = run_single_profile_workflow(
123            args.project,
124            port=args.port,
125            motion_names=args.motion,
126            run_compute=not args.no_compute,
127        )
128    except Exception as ex:
129        print(f"Workflow failed: {ex}", file=sys.stderr)
130        return 1
131
132    if db3_path:
133        print(f"Compute finished. Result database: {db3_path}")
134    else:
135        print("Workflow finished (compute skipped).")
136    return 0
137
138
139if __name__ == "__main__":
140    raise SystemExit(main())