Skip to content

Latest commit

 

History

History
298 lines (218 loc) · 10.6 KB

File metadata and controls

298 lines (218 loc) · 10.6 KB

Extending SEA-Stack

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/

Add a new hydrodynamic force component

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::hydro

Steps:

  1. Create a header under libs/hydro/include/seastack/hydro/force_components/ and a source file under libs/hydro/src/force_components/.

  2. Subclass IHydroForceComponent. Implement Type() (add a value to HydroComponentType if needed) and Compute().

  3. Add the source file to the SEAStack::Hydro target in libs/hydro/CMakeLists.txt.

  4. 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 to HydroForces.

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.


Add a new PTO model

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::pto

Steps:

  1. Create a header and source file under libs/pto/include/seastack/pto/ and libs/pto/src/.

  2. Subclass IPTOModel and implement ComputeForce().

  3. Add the source file to SEAStack::PTO in libs/pto/CMakeLists.txt.

  4. 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.


Add a new controller

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::control

Steps:

  1. Create a header under libs/control/include/seastack/control/. Control is header-only — no source files or CMakeLists.txt changes needed unless your controller requires a .cpp.

  2. Subclass IController. Implement Compute() and optionally Reset().

  3. 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.


Add a new solver adapter

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:

  1. Extracts body states from the solver into SystemState
  2. Calls HydroForces::Evaluate(state, time) to get BodyForces
  3. 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
Loading

Steps for a new adapter:

  1. Create a directory adapters/<solver_name>/ with include/ and src/ subdirectories.

  2. Implement a coupler analogous to ChronoHydroCoupler:

    • Extract positions and velocities from your solver's body types into SystemState
    • Call HydroForces::Evaluate()
    • Apply the returned BodyForces to your solver
  3. Handle added mass. The full operator is nDoF × nDoF with nDoF = 6N for N hydro bodies (not independent 6×6 blocks unless cross-coupling is negligible or omitted in the data). With Chrono, HydroSystem wires ChLoadHydrodynamics as above. With any other MBD package, SEA-Stack does not provide a generic injector: you must map the same inf_added_mass strips (and any off-diagonal coupling your file format encodes) into that solver's mass assembly.

  4. Wire PTO forces. Create a functor analogous to PTOForceFunctor that connects IPTOModel::ComputeForce() to your solver's joint/link mechanism.

  5. Add a CMake target in adapters/<solver_name>/CMakeLists.txt. Link SEAStack::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.


Add or update tests and demos

Test taxonomy

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.

Adding a unit test

  1. Create tests/unit/test_<name>.cpp.

  2. Use TEST_ASSERT / TEST_NEAR macros (see existing tests for patterns).

  3. 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")

Adding a regression test

  1. Create the simulation executable under tests/regression/<family>/.

  2. Run once to produce output, review it, and commit the reference file as ss_ref_<case>.txt.

  3. 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)
  4. Reference data (ss_ref_*.txt) is never regenerated — it is the frozen ground truth. If physics change, update the reference deliberately.

Adding a demo case

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.


General guidelines

  • 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.