Skip to content

Commit c5229d0

Browse files
committed
Add irregular wave regression tests for sphere and OSWEC
Add irregular wave regression tests for sphere and OSWEC (convolution + state-space), with comparison scripts, reference data, and report integration.
1 parent d846171 commit c5229d0

11 files changed

Lines changed: 67263 additions & 1 deletion

data/reference_data/oswec/hc_ref_oswec_irreg_waves.txt

Lines changed: 13335 additions & 0 deletions
Large diffs are not rendered by default.

data/reference_data/oswec/hc_ref_oswec_irreg_waves_ss.txt

Lines changed: 13335 additions & 0 deletions
Large diffs are not rendered by default.

data/reference_data/sphere/hc_ref_sphere_irreg_waves_ss.txt

Lines changed: 40003 additions & 0 deletions
Large diffs are not rendered by default.

tests/regression/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,16 @@ build_regression_test(sphere sphere_decay FALSE)
7575
build_regression_test(sphere sphere_decay_ss FALSE)
7676
build_regression_test(sphere sphere_reg_waves TRUE)
7777
build_regression_test(sphere sphere_irreg_waves FALSE)
78+
build_regression_test(sphere sphere_irreg_waves_ss FALSE)
7879
build_regression_test(sphere sphere_irreg_waves_eta FALSE)
7980
build_regression_test(sphere sphere_irreg_waves_eta_consistency FALSE)
8081
build_regression_test(f3of f3of_dt1 FALSE)
8182
build_regression_test(f3of f3of_dt2 FALSE)
8283
build_regression_test(f3of f3of_dt3 FALSE)
8384
build_regression_test(oswec oswec_decay FALSE)
8485
build_regression_test(oswec oswec_decay_ss FALSE)
86+
build_regression_test(oswec oswec_irreg_waves FALSE)
87+
build_regression_test(oswec oswec_irreg_waves_ss FALSE)
8588
build_regression_test(oswec oswec_reg_waves TRUE)
8689
build_regression_test(rm3 rm3_decay FALSE)
8790
build_regression_test(rm3 rm3_reg_waves FALSE)
@@ -105,5 +108,5 @@ set_tests_properties(
105108
PROPERTIES
106109
LABELS "regression;report;documentation;core"
107110
ENVIRONMENT "PATH=${Python3_ROOT_DIR};$ENV{PATH};HYDROCHRONO_BUILD_DIR=${CMAKE_BINARY_DIR}"
108-
DEPENDS "sphere_decay_regression;sphere_decay_ss_regression;sphere_reg_waves_regression;sphere_irreg_waves_regression;sphere_irreg_waves_eta_regression;sphere_irreg_waves_eta_consistency_regression;f3of_dt1_regression;f3of_dt2_regression;f3of_dt3_regression;oswec_decay_regression;oswec_decay_ss_regression;oswec_reg_waves_regression;rm3_decay_regression;rm3_reg_waves_regression"
111+
DEPENDS "sphere_decay_regression;sphere_decay_ss_regression;sphere_reg_waves_regression;sphere_irreg_waves_regression;sphere_irreg_waves_ss_regression;sphere_irreg_waves_eta_regression;sphere_irreg_waves_eta_consistency_regression;f3of_dt1_regression;f3of_dt2_regression;f3of_dt3_regression;oswec_decay_regression;oswec_decay_ss_regression;oswec_irreg_waves_regression;oswec_irreg_waves_ss_regression;oswec_reg_waves_regression;rm3_decay_regression;rm3_reg_waves_regression"
109112
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env python3
2+
"""
3+
OSWEC Irregular Waves Regression Test Comparison
4+
5+
Compares OSWEC irregular wave results (convolution radiation) against reference data.
6+
"""
7+
8+
import sys
9+
import os
10+
from pathlib import Path
11+
12+
sys.path.append(os.path.join(os.path.dirname(__file__), '../utilities'))
13+
from compare_template import run_comparison
14+
15+
if __name__ == '__main__':
16+
if len(sys.argv) != 3:
17+
print("Usage: python compare_oswec_irreg_waves.py <reference_file> <results_file>")
18+
sys.exit(1)
19+
20+
ref_file = sys.argv[1]
21+
results_file = sys.argv[2]
22+
print("Reference file: ", ref_file)
23+
print("Results file: ", results_file)
24+
25+
test_file_path = Path(results_file)
26+
plots_dir = test_file_path.parent / "plots"
27+
plots_dir.mkdir(parents=True, exist_ok=True)
28+
print(f"Plot will be saved to: {plots_dir}")
29+
30+
test_name = "OSWEC Irregular Waves"
31+
y_label = "Flap Pitch (radians)"
32+
executable_patterns = ["oswec_irreg_waves_test", "oswec_irreg_waves"]
33+
pass_criteria = (1e-4, 0.02)
34+
35+
n1, n2, passed = run_comparison(
36+
ref_file, results_file, test_name, y_label,
37+
executable_patterns, pass_criteria,
38+
status_name="oswec_irreg_waves"
39+
)
40+
41+
sys.exit(0 if passed else 1)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env python3
2+
"""
3+
OSWEC Irregular Waves (State-Space Radiation) Regression Test Comparison
4+
5+
Compares OSWEC irregular wave results using state-space radiation approximation
6+
against reference data. Tolerances are slightly relaxed relative to the
7+
convolution test since state-space is an O(1) approximation.
8+
"""
9+
10+
import sys
11+
import os
12+
from pathlib import Path
13+
14+
sys.path.append(os.path.join(os.path.dirname(__file__), '../utilities'))
15+
from compare_template import run_comparison
16+
17+
if __name__ == '__main__':
18+
if len(sys.argv) != 3:
19+
print("Usage: python compare_oswec_irreg_waves_ss.py <reference_file> <results_file>")
20+
sys.exit(1)
21+
22+
ref_file = sys.argv[1]
23+
results_file = sys.argv[2]
24+
print("Reference file: ", ref_file)
25+
print("Results file: ", results_file)
26+
27+
test_file_path = Path(results_file)
28+
plots_dir = test_file_path.parent / "plots"
29+
plots_dir.mkdir(parents=True, exist_ok=True)
30+
print(f"Plot will be saved to: {plots_dir}")
31+
32+
test_name = "OSWEC Irregular Waves (State-Space)"
33+
y_label = "Flap Pitch (radians)"
34+
executable_patterns = ["oswec_irreg_waves_ss_test", "oswec_irreg_waves_ss"]
35+
pass_criteria = (5e-4, 0.05)
36+
37+
n1, n2, passed = run_comparison(
38+
ref_file, results_file, test_name, y_label,
39+
executable_patterns, pass_criteria,
40+
status_name="oswec_irreg_waves_ss"
41+
)
42+
43+
sys.exit(0 if passed else 1)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include <chrono>
2+
#include <filesystem>
3+
#include <iomanip>
4+
#include <vector>
5+
6+
#include <chrono/physics/ChBodyEasy.h>
7+
#include <chrono/physics/ChSystemNSC.h>
8+
9+
#include <hydroc/helper.h>
10+
#include <hydroc/hydro_system.h>
11+
12+
using namespace chrono;
13+
14+
int main(int argc, char* argv[]) {
15+
std::cout << "Chrono version: " << CHRONO_VERSION << "\n\n";
16+
17+
std::string data_dir;
18+
if (!hydroc::SetInitialEnvironment(data_dir)) return 1;
19+
20+
std::filesystem::path DATADIR(hydroc::getDataDir());
21+
22+
auto body1_meshfame = (DATADIR / "demos" / "oswec" / "geometry" / "flap.obj").lexically_normal().generic_string();
23+
auto body2_meshfame = (DATADIR / "demos" / "oswec" / "geometry" / "base.obj").lexically_normal().generic_string();
24+
auto h5fname = (DATADIR / "demos" / "oswec" / "hydroData" / "oswec.h5").lexically_normal().generic_string();
25+
26+
ChSystemNSC system;
27+
system.SetGravitationalAcceleration(ChVector3d(0.0, 0.0, -9.81));
28+
double timestep = 0.015;
29+
system.SetSolverType(ChSolver::Type::GMRES);
30+
double simulationDuration = hydroc::getSimDuration(200.0, 400.0);
31+
32+
std::vector<double> time_vector;
33+
std::vector<double> flap_rot;
34+
35+
std::cout << "Attempting to open mesh file: " << body1_meshfame << std::endl;
36+
std::shared_ptr<ChBody> flap_body = chrono_types::make_shared<ChBodyEasyMesh>(
37+
body1_meshfame,
38+
1000,
39+
false,
40+
true,
41+
false
42+
);
43+
44+
system.Add(flap_body);
45+
flap_body->SetName("body1");
46+
flap_body->SetPos(ChVector3d(0, 0, -3.9));
47+
flap_body->SetMass(127000.0);
48+
flap_body->SetInertiaXX(ChVector3d(1.85e6, 1.85e6, 1.85e6));
49+
50+
std::cout << "Attempting to open mesh file: " << body2_meshfame << std::endl;
51+
std::shared_ptr<ChBody> base_body = chrono_types::make_shared<ChBodyEasyMesh>(
52+
body2_meshfame,
53+
1000,
54+
false,
55+
true,
56+
false
57+
);
58+
59+
system.Add(base_body);
60+
base_body->SetName("body2");
61+
base_body->SetPos(ChVector3d(0, 0, -10.15));
62+
base_body->SetMass(999);
63+
base_body->SetInertiaXX(ChVector3d(1, 1, 1));
64+
65+
auto ground = chrono_types::make_shared<ChBody>();
66+
system.AddBody(ground);
67+
ground->SetPos(ChVector3d(0, 0, -10.15));
68+
ground->SetTag(-1);
69+
ground->SetFixed(true);
70+
ground->EnableCollision(false);
71+
72+
auto anchor = chrono_types::make_shared<ChLinkMateGeneric>();
73+
anchor->Initialize(base_body, ground, false, base_body->GetVisualModelFrame(), base_body->GetVisualModelFrame());
74+
system.Add(anchor);
75+
anchor->SetConstrainedCoords(true, true, true, true, true, true);
76+
77+
ChQuaternion<> revoluteRot = QuatFromAngleX(CH_PI / 2.0);
78+
auto revolute = chrono_types::make_shared<ChLinkLockRevolute>();
79+
revolute->Initialize(base_body, flap_body, ChFramed(ChVector3d(0.0, 0.0, -8.9), revoluteRot));
80+
system.AddLink(revolute);
81+
82+
// PTO damper between flap and base
83+
auto pto = chrono_types::make_shared<ChLinkRSDA>();
84+
pto->Initialize(flap_body, base_body, ChFramed(ChVector3d(0.0, 0.0, -8.9), revoluteRot));
85+
pto->SetSpringCoefficient(0.0);
86+
pto->SetDampingCoefficient(12.0e6);
87+
system.AddLink(pto);
88+
89+
std::vector<std::shared_ptr<ChBody>> bodies;
90+
bodies.push_back(flap_body);
91+
bodies.push_back(base_body);
92+
93+
IrregularWaveParams wave_inputs;
94+
wave_inputs.ramp_duration = 30.0;
95+
wave_inputs.wave_height = 2.5;
96+
wave_inputs.wave_period = 8.0;
97+
wave_inputs.frequency_min = 0.001;
98+
wave_inputs.frequency_max = 1.0;
99+
wave_inputs.nfrequencies = 1000;
100+
101+
std::shared_ptr<IrregularWaves> my_hydro_inputs;
102+
103+
try {
104+
my_hydro_inputs = std::make_shared<IrregularWaves>(wave_inputs);
105+
} catch (const std::exception& e) {
106+
std::cerr << "Caught exception: " << e.what() << '\n';
107+
return 1;
108+
} catch (...) {
109+
std::cerr << "Caught unknown exception.\n";
110+
return 1;
111+
}
112+
113+
if (!my_hydro_inputs) {
114+
std::cerr << "ERROR: Failed to create IrregularWaves object." << std::endl;
115+
return 1;
116+
}
117+
118+
HydroForces hydro_forces(bodies, h5fname);
119+
hydro_forces.AddWaves(my_hydro_inputs);
120+
121+
auto start = std::chrono::high_resolution_clock::now();
122+
123+
while (system.GetChTime() <= simulationDuration) {
124+
system.DoStepDynamics(timestep);
125+
time_vector.push_back(system.GetChTime());
126+
flap_rot.push_back(flap_body->GetRot().GetCardanAnglesXYZ().y());
127+
}
128+
129+
auto end = std::chrono::high_resolution_clock::now();
130+
unsigned duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
131+
std::cout << "Simulation completed in " << duration << " ms" << std::endl;
132+
133+
std::string out_dir = hydroc::getTestOutDir() + "/" + RESULTS_DIR_NAME;
134+
std::filesystem::create_directories(std::filesystem::path(out_dir));
135+
136+
std::ofstream outputFile(out_dir + "/" + RESULTS_FILE_NAME + ".txt");
137+
if (outputFile.is_open()) {
138+
outputFile << std::left << std::setw(10) << "Time (s)" << std::right << std::setw(16)
139+
<< "Flap Rotation y (radians)" << std::right << std::setw(16) << "Flap Rotation y (degrees)"
140+
<< std::endl;
141+
for (size_t i = 0; i < time_vector.size(); ++i)
142+
outputFile << std::left << std::setw(10) << std::setprecision(3) << std::fixed << time_vector[i]
143+
<< std::right << std::setw(16) << std::setprecision(6) << std::fixed << flap_rot[i]
144+
<< std::right << std::setw(16) << std::setprecision(6) << std::fixed
145+
<< flap_rot[i] * 360.0 / (2.0 * CH_PI) << std::endl;
146+
outputFile.close();
147+
} else {
148+
std::cout << "Error: Could not open output file for writing." << std::endl;
149+
return 1;
150+
}
151+
152+
return 0;
153+
}

0 commit comments

Comments
 (0)