Skip to content

Commit 8b7765c

Browse files
Kyeong Sun Kimclaude
andcommitted
feat(foundations): introduce type/SSI split API + NREL 5MW OC3 dossier (PR #1/N)
Establishes the new foundation API that separates TYPE (monopile, tripod, jacket, suction bucket) from SSI FIDELITY (fixed, 6x6, lumped BNWF, physical BNWF, Craig-Bampton). v1.0 API frozen; legacy path continues to work with a DeprecationWarning. Context ------- op3 v1.0 conflated two axes into one enum: ``FoundationMode.{FIXED, STIFFNESS_6X6, DISTRIBUTED_BNWF, DISSIPATION_WEIGHTED}`` was described as "four foundation modes" but actually encodes SSI fidelity levels, not foundation topologies. There is no place for a Tripod or Jacket to live its own geometry, mass, and coupling interface. This PR scaffolds the clean split so model-by-model V&V&C work can begin. The pattern is validated against the smallest published monopile benchmark (NREL 5MW OC3 Phase I, Passon 2008). Tripod and Jacket land in future PRs. New API surface --------------- - op3/foundations/base.py * FoundationType enum (MONOPILE, TRIPOD, JACKET, SUCTION_BUCKET, GBS) * FoundationProtocol (duck-typed contract) * BaseFoundation ABC with as_legacy_foundation() back-compat bridge - op3/foundations/types/monopile.py * Monopile dataclass with diameter, wall thickness, embed length, stub length, soil profile, material properties * .from_oc3_spec() factory for NREL 5MW OC3 Phase I geometry * .from_yaml(model_dir) factory loading from dossier * .with_ssi(strategy) fluent API; .head_stiffness_6x6() queries it - op3/ssi/base.py: SSIProtocol - op3/ssi/stiffness_6x6.py: Stiffness6x6 strategy (pre-computed K, .rigid() fixed-base factory, .from_csv() loader) - op3/ssi/pisa.py: PISA strategy wrapping pisa_pile_stiffness_6x6 Dossier pattern --------------- op3/models/nrel_5mw_oc3_monopile/ becomes the template for every future validated model: - site.yaml: site metadata, references, DOIs - geometry.yaml: foundation + tower + RNA geometry with provenance - soil.yaml: layered soil profile (placeholder for OC3 Phase I, real profile comes from OC3 Phase II/III in PR #2) - vvc.yaml: Verification / Validation / Calibration dossier with per-metric RED / YELLOW / GREEN status, acceptance tolerances, reference citations, and test module pointers - build.py: build_monopile(ssi) and build_tower_model(ssi) entry points using the new API; bridges to op3.composer for eigen A model is only cleared for use in dissertation results when every vvc.yaml metric reads GREEN. PR #1 ships the nrel_5mw_oc3_monopile dossier with one metric GREEN (f1_Hz_oc3_coupled within 5% of the Passon 2008 benchmark: 0.2815 Hz vs 0.276 Hz published, 2% error) and the rest NOT-STARTED / BLOCKED pending PR #2. Back-compat ----------- - op3/foundations.py is now a package (not a module); all legacy imports (``from op3.foundations import Foundation, FoundationMode, build_foundation, apply_scour_relief, foundation_from_pisa``) are preserved verbatim via ``op3/foundations/_legacy.py`` + ``op3/foundations/__init__.py`` re-export. - build_foundation() now emits DeprecationWarning pointing at the new API. The Monopile back-compat bridge constructs Foundation() directly and is deprecation-silent. - composer.py, opensees_foundations/*, openfast_coupling/*, standards/*, uq/*, anchors/*, and all existing tests are UNCHANGED. The 271-framework + 134-anchor regression suite remains green. Tests (tests/test_model_nrel_5mw_oc3_monopile.py, 11 new) --------------------------------------------------------- - Dossier files exist - Monopile.from_oc3_spec / from_yaml contract - head_stiffness_6x6 without SSI raises clearly - with_ssi chaining, rigid K > 1e19 diagonal - as_legacy_foundation returns STIFFNESS_6X6 with correct provenance - Bridge emits no DeprecationWarning; direct build_foundation() does - compose_tower_model accepts bridged Foundation - f1 eigen matches OC3 Phase I within 5% (Passon 2008) 405 tests passing (271 framework + 134 anchors, 11 skipped). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent daed0c8 commit 8b7765c

18 files changed

Lines changed: 1380 additions & 57 deletions

File tree

op3/foundations/__init__.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
Op^3 foundations package.
3+
4+
Two coexisting APIs:
5+
6+
1. **Legacy API (frozen at v1.0)** — re-exported from
7+
:mod:`op3.foundations._legacy`. Emits :class:`DeprecationWarning`
8+
when :func:`build_foundation` is called. Will be removed in v2.0.
9+
10+
2. **New type/SSI API (v1.1+)** — :mod:`op3.foundations.types`
11+
provides concrete foundation topologies (``Monopile``, ``Tripod``,
12+
``Jacket``, ``SuctionBucket``); :mod:`op3.ssi` provides SSI
13+
fidelity strategies (``Fixed``, ``Stiffness6x6``, ``BNWFLumped``,
14+
``BNWFPhysical``, ``CraigBampton``). Each model under
15+
:mod:`op3.models` is a validated-dossier instance that combines
16+
the two.
17+
18+
Import cheat sheet
19+
------------------
20+
>>> # Legacy (still works; DeprecationWarning)
21+
>>> from op3.foundations import build_foundation, FoundationMode
22+
>>>
23+
>>> # New types
24+
>>> from op3.foundations.types import Monopile
25+
>>> from op3.ssi import Stiffness6x6
26+
>>>
27+
>>> # Foundation protocol (for duck-typing / type hints)
28+
>>> from op3.foundations.base import FoundationProtocol
29+
30+
The package import surface is intentionally thin: everything below is
31+
re-exported from either ``_legacy`` (with a deprecation trail) or
32+
``base`` (the new protocol). Concrete types are NOT re-exported here;
33+
import them explicitly from :mod:`op3.foundations.types`.
34+
"""
35+
from __future__ import annotations
36+
37+
# Re-export the legacy API verbatim so ``from op3.foundations import
38+
# Foundation, FoundationMode, build_foundation, apply_scour_relief,
39+
# foundation_from_pisa`` continues to work for every v1.0 caller.
40+
from op3.foundations._legacy import (
41+
Foundation,
42+
FoundationMode,
43+
apply_scour_relief,
44+
build_foundation,
45+
foundation_from_pisa,
46+
)
47+
48+
# New-API surface (opt-in).
49+
from op3.foundations.base import (
50+
BaseFoundation,
51+
FoundationProtocol,
52+
FoundationType,
53+
)
54+
55+
__all__ = [
56+
# Legacy (deprecated)
57+
"Foundation",
58+
"FoundationMode",
59+
"apply_scour_relief",
60+
"build_foundation",
61+
"foundation_from_pisa",
62+
# New
63+
"BaseFoundation",
64+
"FoundationProtocol",
65+
"FoundationType",
66+
]
Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,32 @@
11
"""
2-
Op^3 foundation module factory.
3-
4-
This module exposes the four OpenSeesPy foundation representations that
5-
differ in fidelity from "fixed base" (Mode A) to "dissipation-weighted
6-
generalized BNWF" (Mode D). All four modes share the same tower
7-
interface, so they can be swapped at runtime with a single flag.
8-
9-
The OpenSeesPy code that actually builds the springs and zero-length
10-
elements lives in `op3.opensees_foundations.*`. This module is a thin
11-
factory that picks the right builder based on the mode name.
12-
13-
The geotechnical/structural integration is the key contribution here:
14-
the CSV schema produced by OptumGX (bearing capacity envelopes, contact
15-
pressures, plastic dissipation fields) maps directly into the spring
16-
parameters consumed by OpenSeesPy. Each mode uses a different subset of
17-
this mapping.
18-
19-
Mode A (fixed):
20-
Does not use any OptumGX data. Fixes the tower base with `fix` on
21-
all six DOF.
22-
23-
Mode B (stiffness_6x6):
24-
Reads a 6x6 stiffness matrix from CSV and attaches it as a single
25-
zeroLength element at the tower base. The matrix is typically
26-
derived from OpenSeesPy Mode C or D via static condensation.
27-
28-
Mode C (distributed_bnwf):
29-
Reads depth-resolved spring stiffness and ultimate resistance from
30-
`opensees_spring_stiffness.csv` (produced by OptumGX) and builds
31-
a chain of lateral p-y and vertical t-z springs along the bucket
32-
skirt. Applies a stress-correction relief factor for scour.
33-
34-
Mode D (dissipation_weighted):
35-
Extends Mode C with a depth-dependent participation factor w(z)
36-
derived from the plastic dissipation field at collapse. This is
37-
the generalized cavity expansion framework of Appendix A of the
38-
dissertation.
39-
40-
Example
41-
-------
42-
43-
>>> from op3 import build_foundation
44-
>>> f = build_foundation(
45-
... mode='distributed_bnwf',
46-
... spring_profile='data/fem_results/opensees_spring_stiffness.csv',
47-
... scour_depth=1.5,
48-
... )
49-
>>> f.attach_to_opensees(base_node=1000) # called by the composer
2+
LEGACY Op^3 foundation factory (frozen at v1.0).
3+
4+
This module is the original ``op3.foundations`` module moved verbatim
5+
into a submodule of the new ``op3.foundations`` package. The public
6+
API (``Foundation``, ``FoundationMode``, ``build_foundation``,
7+
``apply_scour_relief``, ``foundation_from_pisa``) is preserved for
8+
backwards compatibility and re-exported by ``op3.foundations.__init__``.
9+
10+
A :class:`DeprecationWarning` is emitted when ``build_foundation`` or
11+
``FoundationMode`` is used, pointing to the new
12+
:mod:`op3.foundations.types` API (``Monopile``, ``Tripod``, ``Jacket``,
13+
``SuctionBucket``) introduced in v1.1+. The legacy path will remain
14+
functional until the next major version bump.
15+
16+
The four original modes (A/B/C/D) represent **SSI fidelity levels**
17+
(fixed / 6x6 / lumped-BNWF / dissipation-weighted BNWF) — they are
18+
NOT foundation types. The new API separates the axes:
19+
20+
FoundationType (Monopile, Tripod, Jacket, ...) x
21+
SSIStrategy (Fixed, Stiffness6x6, BNWFLumped, BNWFPhysical, CraigBampton)
22+
23+
Concrete foundation topologies with their own mass, geometry, and
24+
coupling interface now live under :mod:`op3.foundations.types`; SSI
25+
strategies live under :mod:`op3.ssi`.
5026
"""
5127
from __future__ import annotations
5228

29+
import warnings
5330
from dataclasses import dataclass, field
5431
from enum import Enum
5532
from pathlib import Path
@@ -60,12 +37,18 @@
6037

6138

6239
class FoundationMode(str, Enum):
63-
"""The five Op^3 foundation representations.
40+
"""The five Op^3 foundation representations (LEGACY, frozen at v1.0).
6441
6542
The first four are the original v1.0 fidelity ladder (Modes A-D).
6643
``distributed_bnwf_nonlinear`` (v1.1+) is the blueprint Q1(a) primary:
6744
a physically-distributed skirt column with per-depth PySimple1/
6845
TzSimple1 backbones.
46+
47+
**Deprecation notice (v1.1+):** these values represent SSI fidelity
48+
levels, not foundation types. New code should use the type/SSI
49+
split under :mod:`op3.foundations.types` and :mod:`op3.ssi`. The
50+
legacy ``build_foundation(mode=...)`` path remains functional
51+
throughout v1.x.
6952
"""
7053
FIXED = "fixed"
7154
STIFFNESS_6X6 = "stiffness_6x6"
@@ -188,14 +171,24 @@ def build_foundation(
188171
skirt_length_m: Optional[float] = None,
189172
skirt_thickness_m: float = 0.025,
190173
physical: bool = False,
174+
_suppress_deprecation_warning: bool = False,
191175
) -> Foundation:
192-
"""Construct a Foundation handle ready for the composer.
176+
"""Construct a Foundation handle ready for the composer (LEGACY API).
177+
178+
**Deprecated (v1.1+):** use :mod:`op3.foundations.types` and
179+
:mod:`op3.ssi` for new code. Example:
180+
181+
>>> from op3.foundations.types import Monopile
182+
>>> from op3.ssi import Stiffness6x6
183+
>>> mono = Monopile.from_oc3_spec(soil_profile=...)
184+
>>> mono.with_ssi(Stiffness6x6(K=mono.head_stiffness_6x6()))
193185
194186
Parameters
195187
----------
196188
mode : str
197189
One of 'fixed', 'stiffness_6x6', 'distributed_bnwf',
198-
'dissipation_weighted'. Case-insensitive.
190+
'dissipation_weighted', 'distributed_bnwf_nonlinear'.
191+
Case-insensitive.
199192
spring_profile : str or Path, optional
200193
Path to a CSV with columns (depth_m, k_ini_kN_per_m,
201194
p_ult_kN_per_m, spring_type). Required for Modes C and D.
@@ -211,12 +204,22 @@ def build_foundation(
211204
resistance scaling.
212205
scour_depth : float, default 0.0
213206
Scour depth in meters. Affects Modes B, C, D.
214-
215-
Returns
216-
-------
217-
Foundation
218-
An opaque handle the composer can attach to an OpenSees model.
207+
_suppress_deprecation_warning : bool
208+
Internal flag used by ``op3.foundations.types`` adapters to
209+
avoid spurious deprecation noise on the back-compat bridge.
219210
"""
211+
if not _suppress_deprecation_warning:
212+
warnings.warn(
213+
"op3.foundations.build_foundation (and FoundationMode) are "
214+
"frozen at v1.0 and will be removed in v2.0. Use the new "
215+
"type/SSI split: op3.foundations.types.{Monopile,Tripod,"
216+
"Jacket,SuctionBucket} + op3.ssi.{Fixed,Stiffness6x6,"
217+
"BNWFLumped,BNWFPhysical,CraigBampton}. See "
218+
"op3/models/<name>/build.py for reference use.",
219+
DeprecationWarning,
220+
stacklevel=2,
221+
)
222+
220223
try:
221224
mode_enum = FoundationMode(mode.lower())
222225
except ValueError:
@@ -358,4 +361,3 @@ def foundation_from_pisa(
358361
f"L={embed_length_m} m, {len(soil_profile)} soil layers"
359362
)
360363
return foundation
361-

0 commit comments

Comments
 (0)