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 Francesco Casella, 11 years ago

Owner: changed from Lennart Ochel to Adrian Pop
Status: newassigned

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

package TestExternalFunction
  record R_base
    Integer z;
  end R_base;

  record R
    extends R_base;
    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;

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.

comment:2 by Martin Sjölund, 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 Francesco Casella, 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 Francesco Casella, 11 years ago

Resolution: fixed
Status: assignedclosed

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
Note: See TracTickets for help on using tickets.