osis-python 0.5.0__tar.gz → 0.5.2__tar.gz
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.
- {osis_python-0.5.0/src/osis_python.egg-info → osis_python-0.5.2}/PKG-INFO +1 -1
- {osis_python-0.5.0 → osis_python-0.5.2/src/osis_python.egg-info}/PKG-INFO +1 -1
- {osis_python-0.5.0 → osis_python-0.5.2}/src/osis_python.egg-info/SOURCES.txt +4 -1
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/__init__.py +1 -1
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/boundary/manager.py +13 -1
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/__init__.py +2 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/apdl_sync.py +53 -11
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/client.py +11 -2
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/command.py +11 -2
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/engine.py +8 -4
- osis_python-0.5.2/src/pyosis/core/exceptions.py +32 -0
- osis_python-0.5.2/src/pyosis/core/references.py +87 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/sync_apdl_cli.py +7 -19
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/dynamic/manager.py +2 -2
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/element/manager.py +13 -1
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/general/interface.py +15 -19
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/geometry/manager.py +8 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/live/manager.py +15 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/load/manager.py +34 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/load/tendon.py +18 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/material/manager.py +8 -1
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/node/manager.py +8 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/manager.py +49 -4
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/manager.py +36 -7
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/param.py +17 -8
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/thickness/manager.py +8 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/transfer/generator.py +3 -2
- osis_python-0.5.2/src/pyosis/transfer/out_to_python.py +246 -0
- osis_python-0.5.2/tests/test_delete_occupied.py +432 -0
- osis_python-0.5.0/src/pyosis/transfer/sync_exec.py +0 -256
- {osis_python-0.5.0 → osis_python-0.5.2}/LICENSE +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/README.md +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/pyproject.toml +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/setup.cfg +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/osis_python.egg-info/dependency_links.txt +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/osis_python.egg-info/requires.txt +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/osis_python.egg-info/top_level.txt +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/boundary/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/boundary/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/control/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/control/control.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/control/io.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/control/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/control/nonlinear.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/all_func.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/basic_manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/core/build.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/dynamic/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/dynamic/load_to_mass.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/dynamic/modal.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/dynamic/seismic.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/element/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/element/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/general/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/geometry/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/geometry/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/live/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/live/analysis.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/live/grade.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/live/lane.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/load/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/load/loadcase.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/load/static.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/material/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/material/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/node/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/node/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/post/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/post/combine.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/post/design_check.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/post/display.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/post/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/project/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/project/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/project/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/component_thickness.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/coordinate.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/creep_shrink.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/damping.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/property/pu_curve.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/quick_building/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/quick_building/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/result/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/result/check.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/result/env.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/result/loadcase.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/result/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/result/result.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/common.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/composite.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/numeric.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/rebar.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/rib.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/section/steel.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/settlement/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/settlement/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/settlement/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stability/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stability/buckling.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stability/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stage/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stage/define.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stage/manager.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/stage/overall.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/thickness/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/thickness/interface.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/transfer/__init__.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/transfer/matrix.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/transfer/parser.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/transfer/routes.py +0 -0
- {osis_python-0.5.0 → osis_python-0.5.2}/src/pyosis/transfer/split.py +0 -0
|
@@ -23,6 +23,8 @@ src/pyosis/core/build.py
|
|
|
23
23
|
src/pyosis/core/client.py
|
|
24
24
|
src/pyosis/core/command.py
|
|
25
25
|
src/pyosis/core/engine.py
|
|
26
|
+
src/pyosis/core/exceptions.py
|
|
27
|
+
src/pyosis/core/references.py
|
|
26
28
|
src/pyosis/core/sync_apdl_cli.py
|
|
27
29
|
src/pyosis/dynamic/__init__.py
|
|
28
30
|
src/pyosis/dynamic/load_to_mass.py
|
|
@@ -101,7 +103,8 @@ src/pyosis/thickness/manager.py
|
|
|
101
103
|
src/pyosis/transfer/__init__.py
|
|
102
104
|
src/pyosis/transfer/generator.py
|
|
103
105
|
src/pyosis/transfer/matrix.py
|
|
106
|
+
src/pyosis/transfer/out_to_python.py
|
|
104
107
|
src/pyosis/transfer/parser.py
|
|
105
108
|
src/pyosis/transfer/routes.py
|
|
106
109
|
src/pyosis/transfer/split.py
|
|
107
|
-
|
|
110
|
+
tests/test_delete_occupied.py
|
|
@@ -7,7 +7,7 @@ A library for extending OSIS functionality.
|
|
|
7
7
|
A library that allows users to use CAE with great flexibility.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
__version__ = "0.5.
|
|
10
|
+
__version__ = "0.5.2"
|
|
11
11
|
__author__ = "CCCC Highway Consultant Co. Ltd." # 中交公路规划设计院 - 工程技术研究中心 - lzh - wjh - myf
|
|
12
12
|
|
|
13
13
|
from .core.engine import OSISEngine
|
|
@@ -23,6 +23,7 @@ from typing import Any, Literal
|
|
|
23
23
|
from enum import IntEnum
|
|
24
24
|
|
|
25
25
|
from ..core.client import osis_client
|
|
26
|
+
from ..core import get_references, raise_if_occupied
|
|
26
27
|
from .interface import (
|
|
27
28
|
osis_boundary_general,
|
|
28
29
|
osis_boundary_elstcspt,
|
|
@@ -638,6 +639,10 @@ class BoundaryManager:
|
|
|
638
639
|
return 1
|
|
639
640
|
return max(bd.no for bd in boundaries) + 1
|
|
640
641
|
|
|
642
|
+
def get_dependencies(self, no: int) -> dict[str, list]:
|
|
643
|
+
"""查询边界被谁引用"""
|
|
644
|
+
return get_references("Boundary", no=no)
|
|
645
|
+
|
|
641
646
|
# ── 增删改 ────────────────────────────────
|
|
642
647
|
def create(self, no: int | None, type: str, *args: Any, **kwargs: Any) -> Boundary:
|
|
643
648
|
"""创建边界(便捷入口,内部转发到对应 create_* 方法)
|
|
@@ -846,7 +851,14 @@ class BoundaryManager:
|
|
|
846
851
|
return self.get(no) # type: ignore[return-value]
|
|
847
852
|
|
|
848
853
|
def delete(self, no: int) -> None:
|
|
849
|
-
"""删除边界
|
|
854
|
+
"""删除边界
|
|
855
|
+
|
|
856
|
+
Raises:
|
|
857
|
+
DependencyError: 存在依赖项时
|
|
858
|
+
RuntimeError: 删除失败时抛出异常
|
|
859
|
+
"""
|
|
860
|
+
deps = self.get_dependencies(no)
|
|
861
|
+
raise_if_occupied("Boundary", deps, no=no)
|
|
850
862
|
ok, err = osis_boundary_del(no)
|
|
851
863
|
if not ok:
|
|
852
864
|
raise RuntimeError(f"删除边界 {no} 失败: {err}")
|
|
@@ -21,17 +21,24 @@ from dataclasses import dataclass
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import TYPE_CHECKING
|
|
23
23
|
|
|
24
|
+
from ..transfer import parse_text
|
|
25
|
+
from ..transfer.out_to_python import write_prep_outputs
|
|
26
|
+
|
|
24
27
|
if TYPE_CHECKING:
|
|
25
28
|
from .engine import OSISEngine
|
|
26
29
|
|
|
27
30
|
SYNC_STATE_FILE = ".apdl_sync.json"
|
|
28
31
|
|
|
32
|
+
# 默认 .out 文件名(位于项目根目录)
|
|
33
|
+
DEFAULT_EXPORT_NAME = "_pyosis_sync.out"
|
|
34
|
+
|
|
29
35
|
# 忽略 TIME: 行,避免时间戳导致 MD5 每次不同
|
|
30
36
|
_TIME_LINE_PATTERN = re.compile(
|
|
31
37
|
r"^//-+ TIME:.*//\s*$",
|
|
32
38
|
re.MULTILINE | re.IGNORECASE,
|
|
33
39
|
)
|
|
34
40
|
|
|
41
|
+
|
|
35
42
|
@dataclass
|
|
36
43
|
class ApdlSyncResult:
|
|
37
44
|
"""APDL 同步结果。"""
|
|
@@ -107,6 +114,49 @@ def _save_persisted_hash(state_path: Path, out_path: Path, file_hash: str) -> No
|
|
|
107
114
|
)
|
|
108
115
|
|
|
109
116
|
|
|
117
|
+
# 解析项目目录
|
|
118
|
+
def _get_project_dir(engine: "OSISEngine") -> Path | None:
|
|
119
|
+
try:
|
|
120
|
+
proj_dir = engine.project.get_directory()
|
|
121
|
+
except RuntimeError:
|
|
122
|
+
return None
|
|
123
|
+
if not proj_dir:
|
|
124
|
+
return None
|
|
125
|
+
return Path(proj_dir)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# 解析 (out_path, prep_dir)
|
|
129
|
+
def _resolve_project_paths(engine: "OSISEngine") -> tuple[Path, Path]:
|
|
130
|
+
"""返回 (默认 out_path, prep_dir)。prep_dir 不存在会自动创建。"""
|
|
131
|
+
proj_dir = _get_project_dir(engine)
|
|
132
|
+
if proj_dir is None:
|
|
133
|
+
raise RuntimeError("无法获取 OSIS 项目目录")
|
|
134
|
+
prep_dir = proj_dir / "py" / "prep"
|
|
135
|
+
prep_dir.mkdir(parents=True, exist_ok=True)
|
|
136
|
+
return proj_dir / DEFAULT_EXPORT_NAME, prep_dir
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# 确保 .out 文件存在
|
|
140
|
+
def _ensure_out_file(
|
|
141
|
+
engine: "OSISEngine",
|
|
142
|
+
out_path: Path,
|
|
143
|
+
*,
|
|
144
|
+
force_export: bool,
|
|
145
|
+
) -> Path:
|
|
146
|
+
"""确保 .out 存在;不存在或 force_export 时调用 engine.export_apdl。"""
|
|
147
|
+
if out_path.is_file() and not force_export:
|
|
148
|
+
return out_path
|
|
149
|
+
|
|
150
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
151
|
+
print(f"执行 export_apdl → {out_path}")
|
|
152
|
+
engine.export_apdl(str(out_path))
|
|
153
|
+
|
|
154
|
+
if not out_path.is_file() or out_path.stat().st_size == 0:
|
|
155
|
+
raise FileNotFoundError(f"导出失败或未生成有效 .out 文件: {out_path}")
|
|
156
|
+
|
|
157
|
+
return out_path
|
|
158
|
+
|
|
159
|
+
|
|
110
160
|
# 执行 APDL 同步
|
|
111
161
|
def perform_apdl_sync(
|
|
112
162
|
engine: "OSISEngine",
|
|
@@ -118,22 +168,14 @@ def perform_apdl_sync(
|
|
|
118
168
|
|
|
119
169
|
无项目路径时返回 None。
|
|
120
170
|
"""
|
|
121
|
-
|
|
122
|
-
from ..transfer.sync_exec import (
|
|
123
|
-
get_project_dir_optional,
|
|
124
|
-
resolve_out_file,
|
|
125
|
-
resolve_project_paths,
|
|
126
|
-
write_prep_outputs,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
if get_project_dir_optional(engine) is None:
|
|
171
|
+
if _get_project_dir(engine) is None:
|
|
130
172
|
return None
|
|
131
173
|
|
|
132
174
|
session: ApdlSessionStore = engine._apdl_session
|
|
133
175
|
|
|
134
|
-
default_out, prep_dir =
|
|
176
|
+
default_out, prep_dir = _resolve_project_paths(engine)
|
|
135
177
|
out_path = Path(path) if path is not None else default_out
|
|
136
|
-
out_path =
|
|
178
|
+
out_path = _ensure_out_file(engine, out_path, force_export=force_export)
|
|
137
179
|
|
|
138
180
|
text = _read_out_text(out_path)
|
|
139
181
|
file_hash = _text_hash(text)
|
|
@@ -26,6 +26,8 @@ session.mount("https://", adapter) # 如果你后续用HTTPS
|
|
|
26
26
|
|
|
27
27
|
DEFAULT_PORT = 18080
|
|
28
28
|
DEFAULT_URL = "http://localhost"
|
|
29
|
+
DEFAULT_TIMEOUT = 300
|
|
30
|
+
DEFAULT_SOLVE_TIMEOUT = 600
|
|
29
31
|
|
|
30
32
|
def set_osis_url(url):
|
|
31
33
|
'''
|
|
@@ -42,12 +44,19 @@ def set_osis_port(port):
|
|
|
42
44
|
DEFAULT_PORT = port
|
|
43
45
|
|
|
44
46
|
# ========== 通用客户端函数(复用连接池) ==========
|
|
45
|
-
def osis_client(
|
|
47
|
+
def osis_client(
|
|
48
|
+
func_name: str,
|
|
49
|
+
payload: dict,
|
|
50
|
+
base_url=None,
|
|
51
|
+
timeout: float | None = None,
|
|
52
|
+
) -> dict:
|
|
46
53
|
"""
|
|
47
54
|
通用 OSIS 接口客户端(复用连接池,高效批量调用)
|
|
48
55
|
Args:
|
|
49
56
|
func_name: C++ 接口名(如 "OSIS_Run")
|
|
50
57
|
payload: 请求参数字典(自动序列化为 JSON)
|
|
58
|
+
base_url: url
|
|
59
|
+
timeout: HTTP 超时秒数,默认 DEFAULT_TIMEOUT
|
|
51
60
|
Returns:
|
|
52
61
|
异常时返回 (False, 错误信息)
|
|
53
62
|
"""
|
|
@@ -59,7 +68,7 @@ def osis_client(func_name: str, payload: dict, base_url=None) -> dict:
|
|
|
59
68
|
url,
|
|
60
69
|
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
|
|
61
70
|
headers={"Content-Type": "application/json; charset=utf-8"},
|
|
62
|
-
timeout=
|
|
71
|
+
timeout=timeout if timeout is not None else DEFAULT_TIMEOUT,
|
|
63
72
|
)
|
|
64
73
|
|
|
65
74
|
data = None
|
|
@@ -19,7 +19,11 @@ from .client import osis_client
|
|
|
19
19
|
# f.write(f"[{timestamp}] {text}\n")
|
|
20
20
|
# f.close()
|
|
21
21
|
|
|
22
|
-
def osis_run(
|
|
22
|
+
def osis_run(
|
|
23
|
+
strCmd: str = "",
|
|
24
|
+
mode: Literal["stash", "exec"] = "exec",
|
|
25
|
+
timeout: float | None = None,
|
|
26
|
+
) -> tuple[bool, str]:
|
|
23
27
|
"""
|
|
24
28
|
http 模拟 C++ 原生 OSIS_Run 函数
|
|
25
29
|
Args:
|
|
@@ -27,11 +31,16 @@ def osis_run(strCmd: str="", mode: Literal["stash", "exec"]="exec") -> tuple[boo
|
|
|
27
31
|
mode: 运行模式,此参数为了同时执行多条命令提高效率
|
|
28
32
|
* 使用 stash 仅会将命令流存到OSIS中,不会执行
|
|
29
33
|
* 收到 exec 信号才会执行暂存包括当前的所有命令流。
|
|
34
|
+
timeout: HTTP 超时秒数,默认由 osis_client 决定
|
|
30
35
|
|
|
31
36
|
Returns:
|
|
32
37
|
tuple (bool, str): 是否成功,失败原因
|
|
33
38
|
"""
|
|
34
|
-
response = osis_client(
|
|
39
|
+
response = osis_client(
|
|
40
|
+
"OSIS_Run",
|
|
41
|
+
{"strCmd": strCmd, "mode": mode},
|
|
42
|
+
timeout=timeout,
|
|
43
|
+
)
|
|
35
44
|
return response['success'], response['error']
|
|
36
45
|
|
|
37
46
|
def _log(text, log_dir="pyosis_logs"):
|
|
@@ -69,7 +69,7 @@ class OSISEngine:
|
|
|
69
69
|
- tendon: 钢束管理器(含 prop/shape 子管理器)
|
|
70
70
|
- live: 活载管理器(含 grade/lane/case 子管理器)
|
|
71
71
|
- geometry: 几何管理器(三维样条曲线)
|
|
72
|
-
- property
|
|
72
|
+
- prop: 属性管理器(property 坐标系、收缩徐变、阻尼等)
|
|
73
73
|
- thickness: 厚度管理器(壳厚度特性)
|
|
74
74
|
- control: 控制管理器(全局参数、分析设置)
|
|
75
75
|
- project: 项目管理器(创建、打开、保存项目)
|
|
@@ -246,9 +246,13 @@ class OSISEngine:
|
|
|
246
246
|
if not ok:
|
|
247
247
|
raise RuntimeError(f"执行失败: {err}")
|
|
248
248
|
|
|
249
|
-
def solve(self) -> None:
|
|
250
|
-
"""求解工程
|
|
251
|
-
|
|
249
|
+
def solve(self, timeout: float | None = None) -> None:
|
|
250
|
+
"""求解工程
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
timeout: HTTP 超时秒数,默认 600(10 分钟)
|
|
254
|
+
"""
|
|
255
|
+
ok, err = osis_solve(timeout=timeout)
|
|
252
256
|
if not ok:
|
|
253
257
|
raise RuntimeError(f"求解失败: {err}")
|
|
254
258
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyosis 自定义异常
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
class DependencyError(RuntimeError):
|
|
6
|
+
"""删除实体时存在依赖项
|
|
7
|
+
|
|
8
|
+
Attributes:
|
|
9
|
+
entity_type: 实体类型字符串,如 "Material"
|
|
10
|
+
id_value: 实体编号(int)或名称(str)
|
|
11
|
+
no: 按编号寻址时的编号,否则为 None
|
|
12
|
+
name: 按名称寻址时的名称,否则为 None
|
|
13
|
+
dependencies: 依赖关系字典
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, entity_type: str, id_value: int | str, dependencies: dict[str, list]):
|
|
16
|
+
self.entity_type = entity_type
|
|
17
|
+
self.id_value = id_value
|
|
18
|
+
self.dependencies = dependencies
|
|
19
|
+
if isinstance(id_value, int):
|
|
20
|
+
self.no = id_value
|
|
21
|
+
self.name = None
|
|
22
|
+
label = f"#{id_value}"
|
|
23
|
+
else:
|
|
24
|
+
self.no = None
|
|
25
|
+
self.name = id_value
|
|
26
|
+
label = id_value
|
|
27
|
+
|
|
28
|
+
lines = [f"无法删除 {entity_type} {label},存在以下依赖:"]
|
|
29
|
+
for kind, refs in dependencies.items():
|
|
30
|
+
if refs:
|
|
31
|
+
lines.append(f" - {kind}: {refs}")
|
|
32
|
+
super().__init__("\n".join(lines))
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
通用引用查询客户端封装
|
|
3
|
+
|
|
4
|
+
后端 C++ 暴露 /GetReferences 接口,根据 entityType 字符串分派
|
|
5
|
+
到对应 Info 类的 getRelated* 方法。
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
from typing import Literal
|
|
9
|
+
from .client import osis_client
|
|
10
|
+
from .exceptions import DependencyError
|
|
11
|
+
|
|
12
|
+
# 支持的实体类型(与 C++ GetReferences.cpp 中的 if-else 分支对齐)
|
|
13
|
+
EntityType = Literal[
|
|
14
|
+
"Material", "Section", "Node", "Element", "Boundary",
|
|
15
|
+
"TendonShape", "TendonProp", "LoadCase", "LiveGrade", "LiveLane",
|
|
16
|
+
"CoorSys", "CreepShrink", "PUCurve", "ShellThickness",
|
|
17
|
+
"Damping", "Spline",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
通用引用查询客户端封装
|
|
23
|
+
后端 /GetReferences 统一字符串协议:{entityType, idKey("no"|"name"), idValue}
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_references(entity_type: str, *, no: int | None = None, name: str | None = None) -> dict[str, list]:
|
|
29
|
+
"""查询某个实体被哪些其他实体引用
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
entity_type: 实体类型,如 "Material" / "Node" / "TendonShape" / "LoadCase"
|
|
33
|
+
no: 按编号寻址时传(Material/Section/Node/Element)
|
|
34
|
+
name: 按名称寻址时传(TendonShape/LoadCase/...)
|
|
35
|
+
no 与 name 二选一。
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
引用关系字典,如 {"elements":[101,102], "loads":["自重"], ...}
|
|
39
|
+
无依赖时返回空字典。
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: no/name 都没传,或都传了
|
|
43
|
+
RuntimeError: 查询失败
|
|
44
|
+
"""
|
|
45
|
+
if no is not None and name is not None:
|
|
46
|
+
raise ValueError("no 与 name 只能传一个")
|
|
47
|
+
if no is None and name is None:
|
|
48
|
+
raise ValueError("必须提供 no 或 name")
|
|
49
|
+
|
|
50
|
+
if no is not None:
|
|
51
|
+
id_key, id_value = "no", str(no)
|
|
52
|
+
else:
|
|
53
|
+
id_key, id_value = "name", name
|
|
54
|
+
|
|
55
|
+
resp = osis_client("GetReferences", {
|
|
56
|
+
"entityType": entity_type,
|
|
57
|
+
"idKey": id_key,
|
|
58
|
+
"idValue": id_value,
|
|
59
|
+
})
|
|
60
|
+
if not resp.get("success"):
|
|
61
|
+
raise RuntimeError(f"查询 {entity_type}({id_key}={id_value}) 引用失败: {resp.get('error')}")
|
|
62
|
+
return resp.get("data") or {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def raise_if_occupied(
|
|
66
|
+
entity_type: str,
|
|
67
|
+
deps: dict[str, list],
|
|
68
|
+
*,
|
|
69
|
+
no: int | None = None,
|
|
70
|
+
name: str | None = None,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""存在非空依赖时抛出 DependencyError。
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
entity_type: 实体类型,如 "Material" / "TendonShape"
|
|
76
|
+
deps: get_references / get_dependencies 返回的依赖字典
|
|
77
|
+
no: 按编号寻址时传
|
|
78
|
+
name: 按名称寻址时传
|
|
79
|
+
"""
|
|
80
|
+
non_empty = {k: v for k, v in deps.items() if v}
|
|
81
|
+
if not non_empty:
|
|
82
|
+
return
|
|
83
|
+
if no is None and name is None:
|
|
84
|
+
raise ValueError("必须提供 no 或 name")
|
|
85
|
+
id_value = no if no is not None else name
|
|
86
|
+
raise DependencyError(entity_type, id_value, non_empty)
|
|
87
|
+
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"""OSIS 界面操作后触发 pyosis APDL 同步的 CLI 入口。
|
|
2
|
+
|
|
3
|
+
仅负责触发 perform_apdl_sync;prep 模块的执行不在本模块职责范围内。
|
|
4
|
+
|
|
2
5
|
用法:
|
|
3
6
|
python -m pyosis.core.sync_apdl_cli
|
|
4
7
|
退出码:
|
|
@@ -9,30 +12,15 @@
|
|
|
9
12
|
from __future__ import annotations
|
|
10
13
|
|
|
11
14
|
import sys
|
|
12
|
-
from typing import TYPE_CHECKING
|
|
13
15
|
|
|
16
|
+
from .apdl_sync import perform_apdl_sync
|
|
14
17
|
from .engine import OSISEngine
|
|
15
18
|
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
from .apdl_sync import ApdlSyncResult
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _print_result(result: ApdlSyncResult) -> None:
|
|
21
|
-
|
|
22
|
-
if result.changed:
|
|
23
|
-
print("命令流有变化,已重新生成并执行 prep")
|
|
24
|
-
else:
|
|
25
|
-
print("命令流未变化,已 export,未重新执行 prep")
|
|
26
|
-
|
|
27
|
-
summary = result.summary()
|
|
28
|
-
if summary:
|
|
29
|
-
print(f"摘要: {summary}")
|
|
30
|
-
|
|
31
|
-
|
|
32
19
|
|
|
33
20
|
def main() -> int:
|
|
21
|
+
engine = OSISEngine()
|
|
34
22
|
try:
|
|
35
|
-
result =
|
|
23
|
+
result = perform_apdl_sync(engine)
|
|
36
24
|
except Exception as exc:
|
|
37
25
|
print(f"sync_apdl 失败: {exc}", file=sys.stderr)
|
|
38
26
|
return 1
|
|
@@ -41,7 +29,7 @@ def main() -> int:
|
|
|
41
29
|
print("跳过:未获取到项目路径")
|
|
42
30
|
return 0
|
|
43
31
|
|
|
44
|
-
|
|
32
|
+
print(result.summary())
|
|
45
33
|
return 0
|
|
46
34
|
|
|
47
35
|
|
|
@@ -579,7 +579,7 @@ class RspecAnalManager:
|
|
|
579
579
|
|
|
580
580
|
def _load(self) -> list[RspecAnal]:
|
|
581
581
|
"""从服务端加载所有反应谱工况信息"""
|
|
582
|
-
resp = osis_client("
|
|
582
|
+
resp = osis_client("GetAllRespSpecInfo", {})
|
|
583
583
|
if not resp['success']:
|
|
584
584
|
raise RuntimeError(f"{resp['error']}")
|
|
585
585
|
rsp = [RspecAnal._from_dict(d) for d in resp.get("data", [])]
|
|
@@ -594,7 +594,7 @@ class RspecAnalManager:
|
|
|
594
594
|
names = [str(name)]
|
|
595
595
|
if not isinstance(names, list):
|
|
596
596
|
raise TypeError(f"不支持的名称类型: {type(name)}")
|
|
597
|
-
resp = osis_client("
|
|
597
|
+
resp = osis_client("GetRespSpecInfoByNames", {"name": names})
|
|
598
598
|
if not resp['success']:
|
|
599
599
|
raise RuntimeError(f"{resp['error']}")
|
|
600
600
|
rsp = [RspecAnal._from_dict(d) if d else None for d in resp.get("data", [])]
|
|
@@ -15,6 +15,7 @@ from dataclasses import dataclass, field
|
|
|
15
15
|
from typing import Any, Literal
|
|
16
16
|
from enum import Enum
|
|
17
17
|
from ..core.client import osis_client
|
|
18
|
+
from ..core import get_references, raise_if_occupied
|
|
18
19
|
from .interface import (
|
|
19
20
|
osis_element_beam3d,
|
|
20
21
|
osis_element_truss,
|
|
@@ -555,6 +556,10 @@ class ElementManager:
|
|
|
555
556
|
ele_no = [ele.no for ele in elements]
|
|
556
557
|
return max(ele_no) + 1
|
|
557
558
|
|
|
559
|
+
def get_dependencies(self, no: int) -> dict[str, list]:
|
|
560
|
+
"""查询单元被谁引用"""
|
|
561
|
+
return get_references("Element", no=no)
|
|
562
|
+
|
|
558
563
|
# ── 增删改 ────────────────────────────────
|
|
559
564
|
def create(self, no: int | None, type: str, *args: Any, **kwargs: Any) -> Element:
|
|
560
565
|
"""创建单元(便捷入口,内部转发到对应 create_* 方法)
|
|
@@ -699,7 +704,14 @@ class ElementManager:
|
|
|
699
704
|
return self.get(no)
|
|
700
705
|
|
|
701
706
|
def delete(self, no: int) -> None:
|
|
702
|
-
"""删除单元
|
|
707
|
+
"""删除单元
|
|
708
|
+
|
|
709
|
+
Raises:
|
|
710
|
+
DependencyError: 存在依赖项时
|
|
711
|
+
RuntimeError: 删除失败时抛出异常
|
|
712
|
+
"""
|
|
713
|
+
deps = self.get_dependencies(no)
|
|
714
|
+
raise_if_occupied("Element", deps, no=no)
|
|
703
715
|
ok, err = osis_element_del(no)
|
|
704
716
|
if not ok:
|
|
705
717
|
raise RuntimeError(f"删除单元 {no} 失败: {err}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Any, Dict, Literal, List, Union, Sequence
|
|
2
2
|
from ..core import REGISTRY, osis_run
|
|
3
|
-
from ..core.client import osis_client
|
|
3
|
+
from ..core.client import DEFAULT_SOLVE_TIMEOUT, osis_client
|
|
4
4
|
|
|
5
5
|
def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
|
|
6
6
|
"""
|
|
@@ -11,7 +11,7 @@ def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
|
|
|
11
11
|
matrix_data: 任意维度的列表(1维/2维/3维/...)或单个数值(0维)
|
|
12
12
|
|
|
13
13
|
Returns:
|
|
14
|
-
|
|
14
|
+
tuple (bool, str): 是否成功,失败原因
|
|
15
15
|
|
|
16
16
|
Example:
|
|
17
17
|
>>> # 2维矩阵示例
|
|
@@ -92,14 +92,10 @@ def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
|
|
|
92
92
|
new_indices = indices + [idx]
|
|
93
93
|
generate_assignments(elem, new_indices)
|
|
94
94
|
else:
|
|
95
|
-
# 处理数值赋值
|
|
96
95
|
value = data
|
|
97
|
-
|
|
98
|
-
# 格式化数值
|
|
99
|
-
if isinstance(value, int) or isinstance(value, float):
|
|
96
|
+
if isinstance(value, (int, float)):
|
|
100
97
|
val_str = str(value)
|
|
101
98
|
else:
|
|
102
|
-
# 其他类型尝试转为string
|
|
103
99
|
val_str = f"\"{value}\""
|
|
104
100
|
idx_parts = list(indices)
|
|
105
101
|
if not is_charn:
|
|
@@ -107,17 +103,11 @@ def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
|
|
|
107
103
|
idx_parts.append(0)
|
|
108
104
|
idx_str = ",".join(map(str, idx_parts))
|
|
109
105
|
assign_cmd = f"{matrix_name}[{idx_str}] = {val_str}"
|
|
110
|
-
# _log(assign_cmd)
|
|
111
|
-
# osis_run(assign_cmd, "stash")
|
|
112
106
|
all_cmds.append(assign_cmd)
|
|
113
|
-
|
|
114
|
-
# 开始递归生成赋值语句(OSIS索引从0开始)
|
|
107
|
+
|
|
115
108
|
generate_assignments(matrix_data, [])
|
|
116
|
-
|
|
117
|
-
# 返回拼接后的完整命令字符串
|
|
118
|
-
# str_cmds = "\n".join(all_cmds)
|
|
109
|
+
|
|
119
110
|
str_cmds = ";".join(all_cmds)
|
|
120
|
-
# _log(str_cmds)
|
|
121
111
|
return osis_run(str_cmds, "exec")
|
|
122
112
|
|
|
123
113
|
def output_result_for_calc_book():
|
|
@@ -154,12 +144,18 @@ def osis_clc():
|
|
|
154
144
|
"""
|
|
155
145
|
pass
|
|
156
146
|
|
|
157
|
-
|
|
158
|
-
def osis_solve():
|
|
147
|
+
def osis_solve(timeout: float | None = None):
|
|
159
148
|
"""
|
|
160
149
|
求解工程
|
|
161
|
-
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
timeout: HTTP 超时秒数,默认 600(10 分钟)
|
|
153
|
+
|
|
162
154
|
Returns:
|
|
163
155
|
tuple (bool, str): 是否成功,失败原因
|
|
164
156
|
"""
|
|
165
|
-
|
|
157
|
+
return osis_run(
|
|
158
|
+
"Solve;",
|
|
159
|
+
"exec",
|
|
160
|
+
timeout=timeout if timeout is not None else DEFAULT_SOLVE_TIMEOUT,
|
|
161
|
+
)
|
|
@@ -19,6 +19,7 @@ from enum import Enum
|
|
|
19
19
|
from typing import Literal, Any
|
|
20
20
|
|
|
21
21
|
from ..core.client import osis_client
|
|
22
|
+
from ..core import get_references, raise_if_occupied
|
|
22
23
|
from .interface import (
|
|
23
24
|
osis_spline3d_general,
|
|
24
25
|
osis_spline3d_natural,
|
|
@@ -286,6 +287,10 @@ class GeometryManager:
|
|
|
286
287
|
raise RuntimeError(f"创建3D圆弧 {name} 失败: {err}")
|
|
287
288
|
return self.get(name)
|
|
288
289
|
|
|
290
|
+
def get_dependencies(self, name: str) -> dict[str, list]:
|
|
291
|
+
"""查询几何样条被谁引用"""
|
|
292
|
+
return get_references("Spline", name=name)
|
|
293
|
+
|
|
289
294
|
def delete(self, name: str) -> None:
|
|
290
295
|
"""删除三维样条曲线
|
|
291
296
|
|
|
@@ -293,8 +298,11 @@ class GeometryManager:
|
|
|
293
298
|
name: 曲线名称
|
|
294
299
|
|
|
295
300
|
Raises:
|
|
301
|
+
DependencyError: 存在依赖项时
|
|
296
302
|
RuntimeError: 删除失败时抛出异常
|
|
297
303
|
"""
|
|
304
|
+
deps = self.get_dependencies(name)
|
|
305
|
+
raise_if_occupied("Spline", deps, name=name)
|
|
298
306
|
ok, err = osis_spline3d_del(name)
|
|
299
307
|
if not ok:
|
|
300
308
|
raise RuntimeError(f"删除样条曲线 {name} 失败: {err}")
|
|
@@ -17,6 +17,7 @@ from dataclasses import dataclass, field
|
|
|
17
17
|
from typing import Literal, Any
|
|
18
18
|
|
|
19
19
|
from ..core.client import osis_client
|
|
20
|
+
from ..core import get_references, raise_if_occupied
|
|
20
21
|
from .grade import (
|
|
21
22
|
osis_livegrade_highway,
|
|
22
23
|
osis_livegrade_vehicle,
|
|
@@ -499,6 +500,10 @@ class LiveGradeManager:
|
|
|
499
500
|
]
|
|
500
501
|
return grades
|
|
501
502
|
|
|
503
|
+
def get_dependencies(self, name: str) -> dict[str, list]:
|
|
504
|
+
"""查询活载等级被谁引用"""
|
|
505
|
+
return get_references("LiveGrade", name=name)
|
|
506
|
+
|
|
502
507
|
def create(
|
|
503
508
|
self,
|
|
504
509
|
name: str,
|
|
@@ -713,8 +718,11 @@ class LiveGradeManager:
|
|
|
713
718
|
name: 活载等级名称
|
|
714
719
|
|
|
715
720
|
Raises:
|
|
721
|
+
DependencyError: 存在依赖项时
|
|
716
722
|
RuntimeError: 删除失败时抛出异常
|
|
717
723
|
"""
|
|
724
|
+
deps = self.get_dependencies(name)
|
|
725
|
+
raise_if_occupied("LiveGrade", deps, name=name)
|
|
718
726
|
ok, err = osis_livegrade_del(name)
|
|
719
727
|
if not ok:
|
|
720
728
|
raise RuntimeError(f"删除活载等级 {name} 失败: {err}")
|
|
@@ -815,6 +823,10 @@ class LaneManager:
|
|
|
815
823
|
]
|
|
816
824
|
return lanes
|
|
817
825
|
|
|
826
|
+
def get_dependencies(self, name: str) -> dict[str, list]:
|
|
827
|
+
"""查询车道被谁引用"""
|
|
828
|
+
return get_references("LiveLane", name=name)
|
|
829
|
+
|
|
818
830
|
def create(
|
|
819
831
|
self,
|
|
820
832
|
name: str,
|
|
@@ -966,8 +978,11 @@ class LaneManager:
|
|
|
966
978
|
name: 车道名称
|
|
967
979
|
|
|
968
980
|
Raises:
|
|
981
|
+
DependencyError: 存在依赖项时
|
|
969
982
|
RuntimeError: 删除失败时抛出异常
|
|
970
983
|
"""
|
|
984
|
+
deps = self.get_dependencies(name)
|
|
985
|
+
raise_if_occupied("LiveLane", deps, name=name)
|
|
971
986
|
ok, err = osis_lane_del(name)
|
|
972
987
|
if not ok:
|
|
973
988
|
raise RuntimeError(f"删除车道 {name} 失败: {err}")
|