Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified Passive_Yaw/PassiveYawRegression/IrrYaw_org.mat
Binary file not shown.
37 changes: 37 additions & 0 deletions Passive_Yaw/PassiveYawRegression/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Passive-yaw regression baseline

The OSWEC irregular-wave regression runs for 250 s with phase seed 1 and
fixed-step `ode4` at 0.01 s. Both bodies use `yaw.threshold = 0`, which
interpolates heading-dependent hydrodynamic coefficients every step. The
previous one-degree coefficient hold produced strongly step-dependent
heading events and PTO work in all three published phase realizations.

`IrrYaw_org.mat` contains only the archived time, flap yaw, total yaw
torque, wave elevation, and spectrum needed by the regression. Its source
is the complete MATLAB R2025b run of WEC-Sim commit
`0753b2e47f2457c078751dcfe5d251d1767b80ab` and WEC-Sim_Applications
commit `d53d4d4c9eda2581f04204f5d394a6ef84bb099e`, with the two yaw
thresholds changed from 1 degree to zero. The archived source output is
from [this workflow run](https://github.com/cmudrc/wec-sim-python/actions/runs/37720514831)
(`matlab-reference-model-output`, `OSWEC_PASSIVE_YAW_IRR_CONT`). Its CSVs
provide `body1` column 1 for time, column 7 for yaw, and column 19 for
total yaw torque (one-based), plus the wave and component files. The
component amplitude divided by two is `waves.spectrum` in this WEC-Sim
version. The archived body, wave, and component CSV SHA-256 values are:

| CSV | SHA-256 |
| --- | --- |
| `body1` | `b8530d3f72ff6c1f842411b716e126c0abc177a39e604214fcbb1178a9e2238e` |
| wave | `be5906bf1e1a0dfc8d5ec5075473202fe72239b1727cd849869847863163a507` |
| components | `053265841734b875d53129e3dd5de92961a43f890a6004f3c68401ae5dcdb384` |

The archived artifact also contains the BEMIO-generated `oswec.h5` used by
that source run. Its byte hash differs across archived regenerations;
the CSV hashes above identify the stored trajectory and wave reference.

The baseline was assembled from those archived numeric outputs rather
than from source solver states during a comparison. To regenerate after an
intentional source change, run `regeneratePassiveYawBaseline` with that
WEC-Sim checkout on the MATLAB path and review the resulting baseline and
regression diff. The test compares the full yaw and torque trajectories,
both spectral moments, and the full wave time history.
30 changes: 30 additions & 0 deletions Passive_Yaw/PassiveYawRegression/regeneratePassiveYawBaseline.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function regeneratePassiveYawBaseline()
% Rebuild the compact phase-seed-1 reference after an intentional rebaseline.
% Run with the pinned WEC-Sim source on the MATLAB path. This overwrites the
% checked-in IrrYaw_org.mat, so inspect the resulting regression diff.
caseDir = fileparts(mfilename('fullpath'));
originalDir = pwd;
cleanup = onCleanup(@() cd(originalDir));
cd(caseDir);
wecSim;
assert(strcmp(simu.solver, 'ode4') && simu.dt == 0.01 && ...
simu.startTime == 0 && simu.endTime == 250 && ...
waves.phaseSeed == 1 && body(1).yaw.threshold == 0 && ...
body(2).yaw.threshold == 0, ...
'The input does not match the continuous-heading regression');
referenceTime = output.bodies(1).time;
referenceYaw = output.bodies(1).position(:,6);
referenceForce = output.bodies(1).forceTotal(:,6);
referenceOmega = waves.omega(:);
referenceSpectrum = waves.spectrum(:);
referenceWave = waves.waveAmpTime(:,2);
assert(numel(referenceTime) == 25001 && ...
isequal(size(referenceYaw),size(referenceTime), ...
size(referenceForce),size(referenceWave)) && ...
all(isfinite([referenceTime,referenceYaw,referenceForce, ...
referenceWave]),'all'), 'Incomplete passive-yaw reference');
save('IrrYaw_org.mat','referenceTime','referenceYaw', ...
'referenceForce','referenceOmega','referenceSpectrum', ...
'referenceWave','-v7');
close_system('OSWEC',0);
end
80 changes: 41 additions & 39 deletions Passive_Yaw/PassiveYawRegression/runLoadPassiveYawIrr.m
Original file line number Diff line number Diff line change
@@ -1,60 +1,62 @@
%% Run the WEC-Sim RM3 Test Case for 1DOF w/PTO
% This script will run the RM3 in WEC-Sim for irregular waves with Hs=2.5[m]
% and Tp=8[s]. The RM3 WEC models has 2 bodies, restricted to heave motion
% only, and has PTO damping=1200[kN-s/m].

%% Run Simulation
wecSim; % Run Simulation

%% Post-Process Data
% Flap position and force
IrrYaw.time_new=output.bodies(1).time;
IrrYaw.Pos_new=output.bodies(1).position(:,6);
IrrYaw.Force_new=output.bodies(1).forceTotal(:,6);
% Spectrum
%% Run the continuous-heading OSWEC passive-yaw regression
wecSim;

%% Compare the current run with the archived 250 s phase-seed-1 reference
reference = load('IrrYaw_org.mat');
assert(all(isfield(reference, {'referenceTime', 'referenceYaw', ...
'referenceForce', 'referenceOmega', 'referenceSpectrum', ...
'referenceWave'})), 'Passive-yaw reference is incomplete');
assert(body(1).yaw.threshold == 0 && body(2).yaw.threshold == 0 && ...
waves.phaseSeed == 1 && simu.endTime == 250 && simu.dt == 0.01, ...
'Passive-yaw input does not match the continuous-heading reference');

IrrYaw.time_new = output.bodies(1).time;
IrrYaw.Pos_new = output.bodies(1).position(:,6);
IrrYaw.Force_new = output.bodies(1).forceTotal(:,6);
IrrYaw.time_org = reference.referenceTime;
IrrYaw.Pos_org = reference.referenceYaw;
IrrYaw.Force_org = reference.referenceForce;
assert(isequal(size(IrrYaw.time_new), size(IrrYaw.time_org)) && ...
max(abs(IrrYaw.time_new - IrrYaw.time_org)) < 1e-10, ...
'Passive-yaw reference time grid differs');

IrrYaw.Sp.WEC_Sim_new.m0 = calcSpectralMoment(waves.omega,waves.spectrum,0);
IrrYaw.Sp.WEC_Sim_new.m2 = calcSpectralMoment(waves.omega,waves.spectrum,2);

%% Load Data
irrCICouput_new=output; % Keeps the new run in the workspace
load('IrrYaw_org.mat') % Load Previous WEC-Sim Data

%% Post-Process Data
% Flap position and force
IrrYaw.time_org=output.bodies(1).time;
IrrYaw.Pos_org=output.bodies(1).position(:,6);
IrrYaw.Force_org=output.bodies(1).forceTotal(:,6);
% Spectrum
IrrYaw.Sp.WEC_Sim_org.m0 = calcSpectralMoment(waves.omega,waves.spectrum,0);
IrrYaw.Sp.WEC_Sim_org.m2 = calcSpectralMoment(waves.omega,waves.spectrum,2);

%% Quantify Maximum Difference Between Saved and Current WEC-Sim RunsReg
[IrrYaw.Pos_diff ,IrrYaw.Pos_i]=max(abs(IrrYaw.Pos_new-IrrYaw.Pos_org));
[IrrYaw.Force_diff ,IrrYaw.Force_i]=max(abs(IrrYaw.Force_new-IrrYaw.Force_org));
IrrYaw.Sp.WEC_Sim_org.m0 = calcSpectralMoment( ...
reference.referenceOmega,reference.referenceSpectrum,0);
IrrYaw.Sp.WEC_Sim_org.m2 = calcSpectralMoment( ...
reference.referenceOmega,reference.referenceSpectrum,2);
assert(isequal(size(waves.waveAmpTime(:,2)), size(reference.referenceWave)) && ...
max(abs(waves.waveAmpTime(:,1) - IrrYaw.time_org)) < 1e-10, ...
'Passive-yaw reference wave grid differs');
IrrYaw.Wave_diff = max(abs(waves.waveAmpTime(:,2) - ...
reference.referenceWave));

[IrrYaw.Pos_diff,IrrYaw.Pos_i] = max(abs(IrrYaw.Pos_new-IrrYaw.Pos_org));
[IrrYaw.Force_diff,IrrYaw.Force_i] = max(abs(IrrYaw.Force_new-IrrYaw.Force_org));
save('IrrYaw','IrrYaw')

%% Plot Old vs. New Comparison
figure(); % plots of yaw position
%% Plot archived and current trajectories
figure();
subplot(1,2,1)
plot(IrrYaw.time_new,IrrYaw.Pos_new,':k','LineWidth',1.4)
hold on; grid on;
plot(IrrYaw.time_org,IrrYaw.Pos_org,'-k')
xlabel('Time(s)'); ylabel('Yaw position (rad)')
title('Yaw position comparison')

subplot(1,2,2) % plots of yaw position
subplot(1,2,2)
plot(IrrYaw.time_new,IrrYaw.Force_new,':k','LineWidth',1.4)
hold on; grid on;
plot(IrrYaw.time_org,IrrYaw.Force_org,'-k');
xlabel('Time(s)');
ylabel('Total Yaw Force (N)');
ylabel('Total Yaw Force (N m)');
legend('New','Original');
title('Yaw force comparison');

sgtitle('Passive yaw regression, irregular wave')
savefig('figYawIrr');
savefig('figYawIrr');

%% Clear output and .slx directory
try
delete('OSWEC.slx.autosave')
if isfile('OSWEC.slx.autosave')
delete('OSWEC.slx.autosave')
end
4 changes: 2 additions & 2 deletions Passive_Yaw/PassiveYawRegression/wecSimInputFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@
body(1).mass = 12700; % User-Defined mass [kg]
body(1).inertia = [1.85e6 1.85e6 1.85e6]; % Moment of Inertia [kg-m^2]
body(1).yaw.option = 1; % Turn passive yaw ON
body(1).yaw.threshold = 1; % Set passive yaw threshold
body(1).yaw.threshold = 0; % Interpolate heading coefficients every step

% Base
body(2) = bodyClass('../../_Common_Input_Files/OSWEC/hydroData/oswec.h5'); % Initialize bodyClass for Base
body(2).geometryFile = '../../_Common_Input_Files/OSWEC/geometry/base.stl'; % Geometry File
body(2).mass = 999; % Placeholder mass for fixed body
body(2).inertia = [999 999 999]; % Placeholder inertia for fixed body
body(2).yaw.option = 1; % Turn passive yaw ON
body(2).yaw.threshold = 1; % Set passive yaw threshold
body(2).yaw.threshold = 0; % Interpolate heading coefficients every step

%% PTO and Constraint Parameters
% Fixed
Expand Down
4 changes: 4 additions & 0 deletions Passive_Yaw/TestPassiveYawRegression.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,9 @@ function irregYaw_2nd_Spectral_Moment(testCase)
fprintf(['2nd Order Spectral Moment, Diff = ' ...
num2str(max(abs(org-new))) '\n']);
end
function irregYaw_wave_elevation(testCase)
% Match the actual wave time history, not just its spectrum.
testCase.verifyEqual(testCase.IrrYaw.Wave_diff,0,'AbsTol',1e-10);
end
end
end