2
votes

I have written a C dll function that I want to call from Modelica, and want it to return a Modelica record.

Based upon the data mapping described in the "Modelica by Example" book, I defined a structure in my C header and my function returns a pointer to the structure. You can see the header contents below:

#ifdef NZ1MAP_EXPORTS
#define NZ1MAP_API __declspec(dllexport)
#else
#define NZ1MAP_API __declspec(dllimport)
#endif

#ifdef __cplusplus
    extern "C" {
#endif

    // Define struct to match the SpeedlineVectors record in Modelica.
    typedef struct
    {
        double Mach;
        double PRVposition;
        double DiffuserGap;
        double Theta[24];
        double Omega[24];
        double MapEfficiency[24];
        double OmegaStall[24];
    } SpeedlineVectors;


    NZ1MAP_API SpeedlineVectors* GetNZ1SpeedlineVectors_External(double Mach, double DiffuserGapFraction);

#ifdef __cplusplus
    }
#endif

In Dymola, I created the record below:

record SpeedlineVectors 
  "Mach, PRV position, Diffuser Gap, and Vectors of Theta, Omega, Map Efficiency, Omega Stall"

  Real Mach "impeller machine Mach";
  Real PRVposition "PRV position, 0 = fully closed, 1 = fully open";
  Real DiffuserGap 
    "Diffuser gap, 0 = fully closed, 1 = fully open, typical minimum = 0.05";

  Real Theta[24] "vector of non-dimensional flow coefficients along speed line";
  Real Omega[24] "vector of non-dimensional head coefficients along speed line";
  Real MapEfficiency[24] 
    "vector of isentropic efficiency normalized to tip Reynolds number of 1E6 along speed line";
  Real OmegaStall[24] 
    "vector of non-dimensional head where stall is expected to begin along speed line";

end SpeedlineVectors;

And I created the function that should call the external C dll and return a "SpeedlineVectors" record:

function GetNZ1SpeedlineVectors_External 
  "Get NZ1 speedline array from external C function"

  input Real operatingMach "Machine Mach number";
  input Real diffuserGapFraction "Diffuser gap open fraction, 0 to 1";
  output SpeedlineVectors speedlineVectors "speedlineVectors record";

  external "C" speedlineVectors = GetNZ1SpeedlineVectors_External(operatingMach, diffuserGapFraction);
  annotation(Include="#include <NZ1Map.h>", Library="NZ1Map");

end GetNZ1SpeedlineVectors_External;

I built the simple test model below:

model GetNZ1SpeedlineVectors_Tester
  Real mach = 1.32;
  Real diffuserGapFraction = 0.50;
  SpeedlineVectors myVectors;

equation 
  myVectors = GetNZ1SpeedlineVectors_External(mach, diffuserGapFraction);

  annotation (Icon(coordinateSystem(preserveAspectRatio=false)), Diagram(
        coordinateSystem(preserveAspectRatio=false)));
end GetNZ1SpeedlineVectors_Tester;

When I try to run the test model, I receive the following error messages from Dymola:

Compiling and linking the model (Visual C++). 

dsmodel.c
dsmodel.c(74) : error C2440: '=' : cannot convert from 'SpeedlineVectors *' to 'DymStruc0'

Error generating Dymosim. 

I also tried adjusting the C function return so that it returns the struct directly rather than a pointer to the struct, but I receive a similar error message:

dsmodel.c(74) : error C2440: '=' : cannot convert from 'SpeedlineVectors' to 'DymStruc0'

Any tips on what must be done to return a Modelica record from an external C function?

Thanks, Justin

3

3 Answers

2
votes

According to the Modelica specification section 12.9.1.3 arrays in a record cannot be mapped when returned from an external function. You could also try using the ExternalObject: https://build.openmodelica.org/Documentation/ModelicaReference.Classes.ExternalObject.html

2
votes

As a workaround for my case, I was able to solve the problem by changing the external function. Instead of returning the arrays in a struct that is mapped to a record (thanks to Shaga for pointing out that this is not supported per the modelica specification), the function was changed to return void with four output arrays.

The new function definition in the C header is:

NZ1MAP_API void GetNZ1SpeedlineVectors_External(double Mach, double DiffuserGapFraction, double ThetaVector[], size_t SizeOfThetaVector, double OmegaVector[], size_t SizeOfOmegaVector, double MapEfficiencyVector[], size_t SizeOfMapEfficiencyVector, double OmegaStallVector[], size_t SizeOfOmegaStallVector);

The C function definition in the C file is:

NZ1MAP_API void GetNZ1SpeedlineVectors_External(double Mach, double DiffuserGapFraction,
    double thetaVector[], size_t thetaVectorSize, // thetaVector[] is equivalent to double* thetaVector
    double omegaVector[], size_t omegaVectorSize,
    double efficiencyVector[], size_t efficiencyVectorSize,
    double omegaStallVector[], size_t omegaStallVectorSize)

The modelica function is shown below:

function GetNZ1SpeedlineVectors_External 
  "Get NZ1 speedline array from external C function"

  input Real operatingMach "Machine Mach number";
  input Real diffuserGapFraction "Diffuser gap open fraction, 0 to 1";
  output Real ThetaVector[24] 
    "vector of non-dimensional flow coefficients along speedline";
  output Real OmegaVector[24] 
    "vector of non-dimensional head coefficients along speedline";
  output Real MapEfficiencyVector[24] 
    "vector of non-dimensional map efficiency along speedline";
  output Real OmegaStallVector[24] "vector of omega stall along speedline";

  external "C" GetNZ1SpeedlineVectors_External(operatingMach, diffuserGapFraction, ThetaVector, size(ThetaVector,1), OmegaVector, size(OmegaVector,1), MapEfficiencyVector, size(MapEfficiencyVector,1), OmegaStallVector, size(OmegaStallVector, 1));
  annotation(Include = "#include <NZ1Map.h>", Library="NZ1Map");

end GetNZ1SpeedlineVectors_External;

And a test model that calls the modelica function is:

model NZ1_External "NZ1 External Dll"
  extends SpeedlineVectorsBase;

  parameter Real inputMach = 1.4;
  parameter Real inputDiffuserGapFraction = 1;

equation 
  Mach = inputMach;
  PRVposition = 1;
  DiffuserGapFraction = inputDiffuserGapFraction;

  (ThetaVector, OmegaVector, MapEfficiencyVector, OmegaStallVector) =
    GetNZ1SpeedlineVectors_External(Mach, DiffuserGapFraction);
end NZ1_External;

I hope this helps someone else see how they can return multiple arrays from an external function to modelica, since modelica will not map arrays returned as part of a struct.

Thanks, Justin

1
votes

You can hide the multiple arrays from the users of the Modelica function, by having hierarchical names in the external function call.

function GetNZ1SpeedlineVectors_External 
  input Real operatingMach "Machine Mach number";
  input Real diffuserGapFraction "Diffuser gap open fraction, 0 to 1";
  output SpeedlineVectors speedlineVectors "speedlineVectors record";
  external "C" GetNZ1SpeedlineVectors_External(operatingMach,diffuserGapFraction, speedLineVectors.ThetaVector, size(speedLineVectors.ThetaVector,1), ...);
 annotation(Include="#include <NZ1Map.h>", Library="NZ1Map");
end GetNZ1SpeedlineVectors_External;

This is a new feature added in Modelica 3.3 Revision 1, https://trac.modelica.org/Modelica/ticket/351 and should be available since Dymola 2015.