Skip to content

Commit

Permalink
Add unit definitions, model structure and time variable to exported FMUs
Browse files Browse the repository at this point in the history
  • Loading branch information
robbr48 committed Oct 11, 2024
1 parent 38a1931 commit 90b9d0b
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 25 deletions.
3 changes: 2 additions & 1 deletion HopsanGenerator/include/GeneratorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ enum ModelVariableCausality {Input, Output};
class ModelVariableSpecification
{
public:
ModelVariableSpecification(QStringList systemHierarchy, QString componentName, QString portName, QString dataName, int dataId, double startValue, ModelVariableCausality causality);
ModelVariableSpecification(QStringList systemHierarchy, QString componentName, QString portName, QString dataName, int dataId, double startValue, ModelVariableCausality causality, QString unit);
QString getName() const;
QString getCausalityStr() const;
QStringList systemHierarchy;
Expand All @@ -308,6 +308,7 @@ class ModelVariableSpecification
int dataId;
double startValue;
ModelVariableCausality causality;
QString unit;
};

void getInterfaces(QList<InterfacePortSpec> &interfaces, hopsan::ComponentSystem *pSystem, QStringList &path);
Expand Down
5 changes: 3 additions & 2 deletions HopsanGenerator/src/GeneratorTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ void getModelVariables(hopsan::ComponentSystem *pSystem, QList<ModelVariableSpec
causality = ModelVariableCausality::Input;
}
}
vars.append(ModelVariableSpecification(systemHierarchy, names[i].c_str(), portName, node.shortname.c_str(), node.id, pPort->getStartValue(node.id), causality));
vars.append(ModelVariableSpecification(systemHierarchy, names[i].c_str(), portName, node.shortname.c_str(), node.id, pPort->getStartValue(node.id), causality, QString(node.unit.c_str())));
}
}
}
Expand Down Expand Up @@ -890,7 +890,7 @@ void getParameters(QList<ParameterSpecification> &parameters, hopsan::ComponentS
}
}

ModelVariableSpecification::ModelVariableSpecification(QStringList systemHierarchy, QString componentName, QString portName, QString dataName, int dataId, double startValue, ModelVariableCausality causality)
ModelVariableSpecification::ModelVariableSpecification(QStringList systemHierarchy, QString componentName, QString portName, QString dataName, int dataId, double startValue, ModelVariableCausality causality, QString unit)
{
this->systemHierarchy = systemHierarchy;
this->componentName = componentName;
Expand All @@ -899,6 +899,7 @@ ModelVariableSpecification::ModelVariableSpecification(QStringList systemHierarc
this->dataId = dataId;
this->startValue = startValue;
this->causality = causality;
this->unit = unit;
}

QString ModelVariableSpecification::getName() const
Expand Down
157 changes: 135 additions & 22 deletions HopsanGenerator/src/generators/HopsanFMIGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,16 +613,43 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
mdWriter.writeAttribute("canHandleVariableCommunicationStepSize", "true");
mdWriter.writeEndElement(); //CoSimulation


QList<ModelVariableSpecification> vars;
QStringList systemHierarchy = QStringList();
getModelVariables(pSystem, vars, systemHierarchy);

QList<ParameterSpecification> parameterSpecs;
getParameters(parameterSpecs, pSystem);

mdWriter.writeStartElement("UnitDefinitions");
mdWriter.writeStartElement("Unit");
mdWriter.writeAttribute("name", "s");
QStringList usedUnits = QStringList() << "s";
mdWriter.writeEndElement(); //Unit
for(const auto &var : qAsConst(vars)) {
if(var.unit != "" && !usedUnits.contains(var.unit)) {
usedUnits << var.unit;
mdWriter.writeStartElement("Unit");
mdWriter.writeAttribute("name", var.unit);
mdWriter.writeEndElement(); //Unit
}
}
for(const auto &par : qAsConst(parameterSpecs)) {
if(par.unit != "" && !usedUnits.contains(par.unit)) {
usedUnits << par.unit;
mdWriter.writeStartElement("Unit");
mdWriter.writeAttribute("name", par.unit);
mdWriter.writeEndElement(); //Unit
}
}
mdWriter.writeEndElement(); //UnitDefinitions

mdWriter.writeStartElement("DefaultExperiment");
mdWriter.writeAttribute("startTime", QString::number(pSystem->getTime()));
mdWriter.writeEndElement(); //DefaultExperiment

mdWriter.writeStartElement("ModelVariables");

QList<ModelVariableSpecification> vars;
QStringList systemHierarchy = QStringList();
getModelVariables(pSystem, vars, systemHierarchy);

mdWriter.writeStartElement("ScalarVariable");
mdWriter.writeAttribute("name", "timestep");
mdWriter.writeAttribute("valueReference", "0");
Expand All @@ -643,6 +670,9 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
mdWriter.writeAttribute("causality", var.getCausalityStr());
mdWriter.writeAttribute("variability", "continuous");
mdWriter.writeStartElement("Real");
if(!var.unit.isEmpty()) {
mdWriter.writeAttribute("unit", var.unit);
}
if(var.causality != Output) {
mdWriter.writeAttribute("start", QString::number(var.startValue));
}
Expand All @@ -651,9 +681,6 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
++vr;
}

QList<ParameterSpecification> parameterSpecs;
getParameters(parameterSpecs, pSystem);

for(auto &parSpec : parameterSpecs) {
mdWriter.writeStartElement("ScalarVariable");
mdWriter.writeAttribute("name", parSpec.name);
Expand All @@ -676,16 +703,41 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
}

mdWriter.writeAttribute("start", parSpec.init);

mdWriter.writeAttribute("unit", parSpec.unit);
if(!parSpec.unit.isEmpty()) {
mdWriter.writeAttribute("unit", parSpec.unit);
}
mdWriter.writeEndElement(); //Float64/Int32/Boolean/String
mdWriter.writeEndElement(); //ScalarVariable
++vr;
}
mdWriter.writeEndElement(); //ModelVariables

mdWriter.writeStartElement("ModelStructure"); // This element must exist, even if it is empty
mdWriter.writeEndElement(); //ModelSctructure
mdWriter.writeStartElement("ModelStructure");
mdWriter.writeStartElement("Outputs");
vr = 1; //vr = 0 reserved for timestep
for(const auto &var : qAsConst(vars)) {
if(var.causality == Output) {
mdWriter.writeStartElement("Unknown");
mdWriter.writeAttribute("index", QString::number(vr+1)); //Index counting starts at 1, not 0
mdWriter.writeAttribute("dependencies", "");
mdWriter.writeEndElement();
}
++vr;
}
mdWriter.writeEndElement(); //Outputs
mdWriter.writeStartElement("InitialUnknowns");
vr = 1; //vr = 0 reserved for timestep
for(const auto &var : qAsConst(vars)) {
if(var.causality == Output) {
mdWriter.writeStartElement("Unknown");
mdWriter.writeAttribute("index", QString::number(vr+1)); //Index counting starts at 1, not 0
mdWriter.writeAttribute("dependencies", "");
mdWriter.writeEndElement();
}
++vr;
}
mdWriter.writeEndElement(); //InitialUnknowns
mdWriter.writeEndElement(); //ModelStructure

mdWriter.writeEndElement(); //fmiModelDescription
}
Expand All @@ -705,20 +757,55 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
mdWriter.writeAttribute("hasEventMode", "false");
mdWriter.writeEndElement(); //CoSimulation

QList<ModelVariableSpecification> vars;
QStringList systemHierarchy = QStringList();
getModelVariables(pSystem, vars, systemHierarchy);

QList<ParameterSpecification> parameterSpecs;
getParameters(parameterSpecs, pSystem);

mdWriter.writeStartElement("UnitDefinitions");
mdWriter.writeStartElement("Unit");
mdWriter.writeAttribute("name", "s");
QStringList usedUnits = QStringList() << "s";
mdWriter.writeEndElement(); //Unit
for(const auto &var : qAsConst(vars)) {
if(var.unit != "" && !usedUnits.contains(var.unit)) {
usedUnits << var.unit;
mdWriter.writeStartElement("Unit");
mdWriter.writeAttribute("name", var.unit);
mdWriter.writeEndElement(); //Unit
}
}
for(const auto &par : qAsConst(parameterSpecs)) {
if(par.unit != "" && !usedUnits.contains(par.unit)) {
usedUnits << par.unit;
mdWriter.writeStartElement("Unit");
mdWriter.writeAttribute("name", par.unit);
mdWriter.writeEndElement(); //Unit
}
}
mdWriter.writeEndElement(); //UnitDefinitions

mdWriter.writeStartElement("DefaultExperiment");
mdWriter.writeAttribute("startTime", QString::number(pSystem->getTime()));
mdWriter.writeAttribute("stepSize", QString::number(pSystem->getDesiredTimeStep()));
mdWriter.writeEndElement(); //DefaultExperiment

mdWriter.writeStartElement("ModelVariables");

QList<ModelVariableSpecification> vars;
QStringList systemHierarchy = QStringList();
getModelVariables(pSystem, vars, systemHierarchy);
mdWriter.writeStartElement("Float64");
mdWriter.writeAttribute("name", "time");
mdWriter.writeAttribute("valueReference", "0");
mdWriter.writeAttribute("causality", "independent");
mdWriter.writeAttribute("variability", "continuous");
mdWriter.writeAttribute("description", "Simulation time");
mdWriter.writeAttribute("unit", "s");
mdWriter.writeEndElement(); //Float64

mdWriter.writeStartElement("Float64");
mdWriter.writeAttribute("name", "timestep");
mdWriter.writeAttribute("valueReference", "0");
mdWriter.writeAttribute("valueReference", "1");
mdWriter.writeAttribute("causality", "parameter");
mdWriter.writeAttribute("variability", "fixed");
mdWriter.writeAttribute("description", "Hopsan time step");
Expand All @@ -729,8 +816,8 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
nReals=0;
nInputs=0;
nOutputs=0;
long vr = 1; //! vr = 0 is reserved for timestep
for(const auto &var : vars) {
long vr = 2; //! vr = 0 and vr = 1 are reserved for time and timestep
for(const auto &var : qAsConst(vars)) {
mdWriter.writeStartElement("Float64");
if(var.systemHierarchy.isEmpty()) {
mdWriter.writeAttribute("name", var.componentName+"."+var.portName+"."+var.dataName);
Expand All @@ -742,18 +829,18 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
mdWriter.writeAttribute("variability", "continuous");
if(var.causality == ModelVariableCausality::Input) {
mdWriter.writeAttribute("causality", "input");
mdWriter.writeAttribute("start", QString::number(var.startValue));
}
else {
mdWriter.writeAttribute("causality", "output");
}
mdWriter.writeAttribute("start", QString::number(var.startValue));
if(!var.unit.isEmpty()) {
mdWriter.writeAttribute("unit", var.unit);
}
++vr;
mdWriter.writeEndElement(); //Float64
}

QList<ParameterSpecification> parameterSpecs;
getParameters(parameterSpecs, pSystem);

for(auto &parSpec : parameterSpecs) {
if(parSpec.type == "Real") {
mdWriter.writeStartElement("Float64");
Expand All @@ -777,11 +864,37 @@ bool HopsanFMIGenerator::generateModelDescriptionXmlFile(ComponentSystem *pSyste
mdWriter.writeAttribute("variability", "fixed");
mdWriter.writeAttribute("start", parSpec.init);
mdWriter.writeAttribute("description", parSpec.description);
mdWriter.writeAttribute("unit", parSpec.unit);
if(!parSpec.unit.isEmpty()) {
mdWriter.writeAttribute("unit", parSpec.unit);
}
mdWriter.writeEndElement(); //Float64/Int32/Boolean/String
++vr;
}
mdWriter.writeEndElement(); //ModelVariables

mdWriter.writeStartElement("ModelStructure");
vr = 2; //vr = 0 and vr = 1 are reserved for time and timestep
for(const auto &var : qAsConst(vars)) {
if(var.causality == Output) {
mdWriter.writeStartElement("Output");
mdWriter.writeAttribute("valueReference", QString::number(vr));
mdWriter.writeAttribute("dependencies", "");
mdWriter.writeEndElement(); //Output
}
++vr;
}
vr = 2; //vr = 0 and vr = 1 are reserved for time and timestep
for(const auto &var : qAsConst(vars)) {
if(var.causality == Output) {
mdWriter.writeStartElement("InitialUnknown");
mdWriter.writeAttribute("valueReference", QString::number(vr));
mdWriter.writeAttribute("dependencies", "");
mdWriter.writeEndElement();
}
++vr;
}
mdWriter.writeEndElement(); //ModelStructure

mdWriter.writeEndElement(); //fmiModelDescription
}
mdWriter.writeEndDocument();
Expand Down

0 comments on commit 90b9d0b

Please sign in to comment.