ModelicaSpecification
ModelicaSpecification copied to clipboard
Clarify/Allow use of ExternalObjects in equations
Reported by thiele on 26 Feb 2015 17:38 UTC
Basic Idea
Using external objects in equations is useful and works in some tools, but the rules in "12.9.7 External Objects" in MLS 3.3 Revision 1 are rather restrictive and might not allow that.
Allowing to use external object in (connect) equations allows to propagate external object through a model using a natural data-flow style. An example usage for this feature is the packaging system found in the Modelica_DeviceDrivers library (https://github.com/modelica/Modelica_DeviceDrivers).

The important point is that this requires that external objects can be used in (connect) equations.
Alternatively, one might try to use the inner/outer mechanism to implement something similar. However, this is not so easy if one wants to support several instances of the packaging system and if parameters to the external object constructor depend on parameters/static equations that are not readily available at the place there the inner is defined.
Illustrative Modelica Code Example
The following Modelica code shows the principle of this implementation (using inline C code to have everything at one place). The model to simulate is TestExtObj. TestExtObj instantiates a source block in which an external object is created. In addition it instantiates block ExtObjThrough that takes an external object as input, modifies its state, and returns it as an output. The various dummy variables introduce dependencies that ensure the correct ordering of the function calls.
package ExtObjEqn "Using external objects in (connect) equations"
class ExtObj
extends ExternalObject;
encapsulated function constructor "Claim the memory"
import ExtObjEqn.ExtObj;
input Integer n = 1;
output ExtObj extObj;
external "C" extObj= extObjConstructor(n)
annotation(Include = "
#ifndef EXTOBJTEST_C_
#include <stdlib.h>
void* extObjConstructor(int n) {
double* payload = calloc(n, sizeof(double));
return (void*) payload;
}
void extObjDestructor(void* p) {
double* payload = (double*) p;
free(payload);
}
double addPayload(void* p, double newValue, double dummy) {
double* payload = (double*) p;
*payload = newValue;
return dummy;
}
double getPayload(void* p, double dummy) {
double* payload = (double*) p;
double value = *payload;
return value;
}
#endif
");
end constructor;
encapsulated function destructor "Free memory"
import ExtObjEqn.ExtObj;
input ExtObj extObj;
external "C" extObjDestructor(extObj);
end destructor;
end ExtObj;
function addPayload
input ExtObj extObj;
input Real newValue;
input Real dummyIn;
output Real dummyOut;
external "C" dummyOut= addPayload(extObj, newValue, dummyIn);
end addPayload;
function getPayload
input ExtObj extObj;
input Real dummy;
output Real payload;
external "C" payload= getPayload(extObj,dummy);
end getPayload;
connector ExtInCon
input ExtObj eo;
input Real dummy;
end ExtInCon;
connector ExtOutCon
output ExtObj eo;
output Real dummy;
end ExtOutCon;
block ExtObjSource
ExtOutCon extOutCon(eo=ExtObj(12), dummy=time);
end ExtObjSource;
block ExtObjThrough
ExtInCon extInCon;
ExtOutCon extOutCon;
equation
extOutCon.dummy =addPayload(
extInCon.eo,
2,
extInCon.dummy);
extOutCon.eo = extInCon.eo;
end ExtObjThrough;
model TestExtObj
ExtObjSource ceos;
ExtObjThrough ceot;
output Real payload;
equation
connect(ceos.extOutCon,ceot.extInCon);
payload = getPayload(ceot.extOutCon.eo, ceot.extOutCon.dummy);
end TestExtObj;
end ExtObjEqn;
Migrated-From: https://trac.modelica.org/Modelica/ticket/1669
Comment by beutlich on 9 Mar 2015 15:27 UTC One Modelica conform way would be to have one instance (with unique name) declared as inner (like e.g. Modelica.Mechanics.MultiBody.World does) which holds an array of external objects to be declared. It also holds an array for each of the constructor arguments of the external object. The array size can be a parameter, too. All other blocks/models that depend on the external objects have this instance declared as outer and take a parameter of the array index to refer to one specific external object of the array.
Thus in the end the current implementation of ExtObjEqn is not in compliance with the MLS and can be avoided by inner/outer declarations that are already use in MSL. If you prefer I can patch your examle ExtObjEqn this way.
Comment by hansolsson on 9 Mar 2015 15:42 UTC Replying to [comment:1 beutlich]:
One Modelica conform way would be to have one instance (with unique name) declared as inner (like e.g. Modelica.Mechanics.MultiBody.World does) which holds an array of external objects to be declared. It also holds an array for each of the constructor arguments of the external object. The array size can be a parameter, too. All other blocks/models that depend on the external objects have this instance declared as outer and take a parameter of the array index to refer to one specific external object of the array.
I understand that will be correct Modelica - but to me it doesn't solve the issue since:
- There is no simple way of automatically increasing the parameter in the world-object when you add a new model using the "outer world".
- The parameters of the constructors must be propagated from the instances to the global "world"; that that does not seem to lead to an object-oriented design.
- What if you have several "inner world"?
Comment by beutlich on 9 Mar 2015 18:39 UTC Replying to [comment:2 hansolsson]:
I understand that will be correct Modelica - but to me it doesn't solve the issue since: * There is no simple way of automatically increasing the parameter in the world-object when you add a new model using the "outer world".
Usually, in context of Modelica_DeviceDrivers you have the "inner world" once for each external object. Look at attachment:SerialPackager.png above.
- The top packet (= packager in Modelica_DeviceDrivers.Blocks.Examples.TestSerialPackager_UDP) would be one "inner world" for Modelica_DeviceDrivers.Packaging.SerialPackager.
- The uDPReceive holds/creates another "inner world" for Modelica_DeviceDrivers.Packaging.SerialPackager.
- The uDPSend would be one "inner world" for Modelica_DeviceDrivers.Communication.UDPSocket.
- The uDPReceive would be another "inner world" for Modelica_DeviceDrivers.Communication.UDPSocket. The critical case is when you need another packager in the model. In this case our packager needs to organize two external objects. Then a dimension parameter must be increased from 1 to 2 and all constructor parameters must be dimensionized as well. Models using the "outer world" need to select which of the external objects they are referring to, e.g. index 1 or 2.
* The parameters of the constructors must be propagated from the instances to the global "world"; that that does not seem to lead to an object-oriented design.
No, parameters of the constructors are kept local to the "inner world" instances.
* What if you have several "inner world"?
Same as now for Modelica.Mechanics.MultiBody.World: If you have another "inner world1" its external objects will be constructed/destructed but simply not referenced. This is fine though (but not very sophisticated).
In the end the goal is to make Modelica_DeviceDrivers standard conform given the MLS we have today. This is just one proposal not saying it is the final solution.
Comment by thiele on 11 Mar 2015 08:28 UTC For me this seems rather clumsy. Allowing external objects in connect equations seems to lead to a cleaner design.
Martin Sjölund proposed to have one external object as inner that contains a map that can associate keys with memory addresses. Now, instead of creating an external object one
- creates a unique reference,
- uses that reference as a key in the map,
- allocates memory at the address there the key points to, and
- instead of propagating the external object using connection equations, one propagates the reference key
I think this solution is even a bit more convenient since no index has to be set. But I still have the feeling that the most direct and natural solution is to propagate external object directly. So if it is not allowed, I think we should consider to allow it instead of resorting to hacks that appear more ugly and unnatural.
Modified by beutlich on 24 Apr 2015 12:07 UTC
Modified by beutlich on 6 Dec 2016 08:42 UTC