Using interpolant surface in Simulink
3 visualizzazioni (ultimi 30 giorni)
Mostra commenti meno recenti
I have created a number of surface interpolants from a 3D-data set describing the fuel consumption of an engine. I have made this usning the curve fit toolbox and now have three thin plate interpolants I need to call from simulink.
Thin-plate spline interpolant:
ans(x,y) = thin-plate spline computed from p with thin-plate extrapolation.
These are stored in a cell array 1x3 in a .mat file, each cell holding a interpolant.
In a separate cell array I have the x/y-data describing the limits for validity for each of the initerpolants.
Best I can understand is that I must use the MAtlab Function block of the User-Defined Function section.
I've come so far that I can read test data for the needed inputs to this function (rpm and engine power) and this works as I can check using the XY-Graph tool.
But when I try to read the cell array holding the interpolants I get an error in the diagnostic viewer stating that
"Found unsupported class for variable using function 'load'.
The value at 'SFOC_model.SFOC_model.fitresult' is unsupported for code generation.
Use the command 'whos -file MTU_12V4000_M65L_v4_small.model.mat' to view the variables in the MAT file. Function 'returnSFOC.m' (#77.264.302), line 8, column 14: "load(string(engineModel),'SFOC_model')"
I'm really stuck here and do not know how to proceed. I suppose I could generate a dense lookup table with the SFOC data and store in e.g. excel but that would to a large degree defeat the effort put into creating the interpolants in the first place. The interpolants will also be used to generate fastest route of descent for the engine controller in order to move the engine working point to a place with better fuel economy, this will be difficult to do using lookup tables unless thery are made redicioulsly dense.
Do I have to create a specific class holding the interpolants or am what I'm trying to achieve just not possible?
A further annoyance is that I have created a String Constant which holds the name of the -mat-file containing the interpolants. For some reason I just cannot get the code in the Matlab Function block to accept this as input when attempting to read the mat-file. It throws the error
The filename argument to 'load' must be a constant string or character vector. Function 'EngineSFOCModel' (#36.343.362), line 7, column 15:
But I know that the variable engineModel passed to the MAtlab Function block is a scalar string, I even tried to cast it to a string in the code in the block by import = load(string(engineModel));
If I hard code the file name as
engineModel = "MTU_12V4000_M65L_v4_small.model.mat";
import = load(string(engineModel));
it works just fine.... What the bonkers am I missing here?
If I set the SFOC-output of the EngineSFOCmodel block manually in the code to e.g. SFOC=210 I do get the correct output and the integations etc works just fine and I arrive at correct calculated fuel consumption in kgs.
The full model is shown below in block form below;

The code in the EngineSFOCModel block is as follows:
function SFOC = EngineSFOCmodel(engineModel,engineRpm,enginePower)
%engineModel = "MTU_12V4000_M65L_v4_small.model.mat"; %Manual hard coded value to test if .mat-file can be found and read
import = load(string(engineModel));
SFOC_model = import.SFOC_model;
BorderX = import.BorderX;
BorderY = import.BorderY;
inon = zeros(size(SFOC_model));
SFOC = zeros(size(SFOC_model));
for i=1:size(SFOC_model,2) %Find which of the three areas of validity of the interpolants the current rpm/power value are inside or possibly on the edge of the polygon border line.
inon(i) = inpolygon(rpm,power,BorderX{i},BorderY{i});
SFOC(i) = SFOC_model{i}.fitresult(rpm,power);
end
SFOC = mean(SFOC(inon==1));
%SFOC = 210; %Manual hard coded value for testing, value is in g/kWh
2 Commenti
Paul
il 18 Apr 2025
Modificato: Paul
il 18 Apr 2025
Hi Martin,
Even if this approach worked, it would be very inefficient because the data is being load-ed at each time step.
Before considering alternatives, what is is the result of
class(SFOC_model{1}.fitresult)
and I assume that's the same for SFOC_model{2} and {3}?
Also, where are the local variables rpm and and power defined?
Risposta accettata
Paul
il 19 Apr 2025
Modificato: Paul
il 19 Apr 2025
I think the fundamental problem is that objects of class sfit are not eligible for code generation, and so cannot be used inside a Matlab Function block.
The path of least resistance (there may be other paths) would be to use the Matlab Function block as a gateway to an extrinsic function, using coder.extrinsic, that does the heavy lifting. The extrinsic function will always be evaluated in interpreted mode in Matlab and there is some overhead going back and forth between Simulink and Matlab, so there may be an execution-time hit, though it's possible you might not notice it.
Here is one approach. I didn't have the time yet to test it, so consider it a prototype.
The Matlab Function block in the Simulink diagram would be
function SFOC = EngineSFOCmodel(engineModel,engineRpm,enginePower)
coder.extrinsic('helperfunction');
% this line might be needed so the code generator can determined the type
% and size of SFOC
SFOC = 0;
SFOC = helperfunction(engineModel,engineRpm,enginePower);
end
Then, define helperfunction.m somewhere on your path where Matlab will find it
function SFOC = helperfunction(engineModel,engineRpm,enginePower)
% this logic loads an engine model if one is not already loaded or if the
% engineModel changes on input
persistent import LocalEngineModel
if isempty(import) || ~strcmp(LocalEngineModel,engineModel)
LocalEngineModel = engineModel;
import = load(engineModel));
end
% computation code
SFOC_model = import.SFOC_model;
BorderX = import.BorderX;
BorderY = import.BorderY;
inon = zeros(size(SFOC_model));
SFOC = zeros(size(SFOC_model));
for i=1:size(SFOC_model,2) %Find which of the three areas of validity of the interpolants the current rpm/power value are inside or possibly on the edge of the polygon border line.
inon(i) = inpolygon(rpm,power,BorderX{i},BorderY{i});
SFOC(i) = SFOC_model{i}.fitresult(rpm,power);
end
SFOC = mean(SFOC(inon==1));
end
I've used persistent here to only load the model when needed. Note that the persistent variables hang around inside helperfunction until such time as helperfunction is cleared.
It also might be worthwhile to use one more layer of indirection to have helperfunction only load the data and then call a computation function. That way you can also call the computation function from Matlab if that would be useful.
2 Commenti
Paul
il 20 Apr 2025
You're quite welcome.
Keep in mind that coder.extrinsic is not required if the Matlab Function calls an m-function that itself is eligible for code generation. In this case, you get the speed-up, such as it may be, from using generated code and you can call the m-function in the normal course of doing business in Matlab if there is a need to do so. However, there may be cases where the generated code does not produce the exact same result as the m-function in Matlab. I think that depends on which functions/features are used inside the m-function.
Più risposte (0)
Vedere anche
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!