diff --git a/CHANGELOG.md b/CHANGELOG.md index cad86089daa4b14ffc66d8c5bfafcb11ef789ca0..e1018e433c473def66547fd00f3bea1c65663107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ to [Semantic Versioning][sem_ver]. ### Added +- Basic result summary table +- Basic result plotting function + ### Changed ### Deprecated diff --git a/RELEASE.md b/RELEASE.md index 9fe476dc0c60a8b4a05711b041adda4c55bf429b..d4a2ab4252bcf8b52ae64f8ee5bc16d3521e49bb 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -10,6 +10,4 @@ automatically generated and do not contain all dependencies. This release features: -- Functions to plot the velocity triangles -- Simpler interface to get the advance ratio -- **BREAKING** Output results as a separate object +- Result output and analysis methods diff --git a/src/classes/@Result/Result.m b/src/classes/@Result/Result.m index 325e083f69a7eae1485898c0e08c2271a9f4ab79..4fa4f0ed2ea2ee5dbe6d57a65b9e400c316c4db9 100644 --- a/src/classes/@Result/Result.m +++ b/src/classes/@Result/Result.m @@ -76,7 +76,8 @@ classdef Result < handle % Note: A bug in Matlab prevents the user of variable names when size checks are added % in the property definition. https://stackoverflow.com/q/48423003 self.operPts = val; - self.operPts.Properties.VariableNames = {'Alt', 'V_ax', 'RPM', 'Coll'}; + self.operPts.Properties.VariableNames = {'altitude', 'speed', 'rpm', 'collective'}; + end % --------------------------------------------- diff --git a/src/classes/@Result/plotperf.m b/src/classes/@Result/plotperf.m new file mode 100644 index 0000000000000000000000000000000000000000..7378db20d0ace6b75033fcd77ec0b65e8836eab5 --- /dev/null +++ b/src/classes/@Result/plotperf.m @@ -0,0 +1,117 @@ +function plotperf(self, varargin) + % PLOTPERF Plot and compare rotor performance. + % This method plots and compares the performance of the rotor under various operating + % conditions and for various solvers. + % + % ----- + % + % Syntax: + % Result.plotperf(perf, varOper, alt, rpm, coll, speed) Plot the rotor performance `perf` + % with respect to `varOper` for the operating point defined by `alt`, `rpm`, `coll`, `speed`. + % + % Result.plotperf(..., 'solvers', SolversList) Plots only the line for the solvers specified + % in `SolversList`. By default all solvers are used. + % + % Result.plotperf(..., 'newFig', true) Create the plot in a new figure. + % + % Inputs: + % perf : Performance metric to plot ('cT', 'cP', 'cQ') + % varOper : Operational points to plot against ('altitude', 'speed', 'rpm', 'collective') + % alt : Altitude, [m] + % rpm : Rotational speed, [rpm] + % coll : Collective pitch, [deg] + % speed : Axial velocity, [m/s] + % + % Outputs: + % plot(s) : Plots with the rotor performance. + % + % Example: + % ResRot.plotperf('cT', 'rpm', 0, 0, nan, 5, 'solvers', {'leishman'}) + % ResRot.plotperf('cT', 'collective', 0, 10, 200, 5, 'solvers', {'leishman','indvel'}) + % + % See also: rotare, Result, template. + % + % <a href="https://gitlab.uliege.be/rotare/documentation">Complete documentation (online)</a> + + % ---------------------------------------------------------------------------------------------- + % (c) Copyright 2022-2023 University of Liege + % Author: Thomas Lambert <t.lambert@uliege.be> + % ULiege - Aeroelasticity and Experimental Aerodynamics + % MIT License + % Repo: https://gitlab.uliege.be/rotare/rotare + % Docs: https://gitlab.uliege.be/rotare/documentation + % Issues: https://gitlab.uliege.be/rotare/rotare/-/issues + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % Defaults and constants + DEF.ALLOWED_PROPERTIES = {'cT', 'cQ', 'cP'}; + DEF.allowed_oper = self.operPts.Properties.VariableNames; + DEF.ALLOWED_SOLVERS = {'leishman', 'indfact', 'indvel', 'stahlhut'}; + + % Parse inputs + [perf, oper, solvers, operPt, newFig] = parseinputs(DEF, varargin{:}); + + % Plot the figure + if newFig + figure('Name', 'Rotor performance'); + end + + filtered = filtertable(self.summarize, ... + operPt.altitude, operPt.speed, ... + operPt.rpm, operPt.collective); + + for iSolv = 1:length(solvers) + perfSolv = sprintf('%s_%s', perf, solvers{iSolv}); + plot(filtered.(oper), filtered.(perfSolv), 'DisplayName', solvers{iSolv}); + if iSolv == 1 + hold on; + end + end + +end + +function [perf, oper, solvers, operPt, newFig] = parseinputs(DEF, varargin) + % PARSEINPUTS Parse the varargin inputs + % - Only the parameter corresponding to oper can be nan, all others need to be defined + + valData = @(x) validateattributes(x, {'numeric'}, {'scalar'}); + valLogi = @(x) validateattributes(x, {'logical'}, {'scalar'}); + + p = inputParser; + addRequired(p, 'perf', @(x) any(validatestring(x, DEF.ALLOWED_PROPERTIES))); + addRequired(p, 'oper', @(x) any(validatestring(x, DEF.allowed_oper))); + addRequired(p, 'altitude', valData); + addRequired(p, 'speed', valData); + addRequired(p, 'rpm', valData); + addRequired(p, 'collective', valData); + addParameter(p, 'solvers', {'leishman', 'indfact', 'indvel', 'stahlhut'}); + addParameter(p, 'newFig', false, valLogi); + + parse(p, varargin{:}); + perf = p.Results.perf; + oper = p.Results.oper; + solvers = p.Results.solvers; + operPt = struct('altitude', [], 'speed', [], 'rpm', [], 'collective', []); + for i = 1:length(DEF.allowed_oper) + curOp = DEF.allowed_oper{i}; + operPt.(curOp) = p.Results.(curOp); + end + operPt.(p.Results.oper) = NaN; + newFig = p.Results.newFig; +end + +function filtered = filtertable(inputTable, alt, speed, rpm, coll) + % FILTERTABLE Filter the data with corresponding alt, coll, rpm and speed from the summary table + + iAlt = getidx('altitude', alt); + iSpeed = getidx('speed', speed); + iRpm = getidx('rpm', rpm); + iColl = getidx('collective', coll); + + filtered = inputTable(iAlt & iColl & iRpm & iSpeed, :); + + function idx = getidx(type, val) + idx = inputTable.(type) == val | isnan(val); + end + +end