Opened 11 years ago
Closed 11 years ago
#2569 closed defect (fixed)
Problem with code generation - external functions with records
Reported by: | Francesco Casella | Owned by: | Adrian Pop |
---|---|---|---|
Priority: | critical | Milestone: | 1.9.1 |
Component: | Code Generation | Version: | trunk |
Keywords: | Cc: | Martin Sjölund |
Description
Please try to compile the TestStatesSat model below
package TweakedExternalMedia package TestMedium extends ExternalMedia.Media.TestMedium( externalFluidConstants = FluidConstants( iupacName= "unknown", casRegistryNumber= "unknown", chemicalFormula= "unknown", structureFormula= "unknown", molarMass= 0.028, criticalTemperature= 623.0, criticalPressure= 221e5, criticalMolarVolume= 15, acentricFactor= 0, triplePointTemperature= 280.0, triplePointPressure= 500.0, meltingPoint= 280, normalBoilingPoint= 380.0, dipoleMoment= 2.0)); end TestMedium; model TestStatesSat "Test case using TestMedium with state + sat records" extends ExternalMedia.Test.TestMedium.TestStatesSat( redeclare package Medium = TweakedExternalMedia.TestMedium); end TestStatesSat; end TweakedExternalMedia;
using ExternalMedia from the MA repo
https://svn.modelica.org/projects/ExternalMediaLibrary
The executable code is generated, but the results in the state records state1 and state2 are all garbled up.
Some of the struct definitions in the C code (e.g. typedef struct ExternalMedia_Test_TestMedium_GenericModels_CompleteThermodynamicState_Medium_ThermodynamicState_s
) contain int phase as the first element of the record, while the Modelica definition has it at the second to last place. Note that the Modelica spec, section 12.9.1.3, states: "The elements of the Modelica record class are declared in the same order in the C struct". For some reason, this is not the case here, as the Integer phase has been moved in pole position.
I have tried to replicate the problem in a simpler setting:
package TestExternalFunction record R Real y; Integer z; Real x; end R; function compute input Real u; output R r; external "C" compute(u, r) annotation(Include= "typedef struct R{double y;int z;double x;} R; void compute(double u, R* r) { r->x = u; r->z = 12; r->y = 3.0*u; }"); end compute; model Test R r; equation r = compute(time); end Test; end TestExternalFunction;
but in this case the struct is declared in the right order, and correct results are obtained. Please help me sort this out, as it might be the last hurdle before we get ExternalMedia to run in OMC
Change History (4)
comment:1 by , 11 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:2 by , 11 years ago
If your small problem replicates the model, OpenModelica does the correct thing.
Spec says:
The declaration elements of the flattened base class shall either
- ...
- Be exactly identical to any element of the flattened enclosing class with the same name and the same level of protection (public or protected) and same contents. In this case, the first element in order (can be either inherited or local) is kept.
The result is correct:
function Test.R "Automatically generated record constructor for Test.R" input Integer z; input Real y; input Real x; output R res; end Test.R;
Dymola says the same thing:
Argument number 1, z = 4.5, in TestExternalFunction.R(4.5, 4.5, 4.5) is of type Real, but it must be a subtype of Integer.
comment:3 by , 11 years ago
Maybe I've found a workaround to get code that works both in Dymola and OMC. When using records in external functions, for some reason Dymola reorders the set of elments in ASCII lexicographical order.
In ExternalMedia.Media.ExternalTwoPhaseMedium, instead of writing
redeclare replaceable record extends ThermodynamicState
which causes the first element to be phase
, which is not what Dymola does in the C code, I just write
redeclare replaceable record ThermodynamicState
so there is no direct inheritance, and then I declare all the elements in ASCII lexicographical order (including the phase
integer which is also in the base class), so that the order is exactly the same as in the Dymola code. I hope this works.
comment:4 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Conclusions:
- OMC correctly orders the elements in the record and generates external C code accordingly
- By avoiding the extends clause and ordering the declaration in the same way Dymola generates code, it is possible to write code that will work with both compilers
- A bug report has already been filed at Dassault
I have now understood the root cause of the problem, and I've been able to replicate it. The ThermodynamicState record is first defined in PartialTwoPhaseMedium to contain the integer phase; then, there is a redeclare replaceable record extends clause in ExternalTwoPhaseMedium, where phase is no longer in the first place. However, OMC keeps the elements declared in the ancestor class before the ones declared in the child class.
IMHO, this is not conforming to the spec, section 12.9.1.3, as it woul seem to me that the redeclaration (even though it has to be type-compatible, i.e., contain the same elements) should completely override the original declaration in terms of the order of the record elements.
The following simple package replicates the problem nicely
Based on this analysis, I guess this is actually a front-end problem. As a workaround, I might move phase in the first place in the redeclare clause, but since this code should work across platforms, it would be better if you could fix the problem in the compiler, so we do not need to rely on any special order of the components for a correct compilation.