RSSeismicScripting 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rsseismic/Application.py +196 -0
- rsseismic/ApplicationManager.py +111 -0
- rsseismic/BedrockLayer.py +29 -0
- rsseismic/MeanProfile.py +169 -0
- rsseismic/MeanProfileSoilLayer.py +119 -0
- rsseismic/Model.py +204 -0
- rsseismic/Motions.py +140 -0
- rsseismic/ProfileGenerationSettings.py +29 -0
- rsseismic/Profiles.py +186 -0
- rsseismic/ProjectSettings.py +46 -0
- rsseismic/ReferenceCurve.py +73 -0
- rsseismic/Results.py +419 -0
- rsseismic/SoilLayer.py +34 -0
- rsseismic/SoilLayerCurve.py +124 -0
- rsseismic/SoilLayerOperations.py +120 -0
- rsseismic/__init__.py +79 -0
- rsseismic/_application_paths.py +74 -0
- rsseismic/_batch_compute.py +420 -0
- rsseismic/_client.py +81 -0
- rsseismic/_proto_converters.py +345 -0
- rsseismic/_proxyObject.py +11 -0
- rsseismic/_result_bulk_dataframes.py +202 -0
- rsseismic/_result_dataframes.py +77 -0
- rsseismic/_rpc_helpers.py +55 -0
- rsseismic/enums/__init__.py +23 -0
- rsseismic/enums/_generated.py +120 -0
- rsseismic/enums/fitting_procedure.py +15 -0
- rsseismic/exceptions.py +11 -0
- rsseismic/generatedFiles/ApplicationService_pb2.py +46 -0
- rsseismic/generatedFiles/ApplicationService_pb2.pyi +67 -0
- rsseismic/generatedFiles/ApplicationService_pb2_grpc.py +274 -0
- rsseismic/generatedFiles/BedrockLayerDataService_pb2.py +27 -0
- rsseismic/generatedFiles/BedrockLayerDataService_pb2.pyi +5 -0
- rsseismic/generatedFiles/BedrockLayerDataService_pb2_grpc.py +231 -0
- rsseismic/generatedFiles/CommonMessages_pb2.py +50 -0
- rsseismic/generatedFiles/CommonMessages_pb2.pyi +113 -0
- rsseismic/generatedFiles/CommonMessages_pb2_grpc.py +29 -0
- rsseismic/generatedFiles/MeanProfileService_pb2.py +62 -0
- rsseismic/generatedFiles/MeanProfileService_pb2.pyi +176 -0
- rsseismic/generatedFiles/MeanProfileService_pb2_grpc.py +458 -0
- rsseismic/generatedFiles/MeanProfileSoilLayerDataService_pb2.py +45 -0
- rsseismic/generatedFiles/MeanProfileSoilLayerDataService_pb2.pyi +87 -0
- rsseismic/generatedFiles/MeanProfileSoilLayerDataService_pb2_grpc.py +360 -0
- rsseismic/generatedFiles/ModelService_pb2.py +54 -0
- rsseismic/generatedFiles/ModelService_pb2.pyi +122 -0
- rsseismic/generatedFiles/ModelService_pb2_grpc.py +360 -0
- rsseismic/generatedFiles/MotionService_pb2.py +78 -0
- rsseismic/generatedFiles/MotionService_pb2.pyi +224 -0
- rsseismic/generatedFiles/MotionService_pb2_grpc.py +575 -0
- rsseismic/generatedFiles/ProfileGenerationSettingsService_pb2.py +27 -0
- rsseismic/generatedFiles/ProfileGenerationSettingsService_pb2.pyi +5 -0
- rsseismic/generatedFiles/ProfileGenerationSettingsService_pb2_grpc.py +145 -0
- rsseismic/generatedFiles/ProfileService_pb2.py +76 -0
- rsseismic/generatedFiles/ProfileService_pb2.pyi +246 -0
- rsseismic/generatedFiles/ProfileService_pb2_grpc.py +532 -0
- rsseismic/generatedFiles/ProjectSettingsDataService_pb2.py +35 -0
- rsseismic/generatedFiles/ProjectSettingsDataService_pb2.pyi +40 -0
- rsseismic/generatedFiles/ProjectSettingsDataService_pb2_grpc.py +232 -0
- rsseismic/generatedFiles/ReferenceCurveDataService_pb2.py +41 -0
- rsseismic/generatedFiles/ReferenceCurveDataService_pb2.pyi +73 -0
- rsseismic/generatedFiles/ReferenceCurveDataService_pb2_grpc.py +281 -0
- rsseismic/generatedFiles/ResultsService_pb2.py +82 -0
- rsseismic/generatedFiles/ResultsService_pb2.pyi +373 -0
- rsseismic/generatedFiles/ResultsService_pb2_grpc.py +489 -0
- rsseismic/generatedFiles/SoilLayerCurveDataService_pb2.py +52 -0
- rsseismic/generatedFiles/SoilLayerCurveDataService_pb2.pyi +150 -0
- rsseismic/generatedFiles/SoilLayerCurveDataService_pb2_grpc.py +280 -0
- rsseismic/generatedFiles/SoilLayerDataService_pb2.py +27 -0
- rsseismic/generatedFiles/SoilLayerDataService_pb2.pyi +5 -0
- rsseismic/generatedFiles/SoilLayerDataService_pb2_grpc.py +231 -0
- rsseismic/generatedFiles/SoilLayerService_pb2.py +46 -0
- rsseismic/generatedFiles/SoilLayerService_pb2.pyi +122 -0
- rsseismic/generatedFiles/SoilLayerService_pb2_grpc.py +317 -0
- rsseismic/generatedFiles/__init__.py +4 -0
- rsseismic/properties/_PropertyAccessor.py +151 -0
- rsseismic/properties/__init__.py +6 -0
- rsseismic/properties/bedrockLayer/__init__.py +1 -0
- rsseismic/properties/bedrockLayer/_generated.py +127 -0
- rsseismic/properties/meanProfileSoilLayer/__init__.py +1 -0
- rsseismic/properties/meanProfileSoilLayer/_generated.py +87 -0
- rsseismic/properties/profileGenerationSettings/__init__.py +1 -0
- rsseismic/properties/profileGenerationSettings/_generated.py +195 -0
- rsseismic/properties/projectSettings/__init__.py +1 -0
- rsseismic/properties/projectSettings/_generated.py +270 -0
- rsseismic/properties/referenceCurve/__init__.py +1 -0
- rsseismic/properties/referenceCurve/_generated.py +264 -0
- rsseismic/properties/soilLayer/__init__.py +1 -0
- rsseismic/properties/soilLayer/_generated.py +523 -0
- rsseismic/types.py +311 -0
- rsseismicscripting-3.0.0.dist-info/METADATA +88 -0
- rsseismicscripting-3.0.0.dist-info/RECORD +94 -0
- rsseismicscripting-3.0.0.dist-info/WHEEL +5 -0
- rsseismicscripting-3.0.0.dist-info/licenses/License.txt +20 -0
- rsseismicscripting-3.0.0.dist-info/top_level.txt +1 -0
rsseismic/Application.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from rsseismic._application_paths import getInstalledExecutablePath
|
|
6
|
+
from rsseismic._client import Client, DEFAULT_SCRIPTING_PORT
|
|
7
|
+
from rsseismic.ApplicationManager import ApplicationManager
|
|
8
|
+
from rsseismic._rpc_helpers import require_success
|
|
9
|
+
from rsseismic.exceptions import ScriptingError
|
|
10
|
+
from rsseismic.generatedFiles import ApplicationService_pb2 as ApplicationService_pb2
|
|
11
|
+
from rsseismic.generatedFiles import ApplicationService_pb2_grpc as ApplicationService_pb2_grpc
|
|
12
|
+
from rsseismic._batch_compute import (
|
|
13
|
+
BatchComputeJobResult,
|
|
14
|
+
BatchComputeResult,
|
|
15
|
+
run_batch_compute,
|
|
16
|
+
)
|
|
17
|
+
from rsseismic.Model import Model
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RSSeismicApplication:
|
|
21
|
+
"""
|
|
22
|
+
Entry point for connecting to a running RSSeismic scripting server.
|
|
23
|
+
|
|
24
|
+
Mirrors RS3's RS3Modeler: connect on a port, open projects, return Model proxies.
|
|
25
|
+
Use startApplication() to launch RSSeismic with -startScriptingServer when it is
|
|
26
|
+
not already running.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, port: int = DEFAULT_SCRIPTING_PORT, host: str = "localhost"):
|
|
30
|
+
self._client = Client(port=port, host=host)
|
|
31
|
+
self._port = port
|
|
32
|
+
self._host = host
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def port(self) -> int:
|
|
36
|
+
return self._port
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def client(self) -> Client:
|
|
40
|
+
return self._client
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def startApplication(
|
|
44
|
+
cls,
|
|
45
|
+
port: int = DEFAULT_SCRIPTING_PORT,
|
|
46
|
+
overridePathToExecutable: str | None = None,
|
|
47
|
+
timeout: float = ApplicationManager.defaultTimeout,
|
|
48
|
+
) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Launch RSSeismic and wait until the scripting server on port accepts ping.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
port: Scripting port (49152–65535). Same value as DEFAULT_SCRIPTING_PORT / 60058.
|
|
54
|
+
overridePathToExecutable: Full path to RSSeismic.exe. When omitted, the latest
|
|
55
|
+
RSSeismic install is resolved from the Windows registry.
|
|
56
|
+
timeout: Seconds to wait for the server to become reachable.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
ValueError: port out of range
|
|
60
|
+
RuntimeError: port occupied, executable not found, or registry lookup failed
|
|
61
|
+
TimeoutError: server not ready within timeout
|
|
62
|
+
"""
|
|
63
|
+
executable_path = (
|
|
64
|
+
overridePathToExecutable
|
|
65
|
+
if overridePathToExecutable
|
|
66
|
+
else getInstalledExecutablePath()
|
|
67
|
+
)
|
|
68
|
+
app_manager = ApplicationManager()
|
|
69
|
+
app_logger = logging.getLogger("Rocscience.RSSeismic")
|
|
70
|
+
app_manager.startApplication(
|
|
71
|
+
executable_path,
|
|
72
|
+
port,
|
|
73
|
+
cls._verifyServerRunning,
|
|
74
|
+
app_logger,
|
|
75
|
+
timeout,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _verifyServerRunning(port: int) -> None:
|
|
80
|
+
client = Client(port=port)
|
|
81
|
+
try:
|
|
82
|
+
client.ping()
|
|
83
|
+
finally:
|
|
84
|
+
client.closeConnection()
|
|
85
|
+
|
|
86
|
+
def ping(self) -> None:
|
|
87
|
+
"""Verify the scripting server is reachable."""
|
|
88
|
+
self._client.ping()
|
|
89
|
+
|
|
90
|
+
def getMaxConcurrentAnalyses(self) -> int:
|
|
91
|
+
"""
|
|
92
|
+
Return the effective maxConcurrentAnalyses from RSSeismic preferences.
|
|
93
|
+
|
|
94
|
+
This is the within-project cap on simultaneous engine runs used during
|
|
95
|
+
RunCompute. Batch parallel_degree capping uses this value when available.
|
|
96
|
+
"""
|
|
97
|
+
stub = ApplicationService_pb2_grpc.ApplicationServiceStub(self._client.channel)
|
|
98
|
+
request = ApplicationService_pb2.GetMaxConcurrentAnalysesRequest()
|
|
99
|
+
response = self._client.callFunction(stub.GetMaxConcurrentAnalyses, request)
|
|
100
|
+
return int(response.maxConcurrentAnalyses)
|
|
101
|
+
|
|
102
|
+
def setMaxConcurrentAnalyses(self, maxConcurrentAnalyses: int) -> int:
|
|
103
|
+
"""
|
|
104
|
+
Set maxConcurrentAnalyses in RSSeismic preferences (within-project engine cap).
|
|
105
|
+
|
|
106
|
+
Values are clamped to 1..256, matching the Preferences UI. Returns the effective
|
|
107
|
+
value after save (same semantics as getMaxConcurrentAnalyses).
|
|
108
|
+
"""
|
|
109
|
+
stub = ApplicationService_pb2_grpc.ApplicationServiceStub(self._client.channel)
|
|
110
|
+
request = ApplicationService_pb2.SetMaxConcurrentAnalysesRequest(
|
|
111
|
+
maxConcurrentAnalyses=maxConcurrentAnalyses,
|
|
112
|
+
)
|
|
113
|
+
response = self._client.callFunction(stub.SetMaxConcurrentAnalyses, request)
|
|
114
|
+
|
|
115
|
+
if not response.success:
|
|
116
|
+
raise ScriptingError(
|
|
117
|
+
response.errorMessage or "SetMaxConcurrentAnalyses failed."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return int(response.maxConcurrentAnalyses)
|
|
121
|
+
|
|
122
|
+
def openFile(self, path: str) -> Model:
|
|
123
|
+
"""Open a DeepSoil project file and return a Model handle."""
|
|
124
|
+
stub = ApplicationService_pb2_grpc.ApplicationServiceStub(self._client.channel)
|
|
125
|
+
request = ApplicationService_pb2.OpenFileRequest(fileName=path)
|
|
126
|
+
response = self._client.callFunction(stub.OpenFile, request)
|
|
127
|
+
require_success(response, "OpenFile")
|
|
128
|
+
|
|
129
|
+
if not response.modelID:
|
|
130
|
+
raise ScriptingError("OpenFile succeeded but returned an empty modelID.")
|
|
131
|
+
|
|
132
|
+
return Model(self._client, response.modelID)
|
|
133
|
+
|
|
134
|
+
def newProject(self) -> Model:
|
|
135
|
+
"""Create a blank project with default settings and return a Model handle."""
|
|
136
|
+
stub = ApplicationService_pb2_grpc.ApplicationServiceStub(self._client.channel)
|
|
137
|
+
request = ApplicationService_pb2.NewProjectRequest()
|
|
138
|
+
response = self._client.callFunction(stub.NewProject, request)
|
|
139
|
+
|
|
140
|
+
if not response.success:
|
|
141
|
+
raise ScriptingError("NewProject failed.")
|
|
142
|
+
|
|
143
|
+
if not response.modelID:
|
|
144
|
+
raise ScriptingError("NewProject succeeded but returned an empty modelID.")
|
|
145
|
+
|
|
146
|
+
return Model(self._client, response.modelID)
|
|
147
|
+
|
|
148
|
+
def runBatchCompute(
|
|
149
|
+
self,
|
|
150
|
+
file_paths: list[str],
|
|
151
|
+
motion_names: list[str] | None = None,
|
|
152
|
+
*,
|
|
153
|
+
parallel_degree: int = 1,
|
|
154
|
+
max_concurrent_analyses: int | None = None,
|
|
155
|
+
timeout: float | None = None,
|
|
156
|
+
close_save_project: bool = False,
|
|
157
|
+
continue_on_error: bool = True,
|
|
158
|
+
) -> BatchComputeResult:
|
|
159
|
+
"""
|
|
160
|
+
Run compute for many project files (open → motion selection → compute → close).
|
|
161
|
+
|
|
162
|
+
Motion selection applies to the open session only; it is not stored in the
|
|
163
|
+
project file. Each job calls setMotionSelection before runCompute.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
file_paths: Project files to process (.rsseismicfile). Duplicate paths are omitted.
|
|
167
|
+
motion_names: Motions to select for every project. When None, each project keeps
|
|
168
|
+
its current selection or uses the first available motion.
|
|
169
|
+
parallel_degree: How many projects may run at once (cross-project only). Capped
|
|
170
|
+
using maxConcurrentAnalyses from RSSeismic preferences when available.
|
|
171
|
+
max_concurrent_analyses: When set, applies this within-project engine cap for the
|
|
172
|
+
batch via setMaxConcurrentAnalyses and restores the previous preference after
|
|
173
|
+
the batch. When None, uses getMaxConcurrentAnalyses().
|
|
174
|
+
timeout: Optional RunCompute deadline in seconds for each project.
|
|
175
|
+
close_save_project: When True, save each project on close.
|
|
176
|
+
continue_on_error: When False, stop starting new jobs after the first failure
|
|
177
|
+
(in-flight jobs may still finish).
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
BatchComputeResult with per-file outcomes in input order.
|
|
181
|
+
"""
|
|
182
|
+
return run_batch_compute(
|
|
183
|
+
self,
|
|
184
|
+
file_paths,
|
|
185
|
+
motion_names,
|
|
186
|
+
parallel_degree=parallel_degree,
|
|
187
|
+
max_concurrent_analyses=max_concurrent_analyses,
|
|
188
|
+
timeout=timeout,
|
|
189
|
+
close_save_project=close_save_project,
|
|
190
|
+
continue_on_error=continue_on_error,
|
|
191
|
+
logger=logging.getLogger("Rocscience.RSSeismic"),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def close(self) -> None:
|
|
195
|
+
"""Close the gRPC connection."""
|
|
196
|
+
self._client.closeConnection()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
from logging import Logger
|
|
9
|
+
from typing import Callable
|
|
10
|
+
|
|
11
|
+
from multiprocessing.connection import Listener
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ApplicationManager:
|
|
15
|
+
minimumPort = 49152
|
|
16
|
+
maximumPort = 65535
|
|
17
|
+
defaultTimeout = 120.0
|
|
18
|
+
|
|
19
|
+
def startApplication(
|
|
20
|
+
self,
|
|
21
|
+
pathToExecutable: str,
|
|
22
|
+
port: int,
|
|
23
|
+
isServerRunning: Callable[[int], None],
|
|
24
|
+
logger: Logger,
|
|
25
|
+
timeout: float = defaultTimeout,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Starts the application specified by pathToExecutable with ``-startScriptingServer`` on the given port.
|
|
29
|
+
Returns when the scripting server is ready to accept requests.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
pathToExecutable: Full path to the RSSeismic executable to start.
|
|
33
|
+
port: Port number for the scripting server (49152–65535).
|
|
34
|
+
isServerRunning: Callable invoked to verify the server on ``port`` is reachable.
|
|
35
|
+
logger: Logger used during startup; logging is suppressed while polling the server.
|
|
36
|
+
timeout: Seconds to wait for the server to become ready.
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
ValueError: port outside 49152–65535
|
|
40
|
+
RuntimeError: port occupied or executable missing
|
|
41
|
+
TimeoutError: server not ready within timeout
|
|
42
|
+
"""
|
|
43
|
+
if port < self.minimumPort or port > self.maximumPort:
|
|
44
|
+
msg = f"port must be in the range {self.minimumPort} to {self.maximumPort}"
|
|
45
|
+
logger.error(msg)
|
|
46
|
+
raise ValueError(msg)
|
|
47
|
+
|
|
48
|
+
if not os.path.isfile(pathToExecutable):
|
|
49
|
+
msg = f"executable not found: {pathToExecutable}"
|
|
50
|
+
logger.error(msg)
|
|
51
|
+
raise RuntimeError(msg)
|
|
52
|
+
|
|
53
|
+
if not self.isPortAvailable(port):
|
|
54
|
+
msg = f"port number {port} is occupied. Please choose another port."
|
|
55
|
+
logger.error(msg)
|
|
56
|
+
raise RuntimeError(msg)
|
|
57
|
+
|
|
58
|
+
logger.info(
|
|
59
|
+
"Starting RSSeismic at %s with scripting server on port %s...",
|
|
60
|
+
pathToExecutable,
|
|
61
|
+
port,
|
|
62
|
+
)
|
|
63
|
+
popen_kwargs: dict = {
|
|
64
|
+
"args": [pathToExecutable, "-startScriptingServer", str(port)],
|
|
65
|
+
"start_new_session": True,
|
|
66
|
+
}
|
|
67
|
+
if sys.platform == "win32":
|
|
68
|
+
popen_kwargs["creationflags"] = subprocess.DETACHED_PROCESS
|
|
69
|
+
|
|
70
|
+
subprocess.Popen(**popen_kwargs)
|
|
71
|
+
self._tryToConnectToServer(port, isServerRunning, timeout, logger)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def isPortAvailable(port: int) -> bool:
|
|
75
|
+
listener = None
|
|
76
|
+
try:
|
|
77
|
+
listener = Listener(("localhost", port), "AF_INET")
|
|
78
|
+
return True
|
|
79
|
+
except Exception:
|
|
80
|
+
return False
|
|
81
|
+
finally:
|
|
82
|
+
if listener is not None:
|
|
83
|
+
listener.close()
|
|
84
|
+
|
|
85
|
+
def _tryToConnectToServer(
|
|
86
|
+
self,
|
|
87
|
+
port: int,
|
|
88
|
+
isServerRunning: Callable[[int], None],
|
|
89
|
+
timeout: float,
|
|
90
|
+
logger: Logger,
|
|
91
|
+
) -> None:
|
|
92
|
+
start_time = time.time()
|
|
93
|
+
|
|
94
|
+
while True:
|
|
95
|
+
if timeout and (time.time() - start_time) > timeout:
|
|
96
|
+
msg = "RSSeismic did not start the scripting server within the given timeout."
|
|
97
|
+
logger.error(msg)
|
|
98
|
+
raise TimeoutError(msg)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
logger.debug("Trying to connect to the scripting server...")
|
|
102
|
+
previous_disabled = logger.disabled
|
|
103
|
+
logger.disabled = True
|
|
104
|
+
try:
|
|
105
|
+
isServerRunning(port)
|
|
106
|
+
finally:
|
|
107
|
+
logger.disabled = previous_disabled
|
|
108
|
+
logger.debug("Connected to scripting server.")
|
|
109
|
+
return
|
|
110
|
+
except Exception:
|
|
111
|
+
time.sleep(0.5)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from rsseismic._client import Client
|
|
4
|
+
from rsseismic._proxyObject import _ProxyObject
|
|
5
|
+
from rsseismic.generatedFiles import BedrockLayerDataService_pb2_grpc as BedrockLayerDataService_pb2_grpc
|
|
6
|
+
from rsseismic.properties._PropertyAccessor import ProjectScopedPropertyAccessor
|
|
7
|
+
from rsseismic.properties.bedrockLayer._generated import BedrockLayerPropertiesMixin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BedrockLayer(_ProxyObject, BedrockLayerPropertiesMixin):
|
|
11
|
+
"""
|
|
12
|
+
Read and edit bedrock layer properties for a profile.
|
|
13
|
+
|
|
14
|
+
Obtain an instance from ``model.Profiles.getBedrockLayerProxy(profileName)``.
|
|
15
|
+
|
|
16
|
+
Bedrock thickness cannot be changed through this object; it is calculated from
|
|
17
|
+
the soil layers above bedrock. To read thickness, use
|
|
18
|
+
``model.Profiles.getBedrockLayer(profileName).thickness``.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, client: Client, modelId: str, layerId: str):
|
|
22
|
+
super().__init__(client, layerId)
|
|
23
|
+
self.modelId = modelId
|
|
24
|
+
stub = BedrockLayerDataService_pb2_grpc.BedrockLayerDataServiceStub(self._client.channel)
|
|
25
|
+
self.Data = ProjectScopedPropertyAccessor(client, modelId, layerId, stub)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def layerId(self) -> str:
|
|
29
|
+
return self._objectId
|
rsseismic/MeanProfile.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from rsseismic._client import Client
|
|
6
|
+
from rsseismic._proto_converters import (
|
|
7
|
+
from_mean_profile_append_layers_response,
|
|
8
|
+
from_mean_profile_bedrock_layer_entry,
|
|
9
|
+
from_mean_profile_delete_layer_response,
|
|
10
|
+
from_mean_profile_insert_layer_response,
|
|
11
|
+
from_mean_profile_move_layer_response,
|
|
12
|
+
from_mean_profile_response,
|
|
13
|
+
from_mean_profile_soil_layer_entry,
|
|
14
|
+
)
|
|
15
|
+
from rsseismic._proxyObject import _ProxyObject
|
|
16
|
+
from rsseismic._rpc_helpers import require_success
|
|
17
|
+
from rsseismic.generatedFiles import MeanProfileService_pb2 as MeanProfileService_pb2
|
|
18
|
+
from rsseismic.generatedFiles import MeanProfileService_pb2_grpc as MeanProfileService_pb2_grpc
|
|
19
|
+
from rsseismic.types import (
|
|
20
|
+
BedrockLayerSummary,
|
|
21
|
+
MeanProfileAppendLayersResult,
|
|
22
|
+
MeanProfileDeleteLayerResult,
|
|
23
|
+
MeanProfileInsertLayerResult,
|
|
24
|
+
MeanProfileMoveLayerResult,
|
|
25
|
+
MeanProfileSummary,
|
|
26
|
+
SoilLayerSummary,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from rsseismic.MeanProfileSoilLayer import MeanProfileSoilLayer
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MeanProfile(_ProxyObject):
|
|
34
|
+
"""
|
|
35
|
+
Template mean profile for automatic profile generation.
|
|
36
|
+
|
|
37
|
+
When automatic profile generation is enabled, configure this profile before
|
|
38
|
+
calling ``Model.generateProfiles()``. The individual profiles created by that
|
|
39
|
+
step are based on this template, not on the profiles under ``model.Profiles``.
|
|
40
|
+
|
|
41
|
+
Use ``listSoilLayers``, ``getSoilLayer``, ``insertLayerBefore``, and related
|
|
42
|
+
methods to edit the template. To change layer properties, use the
|
|
43
|
+
``MeanProfileSoilLayer`` returned by ``getSoilLayer`` (including reference
|
|
44
|
+
curves via ``layer.ReferenceCurve``).
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, client: Client, modelId: str):
|
|
48
|
+
super().__init__(client, modelId)
|
|
49
|
+
self.modelId = modelId
|
|
50
|
+
self._mean_profile_service = MeanProfileService_pb2_grpc.MeanProfileServiceStub(
|
|
51
|
+
self._client.channel
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def getMeanProfile(self) -> MeanProfileSummary:
|
|
55
|
+
"""Return summary information for the mean profile."""
|
|
56
|
+
request = MeanProfileService_pb2.GetMeanProfileRequest(modelID=self._objectId)
|
|
57
|
+
response = self._client.callFunction(self._mean_profile_service.GetMeanProfile, request)
|
|
58
|
+
require_success(response, "GetMeanProfile")
|
|
59
|
+
return from_mean_profile_response(response)
|
|
60
|
+
|
|
61
|
+
def listSoilLayers(self) -> list[SoilLayerSummary]:
|
|
62
|
+
"""List soil layers on the mean profile."""
|
|
63
|
+
request = MeanProfileService_pb2.ListSoilLayersRequest(modelID=self._objectId)
|
|
64
|
+
response = self._client.callFunction(self._mean_profile_service.ListSoilLayers, request)
|
|
65
|
+
require_success(response, "ListSoilLayers")
|
|
66
|
+
return [from_mean_profile_soil_layer_entry(layer) for layer in response.layers]
|
|
67
|
+
|
|
68
|
+
def getBedrockLayer(self) -> BedrockLayerSummary:
|
|
69
|
+
"""Return the bedrock layer on the mean profile."""
|
|
70
|
+
request = MeanProfileService_pb2.GetBedrockLayerRequest(modelID=self._objectId)
|
|
71
|
+
response = self._client.callFunction(self._mean_profile_service.GetBedrockLayer, request)
|
|
72
|
+
require_success(response, "GetBedrockLayer")
|
|
73
|
+
return from_mean_profile_bedrock_layer_entry(response.bedrock)
|
|
74
|
+
|
|
75
|
+
def getSoilLayer(self, layerId: str) -> MeanProfileSoilLayer:
|
|
76
|
+
"""Return a property proxy for a mean-profile soil layer GUID."""
|
|
77
|
+
from rsseismic.MeanProfileSoilLayer import MeanProfileSoilLayer
|
|
78
|
+
|
|
79
|
+
return MeanProfileSoilLayer(self._client, self._objectId, layerId)
|
|
80
|
+
|
|
81
|
+
def insertLayerBefore(
|
|
82
|
+
self,
|
|
83
|
+
referenceLayerID: str,
|
|
84
|
+
) -> MeanProfileInsertLayerResult:
|
|
85
|
+
"""
|
|
86
|
+
Insert a soil layer before referenceLayerID.
|
|
87
|
+
|
|
88
|
+
referenceLayerID may be a soil layer GUID or the bedrock layer GUID
|
|
89
|
+
(insert before bedrock appends a layer).
|
|
90
|
+
"""
|
|
91
|
+
request = MeanProfileService_pb2.InsertLayerBeforeRequest(
|
|
92
|
+
modelID=self._objectId,
|
|
93
|
+
referenceLayerID=referenceLayerID,
|
|
94
|
+
)
|
|
95
|
+
response = self._client.callFunction(
|
|
96
|
+
self._mean_profile_service.InsertLayerBefore,
|
|
97
|
+
request,
|
|
98
|
+
)
|
|
99
|
+
require_success(response, "InsertLayerBefore")
|
|
100
|
+
return from_mean_profile_insert_layer_response(response)
|
|
101
|
+
|
|
102
|
+
def insertLayerAfter(
|
|
103
|
+
self,
|
|
104
|
+
referenceLayerID: str,
|
|
105
|
+
) -> MeanProfileInsertLayerResult:
|
|
106
|
+
"""Insert a soil layer after a soil layer GUID."""
|
|
107
|
+
request = MeanProfileService_pb2.InsertLayerAfterRequest(
|
|
108
|
+
modelID=self._objectId,
|
|
109
|
+
referenceLayerID=referenceLayerID,
|
|
110
|
+
)
|
|
111
|
+
response = self._client.callFunction(
|
|
112
|
+
self._mean_profile_service.InsertLayerAfter,
|
|
113
|
+
request,
|
|
114
|
+
)
|
|
115
|
+
require_success(response, "InsertLayerAfter")
|
|
116
|
+
return from_mean_profile_insert_layer_response(response)
|
|
117
|
+
|
|
118
|
+
def appendLayers(self, count: int = 1) -> MeanProfileAppendLayersResult:
|
|
119
|
+
"""Append one or more soil layers before bedrock."""
|
|
120
|
+
request = MeanProfileService_pb2.AppendLayersRequest(
|
|
121
|
+
modelID=self._objectId,
|
|
122
|
+
count=count,
|
|
123
|
+
)
|
|
124
|
+
response = self._client.callFunction(self._mean_profile_service.AppendLayers, request)
|
|
125
|
+
require_success(response, "AppendLayers")
|
|
126
|
+
return from_mean_profile_append_layers_response(response)
|
|
127
|
+
|
|
128
|
+
def deleteLayer(self, layerID: str) -> MeanProfileDeleteLayerResult:
|
|
129
|
+
"""Delete a mean-profile soil layer. At least one soil layer must remain."""
|
|
130
|
+
request = MeanProfileService_pb2.DeleteLayerRequest(
|
|
131
|
+
modelID=self._objectId,
|
|
132
|
+
layerID=layerID,
|
|
133
|
+
)
|
|
134
|
+
response = self._client.callFunction(self._mean_profile_service.DeleteLayer, request)
|
|
135
|
+
require_success(response, "DeleteLayer")
|
|
136
|
+
return from_mean_profile_delete_layer_response(response)
|
|
137
|
+
|
|
138
|
+
def duplicateLayer(
|
|
139
|
+
self,
|
|
140
|
+
sourceLayerID: str,
|
|
141
|
+
insertAfter: bool = True,
|
|
142
|
+
) -> MeanProfileInsertLayerResult:
|
|
143
|
+
"""Duplicate a mean-profile soil layer before or after the source layer."""
|
|
144
|
+
request = MeanProfileService_pb2.DuplicateLayerRequest(
|
|
145
|
+
modelID=self._objectId,
|
|
146
|
+
sourceLayerID=sourceLayerID,
|
|
147
|
+
insertAfter=insertAfter,
|
|
148
|
+
)
|
|
149
|
+
response = self._client.callFunction(
|
|
150
|
+
self._mean_profile_service.DuplicateLayer,
|
|
151
|
+
request,
|
|
152
|
+
)
|
|
153
|
+
require_success(response, "DuplicateLayer")
|
|
154
|
+
return from_mean_profile_insert_layer_response(response)
|
|
155
|
+
|
|
156
|
+
def moveLayer(
|
|
157
|
+
self,
|
|
158
|
+
layerID: str,
|
|
159
|
+
newIndex1Based: int,
|
|
160
|
+
) -> MeanProfileMoveLayerResult:
|
|
161
|
+
"""Move a mean-profile soil layer to a new 1-based index."""
|
|
162
|
+
request = MeanProfileService_pb2.MoveLayerRequest(
|
|
163
|
+
modelID=self._objectId,
|
|
164
|
+
layerID=layerID,
|
|
165
|
+
newIndex1Based=newIndex1Based,
|
|
166
|
+
)
|
|
167
|
+
response = self._client.callFunction(self._mean_profile_service.MoveLayer, request)
|
|
168
|
+
require_success(response, "MoveLayer")
|
|
169
|
+
return from_mean_profile_move_layer_response(response)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from rsseismic._client import Client
|
|
4
|
+
from rsseismic._proxyObject import _ProxyObject
|
|
5
|
+
from rsseismic._rpc_helpers import require_success
|
|
6
|
+
from rsseismic.generatedFiles import MeanProfileSoilLayerDataService_pb2 as MeanProfileSoilLayerDataService_pb2
|
|
7
|
+
from rsseismic.generatedFiles import MeanProfileSoilLayerDataService_pb2_grpc as MeanProfileSoilLayerDataService_pb2_grpc
|
|
8
|
+
from rsseismic.properties._PropertyAccessor import ProjectScopedPropertyAccessor
|
|
9
|
+
from rsseismic.properties.meanProfileSoilLayer._generated import MeanProfileSoilLayerPropertiesMixin
|
|
10
|
+
from rsseismic.ReferenceCurve import ReferenceCurve
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _to_vary_with_depth_points(
|
|
14
|
+
points: list[tuple[float, float]],
|
|
15
|
+
) -> list[MeanProfileSoilLayerDataService_pb2.VaryWithDepthPoint]:
|
|
16
|
+
return [
|
|
17
|
+
MeanProfileSoilLayerDataService_pb2.VaryWithDepthPoint(depth=depth, value=value)
|
|
18
|
+
for depth, value in points
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _from_vary_with_depth_points(
|
|
23
|
+
points: list[MeanProfileSoilLayerDataService_pb2.VaryWithDepthPoint],
|
|
24
|
+
) -> list[tuple[float, float]]:
|
|
25
|
+
return [(point.depth, point.value) for point in points]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MeanProfileSoilLayer(_ProxyObject, MeanProfileSoilLayerPropertiesMixin):
|
|
29
|
+
"""
|
|
30
|
+
Read and edit properties for one soil layer on the mean profile template.
|
|
31
|
+
|
|
32
|
+
Obtain an instance from ``model.MeanProfile.getSoilLayer(layerId)``. Layer IDs
|
|
33
|
+
come from ``model.MeanProfile.listSoilLayers()``.
|
|
34
|
+
|
|
35
|
+
Use the generated ``get*`` / ``set*`` helpers for scalar layer properties. When
|
|
36
|
+
shear wave velocity or shear strength varies with depth, set the variation mode
|
|
37
|
+
first, then use ``getShearWaveValues`` / ``setShearWaveValues`` or
|
|
38
|
+
``getShearStrengthValues`` / ``setShearStrengthValues`` for the depth table.
|
|
39
|
+
|
|
40
|
+
Reference curves are edited through ``layer.ReferenceCurve``. Unlike regular
|
|
41
|
+
profile layers, ``ReferenceCurve.generateReferenceCurve()`` is not available here;
|
|
42
|
+
set curve properties and points on ``ReferenceCurve`` directly.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, client: Client, modelId: str, layerId: str):
|
|
46
|
+
super().__init__(client, layerId)
|
|
47
|
+
self.modelId = modelId
|
|
48
|
+
stub = MeanProfileSoilLayerDataService_pb2_grpc.MeanProfileSoilLayerDataServiceStub(
|
|
49
|
+
self._client.channel
|
|
50
|
+
)
|
|
51
|
+
self.Data = ProjectScopedPropertyAccessor(client, modelId, layerId, stub)
|
|
52
|
+
self.ReferenceCurve = ReferenceCurve(client, modelId, layerId)
|
|
53
|
+
self._service = stub
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def layerId(self) -> str:
|
|
57
|
+
return self._objectId
|
|
58
|
+
|
|
59
|
+
def getShearWaveValues(self) -> list[tuple[float, float]]:
|
|
60
|
+
"""
|
|
61
|
+
Return vary-with-depth shear wave velocity points as (depth, value) pairs.
|
|
62
|
+
|
|
63
|
+
Requires shearWaveVelocityVariation = Vary_With_Depth.
|
|
64
|
+
"""
|
|
65
|
+
request = MeanProfileSoilLayerDataService_pb2.GetShearWaveValuesRequest(
|
|
66
|
+
projectId=self.modelId,
|
|
67
|
+
layerID=self._objectId,
|
|
68
|
+
)
|
|
69
|
+
response = self._client.callFunction(self._service.GetShearWaveValues, request)
|
|
70
|
+
require_success(response, "GetShearWaveValues")
|
|
71
|
+
return _from_vary_with_depth_points(list(response.points))
|
|
72
|
+
|
|
73
|
+
def setShearWaveValues(self, points: list[tuple[float, float]]) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Set vary-with-depth shear wave velocity points from (depth, value) pairs.
|
|
76
|
+
|
|
77
|
+
Requires shearWaveVelocityVariation = Vary_With_Depth. The server requires
|
|
78
|
+
at least two points.
|
|
79
|
+
"""
|
|
80
|
+
if not points:
|
|
81
|
+
raise ValueError("At least one vary-with-depth point is required.")
|
|
82
|
+
request = MeanProfileSoilLayerDataService_pb2.SetShearWaveValuesRequest(
|
|
83
|
+
projectId=self.modelId,
|
|
84
|
+
layerID=self._objectId,
|
|
85
|
+
points=_to_vary_with_depth_points(points),
|
|
86
|
+
)
|
|
87
|
+
response = self._client.callFunction(self._service.SetShearWaveValues, request)
|
|
88
|
+
require_success(response, "SetShearWaveValues")
|
|
89
|
+
|
|
90
|
+
def getShearStrengthValues(self) -> list[tuple[float, float]]:
|
|
91
|
+
"""
|
|
92
|
+
Return vary-with-depth shear strength points as (depth, value) pairs.
|
|
93
|
+
|
|
94
|
+
Requires shearStrengthVariation = Vary_With_Depth.
|
|
95
|
+
"""
|
|
96
|
+
request = MeanProfileSoilLayerDataService_pb2.GetShearStrengthValuesRequest(
|
|
97
|
+
projectId=self.modelId,
|
|
98
|
+
layerID=self._objectId,
|
|
99
|
+
)
|
|
100
|
+
response = self._client.callFunction(self._service.GetShearStrengthValues, request)
|
|
101
|
+
require_success(response, "GetShearStrengthValues")
|
|
102
|
+
return _from_vary_with_depth_points(list(response.points))
|
|
103
|
+
|
|
104
|
+
def setShearStrengthValues(self, points: list[tuple[float, float]]) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Set vary-with-depth shear strength points from (depth, value) pairs.
|
|
107
|
+
|
|
108
|
+
Requires shearStrengthVariation = Vary_With_Depth. The server requires
|
|
109
|
+
at least two points.
|
|
110
|
+
"""
|
|
111
|
+
if not points:
|
|
112
|
+
raise ValueError("At least one vary-with-depth point is required.")
|
|
113
|
+
request = MeanProfileSoilLayerDataService_pb2.SetShearStrengthValuesRequest(
|
|
114
|
+
projectId=self.modelId,
|
|
115
|
+
layerID=self._objectId,
|
|
116
|
+
points=_to_vary_with_depth_points(points),
|
|
117
|
+
)
|
|
118
|
+
response = self._client.callFunction(self._service.SetShearStrengthValues, request)
|
|
119
|
+
require_success(response, "SetShearStrengthValues")
|