cabledyn 0.3.0__py3-none-win_amd64.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.
- cabledyn/__init__.py +298 -0
- cabledyn/_csv.py +44 -0
- cabledyn/_discovery.py +163 -0
- cabledyn/_lib.py +250 -0
- cabledyn/_optional.py +15 -0
- cabledyn/_paths.py +57 -0
- cabledyn/animation.py +750 -0
- cabledyn/bin/CableDyn_driver.exe +0 -0
- cabledyn/builder.py +3122 -0
- cabledyn/cabledyn.dll +0 -0
- cabledyn/checks.py +602 -0
- cabledyn/clearance.py +1138 -0
- cabledyn/cli.py +37 -0
- cabledyn/compare.py +450 -0
- cabledyn/curves.py +566 -0
- cabledyn/damage.py +772 -0
- cabledyn/deck.py +518 -0
- cabledyn/deck_cli.py +146 -0
- cabledyn/deck_file.py +4804 -0
- cabledyn/driver.py +652 -0
- cabledyn/errors.py +157 -0
- cabledyn/extremes.py +484 -0
- cabledyn/fatigue.py +670 -0
- cabledyn/formats.py +698 -0
- cabledyn/geometry.py +358 -0
- cabledyn/model.py +614 -0
- cabledyn/objects.py +496 -0
- cabledyn/post_cli.py +509 -0
- cabledyn/processing.py +309 -0
- cabledyn/profiles.py +1350 -0
- cabledyn/py.typed +0 -0
- cabledyn/ranges.py +621 -0
- cabledyn/results.py +1732 -0
- cabledyn/spectra.py +1039 -0
- cabledyn/study.py +615 -0
- cabledyn/study_cli.py +59 -0
- cabledyn/summary.py +466 -0
- cabledyn/sweep.py +86 -0
- cabledyn/touchdown.py +443 -0
- cabledyn-0.3.0.dist-info/METADATA +138 -0
- cabledyn-0.3.0.dist-info/RECORD +45 -0
- cabledyn-0.3.0.dist-info/WHEEL +5 -0
- cabledyn-0.3.0.dist-info/entry_points.txt +6 -0
- cabledyn-0.3.0.dist-info/licenses/LICENSE +201 -0
- cabledyn-0.3.0.dist-info/licenses/NOTICE +117 -0
cabledyn/__init__.py
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Python interfaces for the CableDyn cable and mooring dynamics solver.
|
|
3
|
+
|
|
4
|
+
The standalone-driver API works with the statically linked release executable.
|
|
5
|
+
The in-process :class:`CableDyn` class, and the ``abi_version``, ``abi_minor``,
|
|
6
|
+
``library_path``, and ``version_string`` helpers, load the CableDyn shared
|
|
7
|
+
library on first access. They are deliberately left out of ``__all__`` so that
|
|
8
|
+
``from cabledyn import *`` works in installations without the shared library.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
|
|
15
|
+
from cabledyn.animation import Recorder, Seabed, Snapshots, WaterSurface, animate
|
|
16
|
+
from cabledyn.builder import DeckModel, DeckReferenceError, SyropeEA
|
|
17
|
+
from cabledyn.checks import (
|
|
18
|
+
API_RP_2SK_SAFETY_FACTORS,
|
|
19
|
+
DNV_OS_E301_PARTIAL_FACTORS,
|
|
20
|
+
DNV_OS_E301_STRENGTH_FACTOR,
|
|
21
|
+
BendCheck,
|
|
22
|
+
BendLimits,
|
|
23
|
+
TensionCheck,
|
|
24
|
+
bend_check,
|
|
25
|
+
mbr_utilisation,
|
|
26
|
+
tension_check,
|
|
27
|
+
)
|
|
28
|
+
from cabledyn.clearance import (
|
|
29
|
+
Bathymetry,
|
|
30
|
+
ClearanceMatrix,
|
|
31
|
+
LineClearance,
|
|
32
|
+
SeabedClearance,
|
|
33
|
+
clearance_matrix,
|
|
34
|
+
line_clearance,
|
|
35
|
+
read_bathymetry,
|
|
36
|
+
seabed_clearance,
|
|
37
|
+
segment_distance,
|
|
38
|
+
)
|
|
39
|
+
from cabledyn.compare import ChannelComparison, HistoryComparison, compare_histories
|
|
40
|
+
from cabledyn.curves import (
|
|
41
|
+
FatigueCurve,
|
|
42
|
+
api_rp_2sk_curve,
|
|
43
|
+
chain_nominal_area,
|
|
44
|
+
dnv_os_e301_curve,
|
|
45
|
+
dnv_rp_c203_curve,
|
|
46
|
+
)
|
|
47
|
+
from cabledyn.damage import (
|
|
48
|
+
ChannelDamage,
|
|
49
|
+
DamageProfile,
|
|
50
|
+
LifetimeFatigue,
|
|
51
|
+
SeaStateDamage,
|
|
52
|
+
cable_stress,
|
|
53
|
+
channel_damage,
|
|
54
|
+
damage_along_arc,
|
|
55
|
+
lifetime_fatigue,
|
|
56
|
+
miner_damage,
|
|
57
|
+
study_sea_state_damage,
|
|
58
|
+
)
|
|
59
|
+
from cabledyn.deck import DeckWriter
|
|
60
|
+
from cabledyn.deck_file import DeckFile, GeneratedCase, generate_deck_cases
|
|
61
|
+
from cabledyn.driver import (
|
|
62
|
+
CableDynDriver,
|
|
63
|
+
DriverError,
|
|
64
|
+
DriverExecutionError,
|
|
65
|
+
DriverNotFoundError,
|
|
66
|
+
DriverResult,
|
|
67
|
+
read_output,
|
|
68
|
+
)
|
|
69
|
+
from cabledyn.errors import (
|
|
70
|
+
CableDynError,
|
|
71
|
+
ConvergenceError,
|
|
72
|
+
DeckFormatError,
|
|
73
|
+
OutputFormatError,
|
|
74
|
+
StudyFormatError,
|
|
75
|
+
StudyOutputError,
|
|
76
|
+
)
|
|
77
|
+
from cabledyn.extremes import (
|
|
78
|
+
GumbelFit,
|
|
79
|
+
WeibullFit,
|
|
80
|
+
block_maxima,
|
|
81
|
+
fit_gumbel,
|
|
82
|
+
fit_weibull,
|
|
83
|
+
upcrossing_maxima,
|
|
84
|
+
)
|
|
85
|
+
from cabledyn.fatigue import (
|
|
86
|
+
FatigueResult,
|
|
87
|
+
RainflowCycle,
|
|
88
|
+
RainflowHistogram,
|
|
89
|
+
cycle_histogram,
|
|
90
|
+
damage_equivalent_range,
|
|
91
|
+
rainflow_cycles,
|
|
92
|
+
)
|
|
93
|
+
from cabledyn.formats import (
|
|
94
|
+
CoupledRun,
|
|
95
|
+
MoorDynLineHistory,
|
|
96
|
+
read_coupled_run,
|
|
97
|
+
read_moordyn_line,
|
|
98
|
+
read_moordyn_output,
|
|
99
|
+
read_openfast_output,
|
|
100
|
+
read_table,
|
|
101
|
+
)
|
|
102
|
+
from cabledyn.geometry import LineGeometry, Touchdown, line_geometry
|
|
103
|
+
from cabledyn.processing import fft_filter, moving_average, resample
|
|
104
|
+
from cabledyn.profiles import (
|
|
105
|
+
ArcProfile,
|
|
106
|
+
LineField,
|
|
107
|
+
LinePositions,
|
|
108
|
+
available_quantities,
|
|
109
|
+
line_field,
|
|
110
|
+
line_positions,
|
|
111
|
+
line_range_graph,
|
|
112
|
+
profile_at,
|
|
113
|
+
profiles_at,
|
|
114
|
+
)
|
|
115
|
+
from cabledyn.ranges import (
|
|
116
|
+
RangeGraph,
|
|
117
|
+
element_range_graph,
|
|
118
|
+
node_range_graph,
|
|
119
|
+
read_range_graph,
|
|
120
|
+
read_range_graphs,
|
|
121
|
+
static_range_graph,
|
|
122
|
+
)
|
|
123
|
+
from cabledyn.results import (
|
|
124
|
+
ChannelStatistics,
|
|
125
|
+
LineNodeHistory,
|
|
126
|
+
LineSegmentHistory,
|
|
127
|
+
OutputTable,
|
|
128
|
+
SpatialStatistics,
|
|
129
|
+
StaticLineSummary,
|
|
130
|
+
StaticProfile,
|
|
131
|
+
TimeHistory,
|
|
132
|
+
)
|
|
133
|
+
from cabledyn.spectra import (
|
|
134
|
+
CoherenceResult,
|
|
135
|
+
PowerSpectrum,
|
|
136
|
+
SpectralPeak,
|
|
137
|
+
magnitude_squared_coherence,
|
|
138
|
+
power_spectrum,
|
|
139
|
+
)
|
|
140
|
+
from cabledyn.study import StudyCaseResult, StudyResult, run_study
|
|
141
|
+
from cabledyn.summary import (
|
|
142
|
+
ChannelSummary,
|
|
143
|
+
LineSummary,
|
|
144
|
+
SummaryTable,
|
|
145
|
+
channel_summary,
|
|
146
|
+
line_summary,
|
|
147
|
+
)
|
|
148
|
+
from cabledyn.sweep import parameter_grid
|
|
149
|
+
from cabledyn.touchdown import TouchdownHistory, touchdown_history
|
|
150
|
+
|
|
151
|
+
if TYPE_CHECKING:
|
|
152
|
+
from cabledyn._lib import abi_minor as abi_minor
|
|
153
|
+
from cabledyn._lib import abi_version as abi_version
|
|
154
|
+
from cabledyn._lib import library_path as library_path
|
|
155
|
+
from cabledyn._lib import version_string as version_string
|
|
156
|
+
from cabledyn.model import CableDyn as CableDyn
|
|
157
|
+
|
|
158
|
+
__all__ = [
|
|
159
|
+
"API_RP_2SK_SAFETY_FACTORS",
|
|
160
|
+
"DNV_OS_E301_PARTIAL_FACTORS",
|
|
161
|
+
"DNV_OS_E301_STRENGTH_FACTOR",
|
|
162
|
+
"ArcProfile",
|
|
163
|
+
"Bathymetry",
|
|
164
|
+
"BendCheck",
|
|
165
|
+
"BendLimits",
|
|
166
|
+
"CableDynDriver",
|
|
167
|
+
"CableDynError",
|
|
168
|
+
"ChannelComparison",
|
|
169
|
+
"ChannelDamage",
|
|
170
|
+
"ChannelStatistics",
|
|
171
|
+
"ChannelSummary",
|
|
172
|
+
"ClearanceMatrix",
|
|
173
|
+
"CoherenceResult",
|
|
174
|
+
"ConvergenceError",
|
|
175
|
+
"CoupledRun",
|
|
176
|
+
"DamageProfile",
|
|
177
|
+
"DeckFile",
|
|
178
|
+
"DeckFormatError",
|
|
179
|
+
"DeckModel",
|
|
180
|
+
"DeckReferenceError",
|
|
181
|
+
"DeckWriter",
|
|
182
|
+
"DriverError",
|
|
183
|
+
"DriverExecutionError",
|
|
184
|
+
"DriverNotFoundError",
|
|
185
|
+
"DriverResult",
|
|
186
|
+
"FatigueCurve",
|
|
187
|
+
"FatigueResult",
|
|
188
|
+
"GeneratedCase",
|
|
189
|
+
"GumbelFit",
|
|
190
|
+
"HistoryComparison",
|
|
191
|
+
"LifetimeFatigue",
|
|
192
|
+
"LineClearance",
|
|
193
|
+
"LineField",
|
|
194
|
+
"LineGeometry",
|
|
195
|
+
"LineNodeHistory",
|
|
196
|
+
"LinePositions",
|
|
197
|
+
"LineSegmentHistory",
|
|
198
|
+
"LineSummary",
|
|
199
|
+
"MoorDynLineHistory",
|
|
200
|
+
"OutputFormatError",
|
|
201
|
+
"OutputTable",
|
|
202
|
+
"PowerSpectrum",
|
|
203
|
+
"RainflowCycle",
|
|
204
|
+
"RainflowHistogram",
|
|
205
|
+
"RangeGraph",
|
|
206
|
+
"Recorder",
|
|
207
|
+
"SeaStateDamage",
|
|
208
|
+
"Seabed",
|
|
209
|
+
"SeabedClearance",
|
|
210
|
+
"Snapshots",
|
|
211
|
+
"SpatialStatistics",
|
|
212
|
+
"SpectralPeak",
|
|
213
|
+
"StaticLineSummary",
|
|
214
|
+
"StaticProfile",
|
|
215
|
+
"StudyCaseResult",
|
|
216
|
+
"StudyFormatError",
|
|
217
|
+
"StudyOutputError",
|
|
218
|
+
"StudyResult",
|
|
219
|
+
"SummaryTable",
|
|
220
|
+
"SyropeEA",
|
|
221
|
+
"TensionCheck",
|
|
222
|
+
"TimeHistory",
|
|
223
|
+
"Touchdown",
|
|
224
|
+
"TouchdownHistory",
|
|
225
|
+
"WaterSurface",
|
|
226
|
+
"WeibullFit",
|
|
227
|
+
"animate",
|
|
228
|
+
"api_rp_2sk_curve",
|
|
229
|
+
"available_quantities",
|
|
230
|
+
"bend_check",
|
|
231
|
+
"block_maxima",
|
|
232
|
+
"cable_stress",
|
|
233
|
+
"chain_nominal_area",
|
|
234
|
+
"channel_damage",
|
|
235
|
+
"channel_summary",
|
|
236
|
+
"clearance_matrix",
|
|
237
|
+
"compare_histories",
|
|
238
|
+
"cycle_histogram",
|
|
239
|
+
"damage_along_arc",
|
|
240
|
+
"damage_equivalent_range",
|
|
241
|
+
"dnv_os_e301_curve",
|
|
242
|
+
"dnv_rp_c203_curve",
|
|
243
|
+
"element_range_graph",
|
|
244
|
+
"fft_filter",
|
|
245
|
+
"fit_gumbel",
|
|
246
|
+
"fit_weibull",
|
|
247
|
+
"generate_deck_cases",
|
|
248
|
+
"lifetime_fatigue",
|
|
249
|
+
"line_clearance",
|
|
250
|
+
"line_field",
|
|
251
|
+
"line_geometry",
|
|
252
|
+
"line_positions",
|
|
253
|
+
"line_range_graph",
|
|
254
|
+
"line_summary",
|
|
255
|
+
"magnitude_squared_coherence",
|
|
256
|
+
"mbr_utilisation",
|
|
257
|
+
"miner_damage",
|
|
258
|
+
"moving_average",
|
|
259
|
+
"node_range_graph",
|
|
260
|
+
"parameter_grid",
|
|
261
|
+
"power_spectrum",
|
|
262
|
+
"profile_at",
|
|
263
|
+
"profiles_at",
|
|
264
|
+
"rainflow_cycles",
|
|
265
|
+
"read_bathymetry",
|
|
266
|
+
"read_coupled_run",
|
|
267
|
+
"read_moordyn_line",
|
|
268
|
+
"read_moordyn_output",
|
|
269
|
+
"read_openfast_output",
|
|
270
|
+
"read_output",
|
|
271
|
+
"read_range_graph",
|
|
272
|
+
"read_range_graphs",
|
|
273
|
+
"read_table",
|
|
274
|
+
"resample",
|
|
275
|
+
"run_study",
|
|
276
|
+
"seabed_clearance",
|
|
277
|
+
"segment_distance",
|
|
278
|
+
"static_range_graph",
|
|
279
|
+
"study_sea_state_damage",
|
|
280
|
+
"tension_check",
|
|
281
|
+
"touchdown_history",
|
|
282
|
+
"upcrossing_maxima",
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
__version__ = "0.3.0"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def __getattr__(name: str) -> Any:
|
|
289
|
+
"""Load the optional shared-library API only when requested."""
|
|
290
|
+
if name == "CableDyn":
|
|
291
|
+
from cabledyn.model import CableDyn
|
|
292
|
+
|
|
293
|
+
return CableDyn
|
|
294
|
+
if name in {"abi_minor", "abi_version", "library_path", "version_string"}:
|
|
295
|
+
from cabledyn import _lib
|
|
296
|
+
|
|
297
|
+
return getattr(_lib, name)
|
|
298
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
cabledyn/_csv.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Atomic CSV writing shared by the design-check and range-graph results."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import contextlib
|
|
7
|
+
import csv
|
|
8
|
+
import os
|
|
9
|
+
import tempfile
|
|
10
|
+
from collections.abc import Iterable, Sequence
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def number(value: float) -> str:
|
|
15
|
+
"""Format a float with full round-trip precision."""
|
|
16
|
+
return f"{float(value):.17g}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def write_csv(
|
|
20
|
+
path: str | os.PathLike[str],
|
|
21
|
+
header: Sequence[str],
|
|
22
|
+
rows: Iterable[Sequence[object]],
|
|
23
|
+
*,
|
|
24
|
+
overwrite: bool,
|
|
25
|
+
) -> Path:
|
|
26
|
+
"""Atomically write ``rows`` under ``header``; return the absolute path."""
|
|
27
|
+
target = Path(path).expanduser().resolve()
|
|
28
|
+
if target.exists() and not overwrite:
|
|
29
|
+
raise FileExistsError(f"output already exists: {target}")
|
|
30
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
fd, temporary = tempfile.mkstemp(
|
|
32
|
+
prefix=f".{target.name}.", suffix=".tmp", dir=target.parent, text=True
|
|
33
|
+
)
|
|
34
|
+
try:
|
|
35
|
+
with os.fdopen(fd, "w", encoding="utf-8", newline="") as stream:
|
|
36
|
+
writer = csv.writer(stream, lineterminator="\n")
|
|
37
|
+
writer.writerow(header)
|
|
38
|
+
writer.writerows(rows)
|
|
39
|
+
os.replace(temporary, target)
|
|
40
|
+
except BaseException:
|
|
41
|
+
with contextlib.suppress(FileNotFoundError):
|
|
42
|
+
os.unlink(temporary)
|
|
43
|
+
raise
|
|
44
|
+
return target
|
cabledyn/_discovery.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Locate the CableDyn shared library without loading it.
|
|
3
|
+
|
|
4
|
+
The search order is:
|
|
5
|
+
|
|
6
|
+
1. ``CABLEDYN_LIBRARY``: an exact path, used as given.
|
|
7
|
+
2. The package directory, for a wheel or deployment that bundles the library.
|
|
8
|
+
3. The CMake build tree of a source checkout (``<repo>/build``).
|
|
9
|
+
|
|
10
|
+
In the build tree only linker products are considered: ``build/bin`` and
|
|
11
|
+
``build`` for a single-configuration generator, and ``build/bin/<Config>`` and
|
|
12
|
+
``build/<Config>`` for a multi-configuration generator. The self-contained
|
|
13
|
+
``build/runtime`` bundle is a copy and is never auto-selected, so it cannot
|
|
14
|
+
shadow a newer build. When more than one configuration holds a library,
|
|
15
|
+
``CABLEDYN_BUILD_CONFIG`` must name the intended one; discovery never guesses.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
CMAKE_CONFIGURATIONS = ("Release", "RelWithDebInfo", "MinSizeRel", "Debug")
|
|
25
|
+
_UNCONFIGURED = "single-configuration"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def library_basenames(os_name: str | None = None, platform: str | None = None) -> tuple[str, ...]:
|
|
29
|
+
"""Return the shared-library file names loadable by the given (or current) host."""
|
|
30
|
+
host_os = os.name if os_name is None else os_name
|
|
31
|
+
host_platform = sys.platform if platform is None else platform
|
|
32
|
+
if host_os == "nt":
|
|
33
|
+
# GNU prefixes the DLL name; IFX/MSVC use the unprefixed name.
|
|
34
|
+
return ("libcabledyn.dll", "cabledyn.dll")
|
|
35
|
+
if host_platform == "darwin":
|
|
36
|
+
return ("libcabledyn.dylib",)
|
|
37
|
+
return ("libcabledyn.so",)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _existing(directory: Path, basenames: tuple[str, ...]) -> list[Path]:
|
|
41
|
+
"""Return the libraries present in one directory, rejecting ambiguous pairs."""
|
|
42
|
+
found = [directory / name for name in basenames if (directory / name).is_file()]
|
|
43
|
+
if len(found) > 1:
|
|
44
|
+
names = ", ".join(path.name for path in found)
|
|
45
|
+
raise OSError(
|
|
46
|
+
f"cabledyn: several CableDyn shared libraries exist in {directory} ({names}). "
|
|
47
|
+
"Remove the stale product or set CABLEDYN_LIBRARY to the exact library path."
|
|
48
|
+
)
|
|
49
|
+
return found
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def build_tree_layouts(build: Path) -> dict[str, tuple[Path, ...]]:
|
|
53
|
+
"""Return the linker-product directories of each CMake layout, in search order."""
|
|
54
|
+
layouts: dict[str, tuple[Path, ...]] = {_UNCONFIGURED: (build / "bin", build)}
|
|
55
|
+
for configuration in CMAKE_CONFIGURATIONS:
|
|
56
|
+
layouts[configuration] = (build / "bin" / configuration, build / configuration)
|
|
57
|
+
return layouts
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def selected_configuration(value: str | None = None) -> str | None:
|
|
61
|
+
"""Return the canonical ``CABLEDYN_BUILD_CONFIG`` value, or ``None`` when unset."""
|
|
62
|
+
requested = (os.environ.get("CABLEDYN_BUILD_CONFIG", "") if value is None else value).strip()
|
|
63
|
+
if not requested:
|
|
64
|
+
return None
|
|
65
|
+
for configuration in CMAKE_CONFIGURATIONS:
|
|
66
|
+
if configuration.lower() == requested.lower():
|
|
67
|
+
return configuration
|
|
68
|
+
choices = ", ".join(CMAKE_CONFIGURATIONS)
|
|
69
|
+
raise OSError(f"cabledyn: CABLEDYN_BUILD_CONFIG must be one of {choices}; got {requested!r}")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def find_in_build_tree(
|
|
73
|
+
build: Path,
|
|
74
|
+
basenames: tuple[str, ...],
|
|
75
|
+
configuration: str | None,
|
|
76
|
+
) -> tuple[Path | None, list[Path]]:
|
|
77
|
+
"""Return ``(library, searched)`` for a CMake build tree.
|
|
78
|
+
|
|
79
|
+
``library`` is ``None`` when nothing was found. Several populated layouts
|
|
80
|
+
without an explicit ``configuration`` raise :class:`OSError`.
|
|
81
|
+
"""
|
|
82
|
+
layouts = build_tree_layouts(build)
|
|
83
|
+
searched = [
|
|
84
|
+
directory / name for dirs in layouts.values() for directory in dirs for name in basenames
|
|
85
|
+
]
|
|
86
|
+
# An explicit configuration also accepts a single-configuration build whose
|
|
87
|
+
# CMAKE_BUILD_TYPE is not encoded in the directory layout.
|
|
88
|
+
chosen = [configuration, _UNCONFIGURED] if configuration is not None else list(layouts)
|
|
89
|
+
populated: dict[str, Path] = {}
|
|
90
|
+
for name in chosen:
|
|
91
|
+
for directory in layouts[name]:
|
|
92
|
+
found = _existing(directory, basenames)
|
|
93
|
+
if found:
|
|
94
|
+
populated[name] = found[0]
|
|
95
|
+
break
|
|
96
|
+
if configuration is not None:
|
|
97
|
+
if configuration in populated:
|
|
98
|
+
return populated[configuration], searched
|
|
99
|
+
if _UNCONFIGURED in populated and not any(
|
|
100
|
+
_existing(directory, basenames)
|
|
101
|
+
for name in CMAKE_CONFIGURATIONS
|
|
102
|
+
for directory in layouts[name]
|
|
103
|
+
):
|
|
104
|
+
return populated[_UNCONFIGURED], searched
|
|
105
|
+
return None, searched
|
|
106
|
+
if len(populated) > 1:
|
|
107
|
+
layouts_found = ", ".join(sorted(populated))
|
|
108
|
+
raise OSError(
|
|
109
|
+
f"cabledyn: {build} contains CableDyn libraries for several build layouts "
|
|
110
|
+
f"({layouts_found}). "
|
|
111
|
+
"Set CABLEDYN_BUILD_CONFIG to the intended configuration or CABLEDYN_LIBRARY to the "
|
|
112
|
+
"exact library path."
|
|
113
|
+
)
|
|
114
|
+
return (next(iter(populated.values())) if populated else None), searched
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def find_library(package_directory: Path | None = None) -> Path:
|
|
118
|
+
"""Return the CableDyn shared library to load, or raise :class:`OSError`."""
|
|
119
|
+
exact = os.environ.get("CABLEDYN_LIBRARY", "").strip()
|
|
120
|
+
if exact:
|
|
121
|
+
path = Path(exact).expanduser()
|
|
122
|
+
if not path.is_file():
|
|
123
|
+
raise OSError(f"cabledyn: CABLEDYN_LIBRARY does not name an existing file: {exact}")
|
|
124
|
+
return path.resolve()
|
|
125
|
+
|
|
126
|
+
package = Path(__file__).resolve().parent if package_directory is None else package_directory
|
|
127
|
+
basenames = library_basenames()
|
|
128
|
+
bundled = _existing(package, basenames)
|
|
129
|
+
if bundled:
|
|
130
|
+
return bundled[0]
|
|
131
|
+
|
|
132
|
+
# Source checkout: <repo>/python/cabledyn -> <repo>/build.
|
|
133
|
+
build = package.parent.parent / "build"
|
|
134
|
+
library, searched = find_in_build_tree(build, basenames, selected_configuration())
|
|
135
|
+
if library is not None:
|
|
136
|
+
return library.resolve()
|
|
137
|
+
tried = [package / name for name in basenames] + searched
|
|
138
|
+
raise OSError(
|
|
139
|
+
"cabledyn: could not locate the CableDyn shared library. Set CABLEDYN_LIBRARY to its "
|
|
140
|
+
"full path, or build it with `cmake --build build --target cabledyn_shared`. Tried:\n "
|
|
141
|
+
+ "\n ".join(str(path) for path in tried)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def dependency_directories(library: Path) -> list[Path]:
|
|
146
|
+
"""Return directories that may hold the library's toolchain runtime DLLs.
|
|
147
|
+
|
|
148
|
+
The library's own directory comes first. For a multi-configuration build
|
|
149
|
+
tree the GNU/OpenBLAS runtime set is staged once in ``build/bin`` and
|
|
150
|
+
``build/runtime`` rather than beside each configuration's product.
|
|
151
|
+
"""
|
|
152
|
+
directories = [library.parent]
|
|
153
|
+
for ancestor in library.parents:
|
|
154
|
+
if ancestor.name == "build":
|
|
155
|
+
directories.extend(
|
|
156
|
+
path for path in (ancestor / "bin", ancestor / "runtime") if path.is_dir()
|
|
157
|
+
)
|
|
158
|
+
break
|
|
159
|
+
unique: list[Path] = []
|
|
160
|
+
for directory in directories:
|
|
161
|
+
if directory not in unique:
|
|
162
|
+
unique.append(directory)
|
|
163
|
+
return unique
|