Repository navigation
Conversation
akeeste
left a comment
There was a problem hiding this comment.
Great work @basaucier! Some preliminary review comments included here. A few on some typical minor cleanup items and some on the workflow itself.
I recommend committing the various cluster results created in MHKiT for users. They should have the ipynb so that they can recreate results, but provide the precomputed clustered conditions as .mat files here ensures that this WEC-Sim workflow can be rerun easily without MHKiT.
Otherwise on the set-up files--these seem quite capable but first instinct is to pair them down a lot. They are quite long, which is mostly an issue for future understanding and maintenance by a broader team. Let's walk through the content in each and figure out what we need to keep
There was a problem hiding this comment.
Remove plots or wrap in an if statement that checks whether this is a single simulation or a batch simulation (typically whether the variable 'mcr' is present or not)
There was a problem hiding this comment.
This script has a lot of extraneous code that is a lot to review and for a user to understand. The checks are not incorrect, but largely unnecessary given that the WEC-Sim output structure is very fixed and rigid. Please reduce this file greatly. The key components are:
- for a given MCR run (wave condition), calculate the power produced.
- After all MCR runs, calculate the mean annual energy production given power produced in each wave conditions and its weight.
akeeste
left a comment
There was a problem hiding this comment.
Hi @basaucier Thanks for pushing this case forward. There is still a lot of simplification that I would like to see. This is only a demonstration and WEC-Sim's inputs and outputs are fairly rigid. By hardcoding a lot of post-processing, the case will be succinct, easy to understand, and less code to maintain. Let's talk in detail next week.
There was a problem hiding this comment.
This file is adding a lot of complexity and code to review. Please remove it and write the MCR file directly from the MHKiT notebook. This example https://mhkit-software.github.io/MHKiT/wecsim_power_performance_example.html#2.-Write-a-WEC-Sim-batch-file-for-the-given-clusters has a short demo on writing data from python directly to the MCR file format required by WEC-Sim.
There was a problem hiding this comment.
See comment on the python notebook, remove outdated files and only write the most accurate cluster case.
There was a problem hiding this comment.
This script has a lot of extraneous code that is a lot to review and for a user to understand. The checks are not incorrect, but largely unnecessary given that the WEC-Sim output structure is very fixed and rigid. Please reduce this file greatly. The key components are:
- for a given MCR run (wave condition), calculate the power produced.
- After all MCR runs, calculate the mean annual energy production given power produced in each wave conditions and its weight.
| if isstruct(output) && isfield(output, 'bodies') | ||
| bodyOut = output.bodies(1); | ||
| elseif isobject(output) && isprop(output, 'bodies') | ||
| bodyOut = output.bodies(1); | ||
| else | ||
| error('Could not find output.bodies.'); | ||
| end | ||
|
|
||
| %% Extract body 1 pitch velocity | ||
|
|
||
| if ~oswecHasMember(bodyOut, 'velocity') | ||
| error('Could not find output.bodies(1).velocity.'); | ||
| end | ||
|
|
||
| velocityRaw = oswecGetMember(bodyOut, 'velocity'); | ||
|
|
||
| [bodyVelocity, velocityTime] = oswecSignalToArrayAndTime(velocityRaw); | ||
|
|
||
| flapPitchVelocity = oswecExtractDof(bodyVelocity, 5); | ||
|
|
||
| %% Extract body 1 pitch position if available | ||
|
|
||
| flapPitch = []; | ||
|
|
||
| if oswecHasMember(bodyOut, 'position') | ||
|
|
||
| positionRaw = oswecGetMember(bodyOut, 'position'); | ||
|
|
||
| [bodyPosition, positionTime] = oswecSignalToArrayAndTime(positionRaw); | ||
|
|
||
| flapPitch = oswecExtractDof(bodyPosition, 5); | ||
|
|
||
| else | ||
|
|
||
| positionTime = []; | ||
|
|
||
| end |
There was a problem hiding this comment.
unless a run fails, this information is always present
| function value = oswecGetMember(obj, name) | ||
| % Get struct field or object property. | ||
|
|
||
| value = obj.(name); | ||
|
|
||
| end | ||
|
|
||
|
|
||
| function [x, t] = oswecSignalToArrayAndTime(signal) | ||
| % Convert common signal formats to numeric array and time. | ||
|
|
||
| t = []; | ||
|
|
||
| if isa(signal, 'timeseries') | ||
| t = signal.Time; | ||
| x = signal.Data; | ||
| x = squeeze(x); | ||
| return | ||
| end | ||
|
|
||
| if isstruct(signal) | ||
|
|
||
| if isfield(signal, 'time') | ||
| t = signal.time; | ||
| elseif isfield(signal, 'Time') | ||
| t = signal.Time; | ||
| end | ||
|
|
||
| if isfield(signal, 'signals') && isfield(signal.signals, 'values') | ||
| x = signal.signals.values; | ||
| elseif isfield(signal, 'Data') | ||
| x = signal.Data; | ||
| elseif isfield(signal, 'data') | ||
| x = signal.data; | ||
| else | ||
| x = signal; | ||
| end | ||
|
|
||
| else | ||
|
|
||
| x = signal; | ||
|
|
||
| end | ||
|
|
||
| if istable(x) | ||
| x = table2array(x); | ||
| end | ||
|
|
||
| x = squeeze(x); | ||
|
|
||
| end | ||
|
|
||
|
|
||
| function dofSignal = oswecExtractDof(x, dof) | ||
| %Extract DOF column from WEC-Sim response matrix. | ||
| % Handles common layouts: | ||
| % N x 6 columns are DOFs | ||
| % N x 7 first column is time, columns 2:7 are DOFs | ||
| % 6 x N rows are DOFs | ||
| % 7 x N first row is time, rows 2:7 are DOFs | ||
|
|
||
| if isvector(x) | ||
|
|
||
| dofSignal = x(:); | ||
| return | ||
|
|
||
| end | ||
|
|
||
| [nRows, nCols] = size(x); | ||
|
|
||
| if nCols == 6 | ||
| dofSignal = x(:, dof); | ||
|
|
||
| elseif nCols >= 7 | ||
| dofSignal = x(:, dof + 1); | ||
|
|
||
| elseif nRows == 6 | ||
| dofSignal = x(dof, :).'; | ||
|
|
||
| elseif nRows >= 7 | ||
| dofSignal = x(dof + 1, :).'; | ||
|
|
||
| else | ||
| error('Could not extract DOF %d from signal with size %d x %d.', dof, nRows, nCols); | ||
| end | ||
|
|
||
| dofSignal = dofSignal(:); | ||
|
|
||
| end No newline at end of file |
There was a problem hiding this comment.
unnecessarily length code. remove
|
|
||
| [bodyVelocity, velocityTime] = oswecSignalToArrayAndTime(velocityRaw); | ||
|
|
||
| flapPitchVelocity = oswecExtractDof(bodyVelocity, 5); |
There was a problem hiding this comment.
this variable can be defined with only:
flapPitchVelocity = output.bodies(1).velocity(:,5);
removing the need for much of the post-processing code
| %% MCR Damping-Sweep Setup | ||
|
|
||
| project_folder = fileparts(mfilename('fullpath')); | ||
|
|
||
| % Base wave-condition file generated by Python/MHKiT. | ||
| % This can be either your array-style .mat or your wave-condition mcr .mat. | ||
| baseWaveMcrFile = fullfile(project_folder, 'wave_conditions', ... | ||
| 'wave_conditions_buoy_46050_2020_2021_2022_2023_2024_2025_16_clusters.mat'); | ||
|
|
||
| % Runtime WEC-Sim-compatible MCR file. | ||
| runtimeMcrFile = fullfile(project_folder, 'wave_conditions', ... | ||
| 'oswec_runtime_damping_grid_mcr.mat'); | ||
|
|
||
| % PTO damping values for the grid search. | ||
| % Change this line to change the damping sweep. | ||
| dampingValues = [1e4 1e5 1e6 1e7 1e8].'; | ||
|
|
||
| % Build the runtime MCR file on the initial setup evaluation. | ||
| % During the actual MCR loop, variable mcr should already exist. | ||
| if ~exist('mcr', 'var') || ~isfield(mcr, 'cases') | ||
|
|
||
| runtimeMcrFile = oswecBuildDampingMCR( ... | ||
| baseWaveMcrFile, ... | ||
| dampingValues, ... | ||
| runtimeMcrFile); | ||
|
|
||
| data = load(runtimeMcrFile, 'mcr'); | ||
| mcr = data.mcr; | ||
|
|
||
| end | ||
|
|
||
| % Determine current MCR case index. | ||
| % In WEC-Sim MCR, the loop variable is commonly imcr. | ||
| if exist('imcr', 'var') | ||
| caseIndex = imcr; | ||
| elseif isfield(mcr, 'caseNum') | ||
| caseIndex = mcr.caseNum; | ||
| else | ||
| caseIndex = 1; | ||
| end |
There was a problem hiding this comment.
We don't want to rebuild the mcr variable every time this script runs. Point directly to the final result from the python notebook. It can be hardcoded for this case.
I suggest this pre-processing look for a single variable: pto damping, e.g. PTO_DAMPING. If this is not defined, pto.damping defaults to a certain value. If it is defined, it takes the value of PTO_DAMPING. Then a wrapper function can take in one argument, PTO_DAMPING, call wecSimMCR, and return the next mean annual energy production
| **Dependencies:** | ||
| * WEC-Sim | ||
| * MATLAB/Simulink | ||
| * Precomputed wave conditions (`.mat` files in `wave_conditions/`) |
There was a problem hiding this comment.
Move to a products.txt file for the tests
Summary
This draft PR adds an OSWEC PTO damping optimization application workflow.
The application connects buoy-derived wave resource characterization to WEC-Sim MCR package in order to estimate an optimal constant passive PTO damping value for the OSWEC case.