Task-oriented guide for adding new physics, models, and adapters to SEA-Stack. Each section covers one extension point: the interface to implement, where to place files, what to link, and how to test.
For the full architecture and module inventory, see TECHNICAL_OVERVIEW.md.
Extension points at a glance
- Force components — implement
IHydroForceComponent - Wave models — subclass
WaveBase - PTO models — implement
IPTOModel - Controllers — implement
IController - Solver adapters — add an adapter analogous to
adapters/chrono/
Interface: IHydroForceComponent in
libs/core/include/seastack/core/force_component.h
namespace seastack::hydro {
class IHydroForceComponent {
public:
virtual ~IHydroForceComponent() = default;
virtual HydroComponentType Type() const = 0;
virtual void Compute(const SystemState& state,
double time,
BodyForces& inout_forces) = 0;
};
} // namespace seastack::hydroSteps:
-
Create a header under
libs/hydro/include/seastack/hydro/force_components/and a source file underlibs/hydro/src/force_components/. -
Subclass
IHydroForceComponent. ImplementType()(add a value toHydroComponentTypeif needed) andCompute(). -
Add the source file to the
SEAStack::Hydrotarget inlibs/hydro/CMakeLists.txt. -
Register the component with the force model. Two options:
- Via HydroModelBuilder: call
.AddComponent(std::make_unique<YourComponent>(...))on the builder before.Build(). - Manual assembly: construct the component directly and include it in the
std::vector<std::unique_ptr<IHydroForceComponent>>passed toHydroForces.
- Via HydroModelBuilder: call
Example: see DampingComponent in
libs/hydro/include/seastack/hydro/force_components/damping_component.h for
a simple component that reads from SystemState and accumulates into
BodyForces.
Testing: add a unit test under tests/unit/. Chrono-free components can
be tested without a solver — construct a SystemState, call Compute(),
and check the output BodyForces.
Interface: IPTOModel in
libs/pto/include/seastack/pto/pto_model.h
namespace seastack::pto {
class IPTOModel {
public:
virtual ~IPTOModel() = default;
virtual double ComputeForce(double displacement,
double velocity,
double time) = 0;
};
} // namespace seastack::ptoSteps:
-
Create a header and source file under
libs/pto/include/seastack/pto/andlibs/pto/src/. -
Subclass
IPTOModeland implementComputeForce(). -
Add the source file to
SEAStack::PTOinlibs/pto/CMakeLists.txt. -
PTO has no dependencies beyond the C++ standard library. Do not introduce Eigen, HDF5, or Chrono dependencies.
Chrono integration: when running inside Chrono, PTOForceFunctor in the
adapter layer wraps any IPTOModel and connects it to a ChLinkTSDA. No
changes to the adapter are needed for new PTO models.
Example: LinearPTO in libs/pto/include/seastack/pto/linear_pto.h
(spring-damper: F = -kx - cv). For a complex example, see
RectifiedHydraulicPTO which includes sub-stepping, accumulators, and an
optional controller.
Testing: add a unit test under tests/unit/. PTO tests are chrono-free
and run in milliseconds.
Interface: IController in
libs/control/include/seastack/control/controller.h
namespace seastack::control {
class IController {
public:
virtual ~IController() = default;
virtual double Compute(double measurement, double time) = 0;
virtual void Reset() {}
};
} // namespace seastack::controlSteps:
-
Create a header under
libs/control/include/seastack/control/. Control is header-only — no source files orCMakeLists.txtchanges needed unless your controller requires a.cpp. -
Subclass
IController. ImplementCompute()and optionallyReset(). -
Control has no dependencies beyond the C++ standard library. Keep it that way.
Usage with PTO: controllers are typically connected to a PTO model (e.g.
RectifiedHydraulicPTO accepts an optional IController* for motor speed
regulation).
Example: PIController in
libs/control/include/seastack/control/pi_controller.h (proportional-integral
with anti-windup). Also see the ProportionalController in
examples/standalone_controller/main.cpp for a minimal inline example.
Testing: add a unit test under tests/unit/. Controller tests are
chrono-free.
Pattern: adapters/chrono/ — the existing Chrono adapter.
The adapter layer bridges a dynamics engine to SEA-Stack's domain libraries.
The key coupling point is ChronoHydroCoupler, which:
- Extracts body states from the solver into
SystemState - Calls
HydroForces::Evaluate(state, time)to getBodyForces - Maps forces back into the solver's representation
Chrono and infinite-frequency added mass: For N hydro bodies, let
nDoF = 6N. The coupled infinite-frequency added-mass matrix is
nDoF × nDoF (including hydrodynamic cross-coupling between bodies).
Project Chrono's ChLoadHydrodynamics takes N blocks of shape
6 × nDoF: each block is one body's six rows against all hydro DOFs (see
tests/unit/test_added_mass_determinism.cpp). HydroSystem
(adapters/chrono/src/hydro_system.cpp) registers those blocks from the per-body
inf_added_mass arrays read from BEMIO HDF5 (dimensions follow the file:
6×(6N) when the full coupled strip is stored, 6×6 for a self-only
block).
Data flow for a non-Chrono multibody solver (aligned with TECHNICAL_OVERVIEW.md Appendix F.4.1–F.4.2):
graph LR
subgraph other [Other Solver]
Solver["MBD Solver"]
Adapter["Custom Adapter"]
end
subgraph seastack [SEA-Stack Libraries]
HydroIO["HydroIO<br/>(H5 import)"]
HydroForcesLib["HydroForces<br/>(force evaluation)"]
end
Solver -->|"body states"| Adapter
Adapter -->|"SystemState"| HydroForcesLib
HydroForcesLib -->|"BodyForces"| Adapter
Adapter -->|"applied forces"| Solver
HydroIO -->|"HydroData"| HydroForcesLib
Steps for a new adapter:
-
Create a directory
adapters/<solver_name>/withinclude/andsrc/subdirectories. -
Implement a coupler analogous to
ChronoHydroCoupler:- Extract positions and velocities from your solver's body types into
SystemState - Call
HydroForces::Evaluate() - Apply the returned
BodyForcesto your solver
- Extract positions and velocities from your solver's body types into
-
Handle added mass. The full operator is
nDoF × nDoFwithnDoF = 6NforNhydro bodies (not independent6×6blocks unless cross-coupling is negligible or omitted in the data). With Chrono,HydroSystemwiresChLoadHydrodynamicsas above. With any other MBD package, SEA-Stack does not provide a generic injector: you must map the sameinf_added_massstrips (and any off-diagonal coupling your file format encodes) into that solver's mass assembly. -
Wire PTO forces. Create a functor analogous to
PTOForceFunctorthat connectsIPTOModel::ComputeForce()to your solver's joint/link mechanism. -
Add a CMake target in
adapters/<solver_name>/CMakeLists.txt. LinkSEAStack::Core,SEAStack::Hydro,SEAStack::PTO, and your solver.
Reference: the Chrono adapter in adapters/chrono/ is the canonical
example. See ChronoHydroCoupler for the SystemState / BodyForces
bridge, HydroSystem for ChLoadHydrodynamics and added mass, and
PTOForceFunctor for PTO integration.
See also TECHNICAL_OVERVIEW.md Appendix F.3
(subsections F.3.1–F.3.4) and F.4 (F.4.1–F.4.3) for Chrono diagrams, the
run_seastack lifecycle flowchart, and alternate-solver / light-target patterns.
| Suite | Label | Purpose | Reference comparison |
|---|---|---|---|
| Unit | unit |
Library-level checks, mostly chrono-free | None (assertions) |
| Regression | regression |
Simulation output vs frozen ss_ref_*.txt |
L2/L-inf norms |
| Verification | verification |
External/multi-code baselines | data/verification/ |
| Comparison | comparison |
Two SEA-Stack configs compared | Python diff |
| Benchmark | benchmark |
Wall-clock timing | None (JSON output) |
See tests/README.md for how to run suites and track
overview; for per-case CTest names and advanced runner notes, see
tests/TEST_SUITES_REFERENCE.md.
-
Create
tests/unit/test_<name>.cpp. -
Use
TEST_ASSERT/TEST_NEARmacros (see existing tests for patterns). -
Register in
tests/unit/CMakeLists.txt:add_executable(test_my_feature test_my_feature.cpp) target_link_libraries(test_my_feature PRIVATE SEAStack::Hydro) # or appropriate target add_test(NAME test_unit_my_feature COMMAND test_my_feature) set_tests_properties(test_unit_my_feature PROPERTIES LABELS "unit;chrono-free")
-
Create the simulation executable under
tests/regression/<family>/. -
Run once to produce output, review it, and commit the reference file as
ss_ref_<case>.txt. -
Register the CTest run step and a Python comparison step. Use the naming functions from
cmake/SeaStackTestNaming.cmake:include(SeaStackTestNaming) seastack_ctest_name(regression sphere my_case RUN run_name) seastack_ctest_name(regression sphere my_case REFERENCE ref_name)
-
Reference data (
ss_ref_*.txt) is never regenerated — it is the frozen ground truth. If physics change, update the reference deliberately.
YAML-driven demo cases go under data/demos/run_seastack/<model>/. Each
case directory contains a simulation YAML, model YAML, and hydro config.
See data/demos/run_seastack/README.md
for the catalog and file conventions.
- Preserve existing APIs unless explicitly extending them.
- Prefer simple, readable code over heavy abstractions or templates.
- Document units and sign conventions in new headers.
- All existing regression tests must pass after your changes.
- Follow the Google C++ Style Guide and the conventions in CONTRIBUTING.md.