Single-profile workflow (GQ/H example)#

Extended variant: build a five-layer dry sand profile, Darendeli reference curves, MRDF curve fitting, and ChiChi motion selection.

  1"""
  2GQ/H nonlinear analysis — 5-layer dry sand profile, Darendeli curves, MRDF fitting, ChiChi motion.
  3All property name strings verified against DeepSoilFile/SoilLayer.cs and ProjectSettings.cs.
  4"""
  5
  6from __future__ import annotations
  7import math, os
  8from rsseismic import RSSeismicApplication, logger
  9from rsseismic.enums import FittingProcedureModel
 10
 11# ── Configuration ──────────────────────────────────────────────────────────
 12OUTPUT_PROJECT     = r"C:\Users\DylanCentella\source\repos\RSSeismicScripting\tests\resources\test_profile.rsseismicfile"
 13SCRIPTING_PORT     = 60058
 14CHICHI_MOTION_DIR  = r"C:\Program Files\Rocscience\RSSeismic\Resources\InputMotions"
 15CHICHI_MOTION_NAME = "ChiChi"   # verify with model.Motions.listMotions() if unsure
 16PROFILE_NAME       = "Profile 1"
 17
 18# Layer properties (metric: m, kN/m³, kPa)
 19N_LAYERS     = 5
 20THICKNESS    = 5.0    # m
 21UNIT_WEIGHT  = 20.0   # kN/m³
 22VS           = 250.0  # m/s  — set via "ShearWaveVelocity"; Gmax computed automatically
 23K0           = 0.46   # lateral earth pressure coefficient — key is "Ko" (letter o, not zero)
 24PI           = 0.0    # plasticity index
 25PHI_DEG      = 35.0   # friction angle (degrees) — used to compute ShearStrength per layer
 26
 27def shear_strength_kPa(layer_index_0: int) -> float:
 28    """τ_max = σ'v_mid · tan(φ')   [dry profile, c'=0, σ'v = total stress]"""
 29    z_mid = layer_index_0 * THICKNESS + THICKNESS / 2.0
 30    sigma_v_eff = UNIT_WEIGHT * z_mid          # kPa
 31    return sigma_v_eff * math.tan(math.radians(PHI_DEG))
 32
 33# ── Main ───────────────────────────────────────────────────────────────────
 34def main() -> None:
 35    app = RSSeismicApplication(port=SCRIPTING_PORT)
 36    app.ping()
 37    model = app.newProject()
 38
 39    try:
 40        # 1. Analysis mode — confirmed enum value: "Nonlinear" (not "NonLinear")
 41        model.ProjectSettings.Data.setEnumValue("analysisMode", "Nonlinear")
 42        model.ProjectSettings.Data.setEnumValue("hystereticFormulation", "NonMasing") 
 43
 44        # 2. Activate target profile
 45        model.Profiles.setActiveProfile(PROFILE_NAME)
 46
 47        # 3. Remove any existing layers, add 5 fresh ones
 48        # Get the existing layers BEFORE adding new ones
 49        existing = model.Profiles.listActiveSoilLayers()
 50
 51        # Add the 5 new layers first (now there's always more than one layer)
 52        result = model.SoilLayers.appendLayers(N_LAYERS)
 53        layer_ids = list(result.layerIDs)
 54
 55        # Now it's safe to delete the originals
 56        for s in existing:
 57            model.SoilLayers.deleteLayer(s.layerID)
 58
 59        # 4. Configure each layer
 60        for i, lid in enumerate(layer_ids):
 61            layer = model.Profiles.getSoilLayer(lid)
 62            tau_max = shear_strength_kPa(i)
 63
 64            # Basic properties — all confirmed names from SoilLayer.cs
 65            layer.setThickness(THICKNESS)
 66            layer.setUnitWeight(UNIT_WEIGHT)
 67            layer.setSoilModel("GQ_H")                              # eSoilModel.GQ_H
 68            layer.Data.setDoubleProperty("ShearWaveVelocity", VS)   # Gmax computed automatically
 69            layer.Data.setDoubleProperty("ShearStrength", tau_max)  # τ_max in kPa
 70
 71            # 5. Darendeli reference curve
 72            ref = layer.ReferenceCurve
 73            ref.setSoilType("Sand")                    # eSoilType.Sand (PI=0 → sandy)
 74            ref.setCurveModel("Darendeli_2001")        # eCurveModel.Darendeli_2001
 75            ref.Data.setDoubleProperty("Ko", K0)       # "Ko" — letter 'o', NOT the number '0'
 76            ref.Data.setDoubleProperty("PI", PI)       # "PI" confirmed in ReferenceCurve.cs
 77            # OCR, N, Frequency, etc. left at their project defaults
 78
 79            n_pts = ref.generateReferenceCurve()
 80            logger.info("Layer %d (z_mid=%.1f m): %d ref curve pts, τ_max=%.1f kPa",
 81                        i + 1, i * THICKNESS + THICKNESS / 2, n_pts, tau_max)
 82
 83            # 6. MRDF curve fit
 84            fitted = layer.Curve.runCurveFit(FittingProcedureModel.MRDF_UIUC)
 85            logger.info("Layer %d: MRDF fit → %d points.", i + 1, fitted)
 86
 87        # 7. Add ChiChi motion directory and select it
 88        model.Motions.addMotionDirectory(CHICHI_MOTION_DIR)
 89        model.Motions.refreshMotionsList()
 90        available = [m.name for m in model.Motions.listMotions()]
 91        logger.info("Available motions: %s", available)
 92        model.Motions.setMotionSelection([CHICHI_MOTION_NAME], selectOnlyListed=True)
 93
 94        # 8. Save and compute
 95        model.saveAs(OUTPUT_PROJECT)
 96        compute_result = model.runCompute(timeout=3600)
 97        if not compute_result.success:
 98            raise RuntimeError(f"RunCompute failed: {compute_result.errorMessage}")
 99        if compute_result.partialSuccess:
100            logger.warning("Partial success: %s", compute_result.errorMessage)
101
102        # 9. Result path
103        entry = model.Results.getResultDatabasePath(PROFILE_NAME, CHICHI_MOTION_NAME)
104        print(f"\nResult database: {entry.resultDatabasePath}")
105        print(f"Exists on disk:  {entry.resultDatabaseExists}")
106
107    finally:
108        model.close(saveProject=False)
109        app.close()
110
111if __name__ == "__main__":
112    main()