From 1d8b632e76a3c93cca2c7595d6ccce97930e548c Mon Sep 17 00:00:00 2001
From: Mauco03 <marco.gaibotti@skywarder.eu>
Date: Sun, 20 Apr 2025 10:46:01 +0200
Subject: [PATCH] [coeff-transpot][Coefficient] Handles singleton dimension
 cases and improved performance

---
 classes/misc/Coefficient.m | 108 +++++++++++++++++++++++++------------
 1 file changed, 75 insertions(+), 33 deletions(-)

diff --git a/classes/misc/Coefficient.m b/classes/misc/Coefficient.m
index f48b71d..a230fbf 100644
--- a/classes/misc/Coefficient.m
+++ b/classes/misc/Coefficient.m
@@ -55,6 +55,11 @@ classdef Coefficient
         finsCN                      double  % Fins-only CN
     end
 
+    properties(SetAccess = private)
+        isReady             (1, 1)  logical % Whether all coefficients are loaded and of the right size
+        isDynamic           (1, 1)  logical % Whether to load dynamic derivatives. Adds XCG dependece
+    end
+
     properties(Access = private)
         STATIC                              % Cached variable for static coefficients
         DYNAMIC                             % Cached variable for dynamic derivatives
@@ -63,8 +68,6 @@ classdef Coefficient
     end
 
     properties(Access = private)
-        loadDynamic         (1, 1)  logical % Whether to load dynamic derivatives. Adds XCG dependece
-        
         staticInterpolant   (1, 1)  griddedInterpolant
         dynamicInterpolant  (1, 1)  griddedInterpolant
     end
@@ -77,6 +80,8 @@ classdef Coefficient
             end
             
             obj = obj.loadData(filePath, name);
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
 
         function coefficients = get(obj, alpha, mach, beta, altitude, airbakes, xcg)
@@ -102,6 +107,8 @@ classdef Coefficient
             %   the specified xcg. Dynamic derivatives are included only if
             %   loadDynamic is true.
             
+            if ~obj.isReady, error('Canno interpolate coefficients: check that all dimensions match'); end
+
             coefficients = zeros(11, 1);
             coefficients(1:6) = obj.staticInterpolant(alpha, mach, beta, altitude, airbakes);
             % Transporting static coefficients to new xcg
@@ -115,7 +122,7 @@ classdef Coefficient
             
             coefficients(4:6) = coefficients(4:6) + d/l * forceCoeffs;
 
-            if ~obj.loadDynamic, return; end
+            if ~obj.isDynamic, return; end
             coefficients(7:11) = obj.dynamicInterpolant(alpha, mach, beta, altitude, airbakes, xcg);
         end
     end
@@ -140,23 +147,27 @@ classdef Coefficient
 
     methods % Setters
         function obj = set.static(obj, value)
-            obj.STATIC = value(:, :, :, :, :, :, 1);
-            obj = obj.updateInterpolants();
+            obj.STATIC  = value(:, :, :, :, :, :, 1);
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
 
         function obj = set.dynamic(obj, value)
             obj.DYNAMIC = value(:, :, :, :, :, :, :);
-            obj = obj.updateInterpolants();
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
 
         function obj = set.geometry(obj, value)
             obj.GEOMETRY = value;
-            obj = obj.updateInterpolants();
+            obj.isReady  = obj.checkProperties();
+            obj          = obj.updateInterpolants();
         end
 
         function obj = set.state(obj, value)
-            obj.STATE = value;
-            obj = obj.updateInterpolants();
+            obj.STATE   = value;
+            obj.isReady = obj.checkProperties();
+            obj         = obj.updateInterpolants();
         end
     end
 
@@ -179,17 +190,17 @@ classdef Coefficient
             if ~isfield(dataCoeffs, name), return; end
             dataCoeffs = dataCoeffs.(name);
 
-            obj.loadDynamic = ~strcmp(name, 'generic');
+            obj.isDynamic = ~strcmp(name, 'generic');
             obj.finsCN = dataCoeffs.finsCN;
-            obj.geometry = dataCoeffs.geometry;
-            obj.state = dataCoeffs.state;
+            obj.GEOMETRY = dataCoeffs.geometry;
+            obj.STATE = dataCoeffs.state;
             
             % Load CA, CY, CN, Cl, Cm, Cn
-            obj.static = dataCoeffs.total([1, 3, 5, 6, 9, 13], :, :, :, :, :, :);
+            obj.STATIC = dataCoeffs.total([1, 3, 5, 6, 9, 13], :, :, :, :, :, :);
 
-            if obj.loadDynamic
+            if obj.isDynamic
                 % Load Clp, Cmad, Cmq, Cnr, Cnp
-                obj.dynamic = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
+                obj.DYNAMIC = dataCoeffs.total([7, 10, 11, 14, 15], :, :, :, :, :, :);
             end
         end
 
@@ -197,39 +208,70 @@ classdef Coefficient
             % Check if STATIC, DYNAMIC, GEOMETRY, and STATE are non-empty
             ready = ~isempty(obj.STATIC) && ...
                     ~isempty(obj.GEOMETRY) && ~isempty(obj.STATE) && ...
-                    ~(isempty(obj.DYNAMIC) && obj.loadDynamic);
+                    ~(isempty(obj.DYNAMIC) && obj.isDynamic);
+
+            if ~ready, return; end
+
+            alpha = obj.STATE.alphas;
+            mach = obj.STATE.machs;
+            beta = obj.STATE.betas;
+            altitude = obj.STATE.altitudes;
+            airbakes = obj.STATE.hprot;
+            xcg = obj.GEOMETRY.xcg;
+ 
+            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
+            dims = cellfun(@(x) length(x), gridVecs);
+            dims(dims == 0) = 1;                                            % Empty case is the same as scalar case
+
+            staticDimsReady = all(size(obj.STATIC, 2:6) == dims(1:end-1));
+            dynamicDimsReady = true;
+
+            if obj.isDynamic
+                dynamicDimsReady = all(size(obj.DYNAMIC, 2:7) == dims);
+            end
+            
+            ready = ready && staticDimsReady && dynamicDimsReady;
         end
 
         function obj = updateInterpolants(obj)
             % Update static and dynamic interpolants
-            if ~obj.checkProperties(), return; end
+            if ~obj.isReady, return; end
         
             %% Retrieve flight conditions
-            alpha = obj.state.alphas*pi/180;                    % Converting to rad
-            mach = obj.state.machs;
-            beta = obj.state.betas*pi/180;                      % Converting to rad
-            altitude = obj.state.altitudes;
-            airbakes = obj.state.hprot/obj.state.hprot(end);    % Normalizing on height
+            alpha = obj.STATE.alphas*pi/180;                                % Converting to rad
+            mach = obj.STATE.machs;
+            beta = obj.STATE.betas*pi/180;                                  % Converting to rad
+            altitude = obj.STATE.altitudes;
+            xcg = obj.GEOMETRY.xcg;
+
+            if isempty(obj.STATE.hprot), airbakes = 0;
+                else, airbakes = obj.STATE.hprot/obj.STATE.hprot(end);      % Normalizing on height
+            end
+
+            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
+
+            % Find singleton dims (last dimension is coefficients and will not be of length 1)
+            singletonDims = cellfun(@(x) isscalar(x) || isempty(x), gridVecs);
+            gridVecs(singletonDims) = deal({[0, 1]});
 
             %% Create interpolants
-            staticCoeffs = permute(obj.static, [2, 3, 4, 5, 6, 1]);
+            staticCoeffs = permute(obj.STATIC, [2, 3, 4, 5, 6, 1]);
+            staticCoeffs = repmat(staticCoeffs, singletonDims(1:end-1) + 1); % Exclude xcg for static coefficients
 
-            gridVecs = {alpha, mach, beta, altitude, airbakes};
-            obj.staticInterpolant = griddedInterpolant(gridVecs, staticCoeffs, 'linear', 'nearest');
+            obj.staticInterpolant = griddedInterpolant(gridVecs(1:end-1), staticCoeffs, 'linear', 'nearest');
 
-            if ~obj.loadDynamic, return; end
+            if ~obj.isDynamic, return; end
 
-            xcg = obj.geometry.xcg;
-            dynamicCoeffs = permute(obj.dynamic, [2, 3, 4, 5, 6, 7, 1]);
+            %% Handle dynamic derivatives
+            dynamicCoeffs = permute(obj.DYNAMIC, [2, 3, 4, 5, 6, 7, 1]);
 
-            if xcg(2) < xcg(1) % Flip xcg in case of descending order
-                xcg = flip(xcg);
+            % Flip xcg in case of descending order
+            if ~isscalar(xcg) && xcg(2) < xcg(1)
+                gridVecs{end} = flip(xcg);
                 dynamicCoeffs = flip(dynamicCoeffs, 7);
             end
 
-            gridVecs = {alpha, mach, beta, altitude, airbakes, xcg};
             obj.dynamicInterpolant = griddedInterpolant(gridVecs, dynamicCoeffs, 'linear', 'nearest');
         end
     end
-end
-
+end
\ No newline at end of file
-- 
GitLab