From d80f2a935403e4acb620b15114aad6115cd55c23 Mon Sep 17 00:00:00 2001
From: Thomas Lambert <t.lambert@uliege.be>
Date: Sun, 10 Apr 2022 21:21:55 +0200
Subject: [PATCH] feat: add refine to uiuccleaner

---
 +af_tools/+utils/parsefileinputs.m |  6 ++++-
 +af_tools/+utils/spacedvector.m    | 15 +++++++++++++
 +af_tools/uiuccleaner.m            | 35 ++++++++++++++++++++++++++----
 README.md                          |  1 +
 tests/test_uiuccleaner.m           | 30 ++++++++++++++++++++++++-
 5 files changed, 81 insertions(+), 6 deletions(-)
 create mode 100644 +af_tools/+utils/spacedvector.m

diff --git a/+af_tools/+utils/parsefileinputs.m b/+af_tools/+utils/parsefileinputs.m
index f68462f..6b51f2a 100644
--- a/+af_tools/+utils/parsefileinputs.m
+++ b/+af_tools/+utils/parsefileinputs.m
@@ -106,6 +106,10 @@ end
 function bool = isoption(optList, var)
 % ISOPTION Returns true if var is contained in option list
 
-bool = any(strcmpi(var,optList));
+if iscell(var)
+    bool = false;
+else
+    bool = any(strcmpi(var,optList));
+end
 end
 
diff --git a/+af_tools/+utils/spacedvector.m b/+af_tools/+utils/spacedvector.m
new file mode 100644
index 0000000..3601a13
--- /dev/null
+++ b/+af_tools/+utils/spacedvector.m
@@ -0,0 +1,15 @@
+function vect = spacedvector(spacing, nPoints)
+% SPACEDVECTOR Returns a vector based on chosen spacing and number of points
+
+switch spacing
+    case 'linear'
+        vect = linspace(0, 1, (nPoints));
+    case 'halfcosine'
+        beta = linspace(0, pi, (nPoints));
+        vect = (1-cos(beta))/2;
+    case 'cosine'
+        beta = linspace(0, pi/2, (nPoints));
+        vect = 1-cos(beta);
+end
+
+end
diff --git a/+af_tools/uiuccleaner.m b/+af_tools/uiuccleaner.m
index 3503ba1..2226312 100644
--- a/+af_tools/uiuccleaner.m
+++ b/+af_tools/uiuccleaner.m
@@ -39,6 +39,10 @@ function Dat = uiuccleaner(varargin)
 %   overwrite is true, the file will be overwritten anyway. If autosave is set
 %   to false and overwrite is true, the file will not be saved at all.
 %
+%   DAT = UIUCCLEANER(..., 'refine', true) refines the input data by using a
+%   spline interpolation with 100 coordinates in total, spaced following the
+%   half-cosine rule. False by default.
+%
 % Inputs:
 %   inputDir   : Path of the directory with the UIUC oridinal dat-files
 %   inputFiles : Files to select (ex: '*' (default), '*0012*', {'file1','file2'}, etc)
@@ -57,6 +61,7 @@ function Dat = uiuccleaner(varargin)
 %   UIUCCLEANER('test_data', {'0012_re1e5', '0012_re1e6'})
 %   UIUCCLEANER('test_data', 'autosave', true)
 %   UIUCCLEANER('test_data', 'overwrite', true)
+%   UIUCCLEANER('test_data', 'refine', true)
 %
 % See also: NACAAIRFOIL.
 
@@ -69,18 +74,20 @@ function Dat = uiuccleaner(varargin)
 % https://gitlab.uliege.be/am-dept/matlab_airfoil_toolbox
 % ------------------------------------------------------------------------------
 
-narginchk(0,6);
+narginchk(0,8);
 
 % Import other functions from this package
 import af_tools.utils.*
 
 % Constants and defaults
-OPTION_LIST = {'autosave', 'overwrite'};
+OPTION_LIST = {'autosave', 'overwrite', 'refine'};
 FILETYPE = '.dat';
+REFINED_SPACING = 'halfcosine';
+REFINED_NPOINTS = ceil((100+1)/2);
 
 % Parse inputs
 [allFileNames, fullpath, idxOpts] = parsefileinputs(OPTION_LIST, FILETYPE, varargin{:});
-[autosave, overwrite] = parseoptioninputs(varargin{idxOpts:end});
+[autosave, overwrite, refine] = parseoptioninputs(varargin{idxOpts:end});
 
 % Convert filenames to string array
 allFileNames = string(allFileNames);
@@ -115,14 +122,31 @@ for i=1:nbFiles
         upper = flipud(resultArray(1:idx,:));
         lower = resultArray(idx+1:end,:);
         resultArray = [upper;lower(2:end,:)];
+    end
+    
+    % Refine coordinates to add more points
+    if refine && size(resultArray,1) < 2*REFINED_NPOINTS
+        xc = fliplr(spacedvector(REFINED_SPACING, REFINED_NPOINTS))';
+        
+        idx = find(diff(resultArray(:,1)) < 0, 1, 'last');
+        upper = resultArray(1:idx+1,:);
+        lower = resultArray(idx+1:end,:);
+        
+        refinedUpperY = interp1(upper(:,1),upper(:,2),xc,'spline');
+        refinedLowerY = interp1(lower(:,1),lower(:,2),flipud(xc),'spline');
         
+        x = [xc; flipud(xc(1:end-1))];
+        y = [refinedUpperY; refinedLowerY(2:end)];
+        resultArray = [x,y];
     end
+    
     % Output structure
     Dat(i).path = fullpath;
     Dat(i).file = allFileNames{i};
     Dat(i).airfoil = char(cellstr(tmpAirfoil{:}));
     Dat(i).x = resultArray(:,1);
     Dat(i).y = resultArray(:,2);
+    
 end
 
 
@@ -150,7 +174,7 @@ end
 
 
 % ------------------------------------------------------------------------------
-function [autosave, overwrite] = parseoptioninputs(varargin)
+function [autosave, overwrite, refine] = parseoptioninputs(varargin)
 % PARSEOPTIONINPUTS Parses the options and checks their validity
 
 import af_tools.utils.*
@@ -158,6 +182,7 @@ import af_tools.utils.*
 % Constants and defaults
 DEFAULT_AUTOSAVE = false;
 DEFAULT_OVEWRITE = false;
+DEFAULT_REFINE = false;
 
 % Option validator
 validLogical = @(x) validateattributes(x, {'logical'},{'scalar'}, mfilename());
@@ -166,8 +191,10 @@ validLogical = @(x) validateattributes(x, {'logical'},{'scalar'}, mfilename());
 p = inputParser;
 addParameter(p,'autosave',DEFAULT_AUTOSAVE, validLogical);
 addParameter(p,'overwrite',DEFAULT_OVEWRITE, validLogical);
+addParameter(p,'refine',DEFAULT_REFINE, validLogical);
 parse(p,varargin{:});
 autosave = p.Results.autosave;
 overwrite = p.Results.overwrite;
+refine = p.Results.refine;
 
 end
diff --git a/README.md b/README.md
index b213e24..0915c8c 100644
--- a/README.md
+++ b/README.md
@@ -165,6 +165,7 @@ Polar = uiuccleaner(inputDir, inputFiles, 'overwrite', true)
 |              |                                                |         |
 | 'autosave'   | `false`, `true`                                | `false` |
 | 'overwrite'  | `false`, `true`                                | `false` |
+| 'refine'     | `false`, `true`                                | `false` |
 
 ### Airfoil generation
 
diff --git a/tests/test_uiuccleaner.m b/tests/test_uiuccleaner.m
index 451192e..dea3c76 100644
--- a/tests/test_uiuccleaner.m
+++ b/tests/test_uiuccleaner.m
@@ -59,7 +59,7 @@ function test_nargin(testCase)
 
 dummy = (1:10)';
 
-verifyError(testCase, @() af_tools.uiuccleaner(dummy, dummy, dummy, 'autosave',true,'overwrite',false), 'MATLAB:narginchk:tooManyInputs')
+verifyError(testCase, @() af_tools.uiuccleaner(dummy, dummy, dummy, 'autosave',true,'overwrite',false, 'refine', true), 'MATLAB:narginchk:tooManyInputs')
 
 end
 
@@ -124,6 +124,12 @@ verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'overwrite', char), 'MAT
 verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'overwrite', Struct), 'MATLAB:uiuccleaner:invalidType')
 verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'overwrite', vect), 'MATLAB:uiuccleaner:invalidType')
 
+verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'refine', empty), 'MATLAB:uiuccleaner:invalidType')
+verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'refine', scal), 'MATLAB:uiuccleaner:invalidType')
+verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'refine', char), 'MATLAB:uiuccleaner:invalidType')
+verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'refine', Struct), 'MATLAB:uiuccleaner:invalidType')
+verifyError(testCase, @() af_tools.uiuccleaner(testdir, 'refine', vect), 'MATLAB:uiuccleaner:invalidType')
+
 end
 
 
@@ -164,4 +170,26 @@ verifyEqual(testCase, Dat(1).x(end), 1)
 verifyEqual(testCase, Dat(2).x(end), 1)
 verifyEqual(testCase, Dat(2).x(end), 1)
 
+end
+
+function test_correctRefinePoints(testCase)
+% Verify if the output of refined solution has 101 points
+
+testdir = [pwd,'/test_utils'];
+nPoints = 101;
+
+DatNoRefine = af_tools.uiuccleaner(testdir, '*');
+Dat = af_tools.uiuccleaner(testdir, '*', 'refine', true);
+
+for i = 1:length(Dat)
+    if length(DatNoRefine(i).x) > nPoints
+        verifyEqual(testCase, Dat(i).x, DatNoRefine(i).x)
+        verifyEqual(testCase, Dat(i).y, DatNoRefine(i).y)
+    else
+        verifyEqual(testCase, length(Dat(i).x), nPoints)
+        verifyEqual(testCase, length(Dat(i).y), nPoints)
+    end
+end
+
+
 end
-- 
GitLab