From a0d34cc6e6948f76b339e9bbc316cdc943f3a223 Mon Sep 17 00:00:00 2001 From: Salman Husain Date: Wed, 10 Jun 2026 16:41:20 -0600 Subject: [PATCH 1/8] build(linux): prefer Chrono's bundled yaml-cpp and adapt to Chrono 10 API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small build fixes that together get SEA-Stack compiling cleanly on Linux against Chrono 10.0.0. * adapters/chrono/CMakeLists.txt — prefer Chrono::yaml-cpp over a system yaml-cpp when both are visible. Mixing Chrono's bundled yaml-cpp headers (from chrono_thirdparty/yaml-cpp/include) with a system libyaml-cpp.so triggers link errors against internal symbols such as YAML::FpToString and YAML::Emitter::Write. * apps/seastack/single_run.cpp — Chrono 10 moved the protected m_script_directory member of ChParserMbsYAML into an internal file handler; the script directory must now be set via m_file_handler.SetReferenceDirectory(dir). --- adapters/chrono/CMakeLists.txt | 28 +++++++++++++++++----------- apps/seastack/single_run.cpp | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/adapters/chrono/CMakeLists.txt b/adapters/chrono/CMakeLists.txt index 46f30a5..ae95378 100644 --- a/adapters/chrono/CMakeLists.txt +++ b/adapters/chrono/CMakeLists.txt @@ -24,19 +24,25 @@ target_include_directories(seastack_chrono_adapter # Chrono MBS YAML registers TSDA/RSDA force functors without copying damping onto the link; # simulation_export reads damping_coefficient from model YAML when GetDampingCoefficient()==0. -find_package(yaml-cpp CONFIG QUIET) -if(NOT yaml-cpp_FOUND) - find_package(yaml-cpp QUIET) -endif() -if(TARGET yaml-cpp::yaml-cpp) - set(_seastack_yaml_cpp_target yaml-cpp::yaml-cpp) -elseif(TARGET yaml-cpp) - set(_seastack_yaml_cpp_target yaml-cpp) -elseif(TARGET Chrono::yaml-cpp) - # Chrono's config often provides yaml-cpp as an imported target without a system package. +# +# Prefer Chrono's bundled yaml-cpp when available so that the yaml-cpp headers +# (which come from Chrono's chrono_thirdparty/yaml-cpp/include via Chrono::Chrono_core) +# match the linked library. Mixing a system libyaml-cpp.so with Chrono's headers can +# produce link errors against internal symbols (e.g. YAML::FpToString, YAML::Emitter::Write). +if(TARGET Chrono::yaml-cpp) set(_seastack_yaml_cpp_target Chrono::yaml-cpp) else() - message(FATAL_ERROR "yaml-cpp not found (required for Chrono adapter).") + find_package(yaml-cpp CONFIG QUIET) + if(NOT yaml-cpp_FOUND) + find_package(yaml-cpp QUIET) + endif() + if(TARGET yaml-cpp::yaml-cpp) + set(_seastack_yaml_cpp_target yaml-cpp::yaml-cpp) + elseif(TARGET yaml-cpp) + set(_seastack_yaml_cpp_target yaml-cpp) + else() + message(FATAL_ERROR "yaml-cpp not found (required for Chrono adapter).") + endif() endif() target_link_libraries(seastack_chrono_adapter diff --git a/apps/seastack/single_run.cpp b/apps/seastack/single_run.cpp index 3c3b36b..56d8918 100644 --- a/apps/seastack/single_run.cpp +++ b/apps/seastack/single_run.cpp @@ -123,7 +123,7 @@ namespace { class HCParser : public ::chrono::parsers::ChParserMbsYAML { public: HCParser() : ChParserMbsYAML() {} - void SetScriptDir(const std::string& dir) { m_script_directory = dir; } + void SetScriptDir(const std::string& dir) { m_file_handler.SetReferenceDirectory(dir); } }; static std::shared_ptr<::chrono::ChBody> FindBodyByName(::chrono::ChSystem& system, From 4b12d7db31f752974b6de8beba65fc00b59ffbf2 Mon Sep 17 00:00:00 2001 From: Salman Husain Date: Wed, 10 Jun 2026 16:41:20 -0600 Subject: [PATCH 2/8] fix(export): log joint reactions for all ChLink subclasses The generic joint reaction extraction in SimulationExporter::RecordStep was guarded by `dynamic_cast`, which silently fell through to an "all zeros" fallback for every other ChLink subclass -- including ChLinkUniversal (used by the 5SA articulated WEC demos), ChLinkRevolute, ChLinkRevoluteSpherical, and others. GetReaction1, GetReaction2, GetFrame1Abs, and GetFrame2Abs are virtual on ChLinkBase, so they resolve uniformly for any concrete joint type. Calling them on the base ChLink pointer fixes the export for all current and future joint types. Validation (5SA bimodal, 600s): Before: joint_12..45 max |F| = 0.000 N for entire run After: joint_12 max |F| = 192,471 N max |T| = 158,075 N*m joint_23 max |F| = 211,687 N max |T| = 220,279 N*m joint_34 max |F| = 201,941 N max |T| = 311,303 N*m joint_45 max |F| = 169,830 N max |T| = 401,657 N*m Internal consistency: reaction1_force == -reaction2_force to machine precision (Newton's third law). --- adapters/chrono/src/simulation_export.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/adapters/chrono/src/simulation_export.cpp b/adapters/chrono/src/simulation_export.cpp index 5e7a60c..c9e55c2 100644 --- a/adapters/chrono/src/simulation_export.cpp +++ b/adapters/chrono/src/simulation_export.cpp @@ -1082,13 +1082,18 @@ void SimulationExporter::RecordStep(::chrono::ChSystem* system) { } // ── Generic joint reactions (level-gated) ─────────────────────────────── + // GetReaction1/2 and GetFrame1Abs/2Abs are virtual on ChLinkBase, so they + // work for every concrete joint type (ChLinkLock, ChLinkUniversal, + // ChLinkRevoluteSpherical, ...). Previously this code dynamic_cast<>'d to + // ChLinkLock* only, silently writing zeros for every other joint type -- + // including the ChLinkUniversal joints used by the 5SA articulated WEC demos. for (auto& j : impl_->joints) { auto* L = j.link; - if (auto* lock = dynamic_cast<::chrono::ChLinkLock*>(L)) { + if (L) { try { - auto w1 = lock->GetReaction1(); - auto F1 = lock->GetFrame1Abs().TransformDirectionLocalToParent(w1.force); - auto T1 = lock->GetFrame1Abs().TransformDirectionLocalToParent(w1.torque); + auto w1 = L->GetReaction1(); + auto F1 = L->GetFrame1Abs().TransformDirectionLocalToParent(w1.force); + auto T1 = L->GetFrame1Abs().TransformDirectionLocalToParent(w1.torque); j.react_force_b1.insert(j.react_force_b1.end(), {F1.x(), F1.y(), F1.z()}); j.react_torque_b1.insert(j.react_torque_b1.end(), {T1.x(), T1.y(), T1.z()}); } catch (const std::exception& ex) { @@ -1098,9 +1103,9 @@ void SimulationExporter::RecordStep(::chrono::ChSystem* system) { } if (!is_compact) { try { - auto w2 = lock->GetReaction2(); - auto F2 = lock->GetFrame2Abs().TransformDirectionLocalToParent(w2.force); - auto T2 = lock->GetFrame2Abs().TransformDirectionLocalToParent(w2.torque); + auto w2 = L->GetReaction2(); + auto F2 = L->GetFrame2Abs().TransformDirectionLocalToParent(w2.force); + auto T2 = L->GetFrame2Abs().TransformDirectionLocalToParent(w2.torque); j.react_force_b2.insert(j.react_force_b2.end(), {F2.x(), F2.y(), F2.z()}); j.react_torque_b2.insert(j.react_torque_b2.end(), {T2.x(), T2.y(), T2.z()}); } catch (const std::exception& ex) { From f85e175c5dd48f536335879efdbe6b8abcb12d9d Mon Sep 17 00:00:00 2001 From: Salman Husain Date: Wed, 10 Jun 2026 16:41:20 -0600 Subject: [PATCH 3/8] fix(5sa/bimodal): add heave/pitch/yaw damping and frequency-domain excitation The bimodal case had linear_damping = [0, 100000, 0, 500000, 0, 0] (zero linear damping in surge, heave, pitch, and yaw) and no quadratic damping at all. The bimodal swell + wind-sea spectrum contains energy near the chain's rigid-body heave natural frequency, and with no restoring damping in heave the mode integrated unboundedly: at t=198.86s body3 crossed 5 m/s, and by t=280s body z-positions exceeded +10 m above SWL with the entire 144-m chain out of the water. The fix mirrors the damping already present in the spreading case (a near-identical multi-body configuration that runs to completion): linear_damping: [20000, 80000, 80000, 500000, 200000, 200000] quadratic_damping: [10000, 40000, 40000, 250000, 100000, 100000] It also adds an explicit `excitation: frequency_domain` block, again mirroring spreading. The bimodal swell and wind-sea have different headings, so excitation must be evaluated in the frequency domain -- this is auto-selected by the loader when wave components span multiple headings, but stating it explicitly matches the parity with spreading and makes intent clear. Validation (run_seastack 600s, no MoorDyn): Before: body3 max |v| = diverged past 5.0 m/s at t=198.86s body3 max z = +10.98 m at t=280s (chain in the air) run terminated at 280.7s After: body3 max |v| = 3.92 m/s at t=592.99s (bounded) body3 max z = within +/- 2 m throughout run completed full 600s, clean exit, 2m wallclock --- .../5sa/bimodal/5sa_bimodal.hydro.yaml | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/data/demos/run_seastack/5sa/bimodal/5sa_bimodal.hydro.yaml b/data/demos/run_seastack/5sa/bimodal/5sa_bimodal.hydro.yaml index 32569c1..1159c59 100644 --- a/data/demos/run_seastack/5sa/bimodal/5sa_bimodal.hydro.yaml +++ b/data/demos/run_seastack/5sa/bimodal/5sa_bimodal.hydro.yaml @@ -1,20 +1,31 @@ hydrodynamics: bodies: + # Surge/sway/heave/roll/pitch/yaw damping mirrored from the spreading + # case. The bimodal swell + wind sea combination contains spectral + # energy near the chain's heave natural frequency, and with zero + # linear and quadratic damping in heave/pitch/yaw the rigid-body + # heave mode runs away after ~200 s of accumulation (body z-position + # exceeded 10 m above SWL by t=280 s in the previous configuration). - name: body1 h5_file: ../assets/hydroData/5sa_directional.h5 - linear_damping: [0, 100000, 0, 500000, 0, 0] + linear_damping: [20000, 80000, 80000, 500000, 200000, 200000] + quadratic_damping: [10000, 40000, 40000, 250000, 100000, 100000] - name: body2 h5_file: ../assets/hydroData/5sa_directional.h5 - linear_damping: [0, 100000, 0, 500000, 0, 0] + linear_damping: [20000, 80000, 80000, 500000, 200000, 200000] + quadratic_damping: [10000, 40000, 40000, 250000, 100000, 100000] - name: body3 h5_file: ../assets/hydroData/5sa_directional.h5 - linear_damping: [0, 100000, 0, 500000, 0, 0] + linear_damping: [20000, 80000, 80000, 500000, 200000, 200000] + quadratic_damping: [10000, 40000, 40000, 250000, 100000, 100000] - name: body4 h5_file: ../assets/hydroData/5sa_directional.h5 - linear_damping: [0, 100000, 0, 500000, 0, 0] + linear_damping: [20000, 80000, 80000, 500000, 200000, 200000] + quadratic_damping: [10000, 40000, 40000, 250000, 100000, 100000] - name: body5 h5_file: ../assets/hydroData/5sa_directional.h5 - linear_damping: [0, 100000, 0, 500000, 0, 0] + linear_damping: [20000, 80000, 80000, 500000, 200000, 200000] + quadratic_damping: [10000, 40000, 40000, 250000, 100000, 100000] # Bimodal sea state: swell along +X, wind sea from beam (90 deg). waves: @@ -42,6 +53,11 @@ hydrodynamics: type: cos2s s: 8 + # Bimodal sea: swell and wind-sea have different headings, so excitation + # must be evaluated in the frequency domain (matches spreading case). + excitation: + method: frequency_domain + radiation: method: rirf_convolution smoothing: From 2172d0bc9133a71ef01ea8f9812f03afd3e020d9 Mon Sep 17 00:00:00 2001 From: Salman Husain Date: Wed, 10 Jun 2026 16:41:21 -0600 Subject: [PATCH 4/8] fix(gui/vsg): use opaque water surface to avoid runtime DepthSorted crash When the animated water surface is added to the VSG scene graph after ChVisualSystemVSG::Initialize() -- which happens because the wave model is set up at runtime, not pre-bind -- Chrono's wrapIfTransparent helper wraps the new node in a vsg::DepthSorted set to bin 10 if the material opacity is less than 1.0. The first record traversal that encounters this node then segfaults inside vsg::Bin::add() because the bin and its pipeline state were not present when Initialize() finalized the view's render path. Stack at the crash: #0 vsg::Bin::add(State*, double, Node const*) #1 vsg::RecordTraversal::apply(MatrixTransform const&) #2 vsg::Group::traverse(RecordTraversal&) ... #N chrono::vsg3d::ChVisualSystemVSG::Render() Water surface (kWaterOpacity=0.55) and wireframe overlay (SetOpacity(0.35)) both hit this. Setting both to 1.0 keeps the nodes out of the bin-10 DepthSorted path, so neither demos nor run_seastack crash on first render, including with the in-GUI wireframe toggle. Tradeoff: the water is opaque so submerged geometry is no longer visible through it. The proper fix is to compile any runtime-added transparent subtree against the live viewer (Chrono's ChShapeBuilderVSG owns an m_compileTraversal for this purpose but it is not exposed via SEA-Stack's public API surface). Tracking as a follow-up. --- apps/seastack/gui/vsg_config.h | 9 ++++++++- apps/seastack/gui/vsg_water_surface.cpp | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/seastack/gui/vsg_config.h b/apps/seastack/gui/vsg_config.h index 99d237d..bfe5bad 100644 --- a/apps/seastack/gui/vsg_config.h +++ b/apps/seastack/gui/vsg_config.h @@ -25,7 +25,14 @@ inline constexpr int kColorVariationCycle = 8; inline constexpr float kWaterR = 0.01f; inline constexpr float kWaterG = 0.20f; inline constexpr float kWaterB = 0.35f; -inline constexpr float kWaterOpacity = 0.55f; +// NOTE: Material opacity must stay at 1.0 on Linux. +// With opacity < 1.0, Chrono's wrapIfTransparent() wraps the runtime-added +// water node in vsg::DepthSorted (bin 10), which crashes the VSG renderer +// at vsg::Bin::add during the first record traversal. The animated water +// surface is added to the scene after pVis->Initialize(), bypassing the +// BindAll() compile path that built-in Chrono demos rely on. +// TODO: enable proper translucency via a runtime compile traversal. +inline constexpr float kWaterOpacity = 1.0f; inline constexpr float kWaterSpecular = 0.6f; inline constexpr float kWaterRoughness = 0.05f; inline constexpr float kWaterMetallic = 0.0f; diff --git a/apps/seastack/gui/vsg_water_surface.cpp b/apps/seastack/gui/vsg_water_surface.cpp index ecc097a..6c86e50 100644 --- a/apps/seastack/gui/vsg_water_surface.cpp +++ b/apps/seastack/gui/vsg_water_surface.cpp @@ -501,7 +501,7 @@ void AnimatedWaterSurface::InitializeWireframe() { // Faint blue-gray material. auto wire_material = ::chrono_types::make_shared<::chrono::ChVisualMaterial>(); wire_material->SetDiffuseColor(::chrono::ChColor(0.1f, 0.2f, 0.3f)); - wire_material->SetOpacity(0.35f); + wire_material->SetOpacity(1.0f); wire_material->SetRoughness(0.9f); wire_material->SetMetallic(0.0f); From 5c2b908b332aa1b27868d52c9df7c6219f9cfbfa Mon Sep 17 00:00:00 2001 From: Salman Husain Date: Wed, 10 Jun 2026 16:41:21 -0600 Subject: [PATCH 5/8] chore(scripts): mark unix shell scripts as executable Each script under scripts/unix/ was checked in with mode 100644 (no executable bit), so `./scripts/unix/build.sh` fails on a fresh Linux clone. Setting +x on all of them makes the standard invocation pattern work as documented. --- scripts/unix/build.sh | 0 scripts/unix/ctest_suite.sh | 0 scripts/unix/run_benchmarks.sh | 0 scripts/unix/run_chrono_free_tests.sh | 0 scripts/unix/run_comparison_tests.sh | 0 scripts/unix/run_regression_tests.sh | 0 scripts/unix/run_seastack_demo_smoke.sh | 0 scripts/unix/run_unit_tests.sh | 0 scripts/unix/run_verification_tests.sh | 0 9 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/unix/build.sh mode change 100644 => 100755 scripts/unix/ctest_suite.sh mode change 100644 => 100755 scripts/unix/run_benchmarks.sh mode change 100644 => 100755 scripts/unix/run_chrono_free_tests.sh mode change 100644 => 100755 scripts/unix/run_comparison_tests.sh mode change 100644 => 100755 scripts/unix/run_regression_tests.sh mode change 100644 => 100755 scripts/unix/run_seastack_demo_smoke.sh mode change 100644 => 100755 scripts/unix/run_unit_tests.sh mode change 100644 => 100755 scripts/unix/run_verification_tests.sh diff --git a/scripts/unix/build.sh b/scripts/unix/build.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/ctest_suite.sh b/scripts/unix/ctest_suite.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_benchmarks.sh b/scripts/unix/run_benchmarks.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_chrono_free_tests.sh b/scripts/unix/run_chrono_free_tests.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_comparison_tests.sh b/scripts/unix/run_comparison_tests.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_regression_tests.sh b/scripts/unix/run_regression_tests.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_seastack_demo_smoke.sh b/scripts/unix/run_seastack_demo_smoke.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_unit_tests.sh b/scripts/unix/run_unit_tests.sh old mode 100644 new mode 100755 diff --git a/scripts/unix/run_verification_tests.sh b/scripts/unix/run_verification_tests.sh old mode 100644 new mode 100755 From 79aa0c9b2542f3beef13f7516886ae1c14ba7908 Mon Sep 17 00:00:00 2001 From: dav-og <12831266+dav-og@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:11:16 +0100 Subject: [PATCH 6/8] fix(chrono10): keep m_script_directory for the Chrono 10.0.0 baseline a0d34cc switched HCParser::SetScriptDir to m_file_handler.SetReferenceDirectory(dir). That member exists on Chrono main, but not on the Chrono 10.0.0 release that SEA-Stack documents as its baseline, so the app fails to compile against 10.0.0. Restore the protected m_script_directory assignment, which is the Chrono 10.0.0 API. The yaml-cpp half of a0d34cc is kept unchanged -- preferring Chrono::yaml-cpp over a system yaml-cpp is correct on both Chrono versions and avoids the YAML::FpToString / YAML::Emitter::Write link errors Salhus reported. Verified on Ubuntu 22.04 against Chrono 10.0.0 built with Chrono's pinned VSG stack (vsg 1.1.11, vsgXchange 1.1.7, vsgImGui 0.7.0). --- apps/seastack/single_run.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/seastack/single_run.cpp b/apps/seastack/single_run.cpp index 56d8918..3c3b36b 100644 --- a/apps/seastack/single_run.cpp +++ b/apps/seastack/single_run.cpp @@ -123,7 +123,7 @@ namespace { class HCParser : public ::chrono::parsers::ChParserMbsYAML { public: HCParser() : ChParserMbsYAML() {} - void SetScriptDir(const std::string& dir) { m_file_handler.SetReferenceDirectory(dir); } + void SetScriptDir(const std::string& dir) { m_script_directory = dir; } }; static std::shared_ptr<::chrono::ChBody> FindBodyByName(::chrono::ChSystem& system, From 51505c34cca9857fc4609f3773f34e7936c86e25 Mon Sep 17 00:00:00 2001 From: dav-og <12831266+dav-og@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:11:51 +0100 Subject: [PATCH 7/8] Revert "fix(gui/vsg): use opaque water surface to avoid runtime DepthSorted crash" This reverts 2172d0b, restoring the intended translucent free surface (kWaterOpacity = 0.55) and the faint translucent wireframe overlay (SetOpacity(0.35)). The DepthSorted / vsg::Bin::add crash does not reproduce against the documented Chrono 10.0.0 baseline. Chrono 10.0.0 has no wrapIfTransparent() helper and does not wrap runtime-added nodes in vsg::DepthSorted, so a sub-1.0 material opacity never reaches the bin-10 path described in 2172d0b. That code was added on Chrono main after the 10.0.0 release, which is the most likely reason the crash was seen. Validated on Ubuntu 22.04.5 + RTX 3060 against Chrono 10.0.0 built with Chrono's pinned VSG stack (vsg 1.1.11, vsgXchange 1.1.7, vsgImGui 0.7.0, glslang 15.4.0): the 5SA bimodal GUI runs with translucent water and the wireframe toggle without any FATAL or renderer crash. Please retest on your Linux machine against Chrono 10.0.0 specifically. If the crash returns there, the VSG stack version is the thing to compare first, and we should fix the runtime compile traversal rather than force opacity to 1.0. --- apps/seastack/gui/vsg_config.h | 9 +-------- apps/seastack/gui/vsg_water_surface.cpp | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/seastack/gui/vsg_config.h b/apps/seastack/gui/vsg_config.h index bfe5bad..99d237d 100644 --- a/apps/seastack/gui/vsg_config.h +++ b/apps/seastack/gui/vsg_config.h @@ -25,14 +25,7 @@ inline constexpr int kColorVariationCycle = 8; inline constexpr float kWaterR = 0.01f; inline constexpr float kWaterG = 0.20f; inline constexpr float kWaterB = 0.35f; -// NOTE: Material opacity must stay at 1.0 on Linux. -// With opacity < 1.0, Chrono's wrapIfTransparent() wraps the runtime-added -// water node in vsg::DepthSorted (bin 10), which crashes the VSG renderer -// at vsg::Bin::add during the first record traversal. The animated water -// surface is added to the scene after pVis->Initialize(), bypassing the -// BindAll() compile path that built-in Chrono demos rely on. -// TODO: enable proper translucency via a runtime compile traversal. -inline constexpr float kWaterOpacity = 1.0f; +inline constexpr float kWaterOpacity = 0.55f; inline constexpr float kWaterSpecular = 0.6f; inline constexpr float kWaterRoughness = 0.05f; inline constexpr float kWaterMetallic = 0.0f; diff --git a/apps/seastack/gui/vsg_water_surface.cpp b/apps/seastack/gui/vsg_water_surface.cpp index 6c86e50..ecc097a 100644 --- a/apps/seastack/gui/vsg_water_surface.cpp +++ b/apps/seastack/gui/vsg_water_surface.cpp @@ -501,7 +501,7 @@ void AnimatedWaterSurface::InitializeWireframe() { // Faint blue-gray material. auto wire_material = ::chrono_types::make_shared<::chrono::ChVisualMaterial>(); wire_material->SetDiffuseColor(::chrono::ChColor(0.1f, 0.2f, 0.3f)); - wire_material->SetOpacity(1.0f); + wire_material->SetOpacity(0.35f); wire_material->SetRoughness(0.9f); wire_material->SetMetallic(0.0f); From 4861a0fc7dbfb3f7377d77c4c313f681edb29a1f Mon Sep 17 00:00:00 2001 From: dav-og <12831266+dav-og@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:12:23 +0100 Subject: [PATCH 8/8] fix(linux): allow HydroIO when HDF5Dir is unset On Ubuntu, scripts/unix/build.sh treated an empty HDF5Dir in build-config.json as "HDF5 missing" and forced SEASTACK_ENABLE_HYDRO_IO=OFF. CMake was therefore never given a chance to find the apt libhdf5-dev package, and builds that did want HydroIO failed later on a missing h5_reader.h rather than at configure time. The only workaround was an undocumented manual HDF5Dir entry. Two small changes: * scripts/unix/build.sh - leave HydroIO ON when HDF5Dir is unset and print an INFO note instead. CMake's FindHDF5 then runs normally, and a genuinely missing HDF5 fails at configure time with a clear error. * CMakeLists.txt - Chrono's ChronoConfig can leave HDF5_DIR cached as NOTFOUND when Chrono itself found HDF5 via module mode. Clear that stale value, then probe module-mode find_package(HDF5 COMPONENTS CXX) when HydroIO is enabled and HDF5 is not yet found. Validated on Ubuntu 22.04.5 with "HDF5Dir": "" and Chrono 10.0.0 + VSG: configure reports HydroIO ON, libseastack_hydro_io.a builds, and run_seastack links libhdf5_serial 1.10.7 via module mode while HDF5_DIR stays NOTFOUND. --- CMakeLists.txt | 7 ++++++- scripts/unix/build.sh | 15 ++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0bb271..72ff2e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -183,13 +183,18 @@ if(SEASTACK_ENABLE_CHRONO) " (or /usr/local/opt/libomp on Intel Homebrew).") endif() - # If Chrono set HDF5_DIR, retry the find + # If Chrono set HDF5_DIR to NOTFOUND (common when Chrono itself found HDF5 + # via module mode, not hdf5-config.cmake), clear it so later FindHDF5 + # module-mode discovery (e.g. Ubuntu libhdf5-dev) can succeed. if(NOT HDF5_FOUND AND DEFINED CACHE{HDF5_DIR}) if("${HDF5_DIR}" MATCHES "NOTFOUND") unset(HDF5_DIR CACHE) find_package(HDF5 CONFIG QUIET) endif() endif() + if(SEASTACK_ENABLE_HYDRO_IO AND NOT HDF5_FOUND) + find_package(HDF5 QUIET COMPONENTS CXX) + endif() # Use same C++ standard as Chrono for ABI compatibility if(DEFINED CHRONO_CXX_STANDARD) diff --git a/scripts/unix/build.sh b/scripts/unix/build.sh index f0170f8..4cfbf31 100755 --- a/scripts/unix/build.sh +++ b/scripts/unix/build.sh @@ -318,14 +318,15 @@ else USE_VSG=0 fi +# HydroIO / HDF5: +# Do not force HydroIO OFF merely because build-config.json leaves HDF5Dir empty. +# On Ubuntu/Debian, libhdf5-dev is discovered by CMake FindHDF5 (module mode) +# without a config-mode HDF5_DIR. Passing -DHDF5_DIR is optional; Chrono's +# ChronoConfig may even set HDF5_DIR to NOTFOUND when it used module-mode HDF5. +# Only --no-hydro-io disables HydroIO here. If CMake cannot find HDF5, configure +# fails with a clear FindHDF5 error instead of a later missing-header failure. if [[ -z "${SEASTACK_HDF5_DIR:-}" && "${NO_HYDRO_IO}" -eq 0 ]]; then - echo " [WARN] HDF5 not found; HydroIO will be disabled." >&2 - if [[ "${USE_CHRONO}" -eq 1 ]]; then - echo " If Chrono was built without HDF5, set HDF5Dir in build-config.json." >&2 - else - echo " For Chrono-free builds, set HDF5Dir or use --no-hydro-io." >&2 - fi - NO_HYDRO_IO=1 + echo " [INFO] HDF5Dir unset; HydroIO left ON for CMake HDF5 discovery (module/config)." >&2 fi if [[ "${MOORDYN}" -eq 1 && ! -f "${REPO_ROOT}/extern/MoorDyn/CMakeLists.txt" ]]; then