Skip to content

Commit fa661c0

Browse files
authored
add support for ssc:LinearTransformation in connections (#1566)
* propagate linearTransformation to C code base * propagate linearTransformation from python to C-API * apply linearTransformation during simulation * support LinearTransformation in parameter mapping
1 parent caee6d0 commit fa661c0

32 files changed

Lines changed: 603 additions & 48 deletions

include/OMSimulator/OMSimulator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ OMSAPI oms_status_enu_t OMSCALL oms_setBoolean(const char* cref, bool value);
136136
OMSAPI oms_status_enu_t OMSCALL oms_setBusGeometry(const char* bus, const ssd_connector_geometry_t* geometry);
137137
OMSAPI oms_status_enu_t OMSCALL oms_setCommandLineOption(const char* cmd);
138138
OMSAPI oms_status_enu_t OMSCALL oms_setConnectionGeometry(const char* crefA, const char* crefB, const ssd_connection_geometry_t* geometry);
139+
OMSAPI oms_status_enu_t OMSCALL oms_setConnectionLinearTransformation(const char* crefA, const char* crefB, double factor, double offset);
139140
OMSAPI oms_status_enu_t OMSCALL oms_setConnectorGeometry(const char* cref, const ssd_connector_geometry_t* geometry);
140141
OMSAPI oms_status_enu_t OMSCALL oms_setElementGeometry(const char* cref, const ssd_element_geometry_t* geometry);
141142
OMSAPI oms_status_enu_t OMSCALL oms_setFixedStepSize(const char* cref, double stepSize);

include/OMSimulator/Types.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,15 +316,33 @@ typedef struct {
316316
double y2;
317317
} ssd_system_geometry_t;
318318

319+
/*
320+
* \brief 4.5.2.1 ssc:LinearTransformation
321+
* This element provides for a linear transformation of the source value to the target value,
322+
* i.e. in the calculation target = factor * source + offset.
323+
*/
324+
typedef struct {
325+
/*
326+
* This attribute specifies an optional factor value to use in a linear transformation. The default is 1.
327+
*/
328+
double factor;
329+
/*
330+
* This attribute specifies an optional offset value to use in a linear transformation. The default is 0.
331+
*/
332+
double offset;
333+
bool isSet; ///< This flag indicates whether the linear transformation has been set or not. The default is false.
334+
} oms_linear_transformation_t;
335+
319336
/**
320337
* \brief Connection between two connectors.
321338
*/
322339
typedef struct {
323-
oms_connection_type_enu_t type; ///< Connection type
324-
char* conA; ///< Name of connector A
325-
char* conB; ///< Name of connector B
326-
ssd_connection_geometry_t* geometry; ///< Geometry information of the connection
327-
bool suppressUnitConversion; ///< Boolean to specify if automatic unit conversion between connections should be suppressed
340+
oms_connection_type_enu_t type; ///< Connection type
341+
char* conA; ///< Name of connector A
342+
char* conB; ///< Name of connector B
343+
ssd_connection_geometry_t* geometry; ///< Geometry information of the connection
344+
bool suppressUnitConversion; ///< Boolean to specify if automatic unit conversion between connections should be suppressed
345+
oms_linear_transformation_t linearTransformation; ///< Linear transformation of the source value to the target value, i.e. in the calculation target = factor * source + offset.
328346
} oms_connection_t;
329347

330348
/**

src/OMSimulatorLib/Connection.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ oms::Connection::Connection(const oms::ComRef& conA, const oms::ComRef& conB, bo
5656
this->geometry = reinterpret_cast<ssd_connection_geometry_t*>(new oms::ssd::ConnectionGeometry());
5757

5858
this->suppressUnitConversion = suppressUnitConversion;
59+
60+
// default linear transformation settings
61+
this->linearTransformation.factor = 1.0;
62+
this->linearTransformation.offset = 0.0;
63+
this->linearTransformation.isSet = false;
5964
}
6065

6166
oms::Connection::~Connection()
@@ -80,6 +85,7 @@ oms::Connection::Connection(const oms::Connection& rhs)
8085
this->geometry = reinterpret_cast<ssd_connection_geometry_t*>(geometry_);
8186

8287
this->suppressUnitConversion = rhs.suppressUnitConversion;
88+
this->linearTransformation = rhs.linearTransformation;
8389
}
8490

8591
oms::Connection& oms::Connection::operator=(const oms::Connection& rhs)
@@ -105,6 +111,7 @@ oms::Connection& oms::Connection::operator=(const oms::Connection& rhs)
105111
this->geometry = reinterpret_cast<ssd_connection_geometry_t*>(geometry_);
106112

107113
this->suppressUnitConversion = rhs.suppressUnitConversion;
114+
this->linearTransformation = rhs.linearTransformation;
108115

109116
return *this;
110117
}
@@ -144,6 +151,13 @@ void oms::Connection::setGeometry(const oms::ssd::ConnectionGeometry* newGeometr
144151
this->geometry = reinterpret_cast<ssd_connection_geometry_t*>(geometry_);
145152
}
146153

154+
void oms::Connection::setLinearTransformation(double factor, double offset)
155+
{
156+
this->linearTransformation.factor = factor;
157+
this->linearTransformation.offset = offset;
158+
this->linearTransformation.isSet = true;
159+
}
160+
147161
bool oms::Connection::isStrictEqual(const oms::ComRef& signalA, const oms::ComRef& signalB) const
148162
{
149163
return signalA == oms::ComRef(this->conA) && signalB == oms::ComRef(this->conB);

src/OMSimulatorLib/Connection.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ namespace oms
6262
const oms::ssd::ConnectionGeometry* getGeometry() const {return reinterpret_cast<oms::ssd::ConnectionGeometry*>(geometry);}
6363
void setGeometry(const oms::ssd::ConnectionGeometry* newGeometry, bool inverse=false);
6464

65+
const oms_linear_transformation_t getLinearTransformation() const { return linearTransformation; }
66+
void setLinearTransformation(double factor, double offset);
67+
6568
oms_connection_type_enu_t getType() const {return type;}
6669

6770
bool isEqual(const oms::Connection& connection) const;

src/OMSimulatorLib/DirectedGraph.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,6 @@ void oms::DirectedGraph::calculateSortedConnections()
266266
if (oms::Connection::isValid(conA.getName(), conB.getName(), conA, conB) ||
267267
oms::Connection::isValidExportConnectorName(conA, conB))
268268
{
269-
// std::cout << "\n valid connection from " << conA.getName().c_str() << " ==> " << conB.getName().c_str();
270-
// std::cout << "\n*******" << std::endl;
271269
scc.connections.push_back(std::pair<int, int>(edges.connections[components[i][j]]));
272270
scc.component_names.insert(conA.getOwner());
273271
scc.component_names.insert(conB.getOwner());
@@ -304,15 +302,24 @@ void oms::DirectedGraph::calculateSortedConnections()
304302

305303
// set suppressUnitConversion = true or false
306304
scc.suppressUnitConversion = false;
307-
for (const auto &it : unitConversion)
305+
for (const auto &it : connections)
308306
{
309-
if (it.conA == conA.getName() && it.conB == conB.getName())
307+
if (it->getSignalA() == conA.getName() && it->getSignalB() == conB.getName())
310308
{
311-
scc.suppressUnitConversion = it.unitConversion;
309+
scc.suppressUnitConversion = it->getSuppressUnitConversion();
312310
break;
313311
}
314312
}
315313
}
314+
// apply linear transformation on connection if exists
315+
for (const auto &it : connections)
316+
{
317+
if (it->getSignalA() == conA.getName() && it->getSignalB() == conB.getName())
318+
{
319+
scc.linearTransformation = it->getLinearTransformation();
320+
break;
321+
}
322+
}
316323
}
317324
}
318325

@@ -340,31 +347,24 @@ void oms::DirectedGraph::calculateSortedConnections()
340347
sortedConnectionsAreValid = true;
341348
}
342349

343-
void oms::DirectedGraph::setUnits(Connector* conA, Connector* conB, bool suppressUnitConversion)
350+
void oms::DirectedGraph::setUnits(Connector* conA, Connector* conB, Connection* connection)
344351
{
345352
/* get the full cref to check the connector owner with nodes
346353
(e.g) model.root.A.y1 ==> A.y1
347354
*/
348-
ComRef crefA(conA->getOwner() + conA->getName());
349-
ComRef tailA = crefA.pop_front();
350-
ComRef tailB = crefA.pop_front();
351-
352-
ComRef crefB(conB->getOwner() + conB->getName());
353-
ComRef tailA1 = crefB.pop_front();
354-
ComRef tailB1 = crefB.pop_front();
355355

356-
unitConversion.push_back({crefA, crefB, suppressUnitConversion});
356+
connections.push_back(connection);
357357

358358
for (auto &it : nodes)
359359
{
360-
if (it.getName() == crefA)
360+
if (it.getName() == connection->getSignalA())
361361
{
362362
for (const auto &con : conA->connectorUnits)
363363
it.connectorUnits[con.first] = con.second;
364364
// set the export name for the connector
365365
it.setExportName(conA->getExportName());
366366
}
367-
if (it.getName() == crefB)
367+
if (it.getName() == connection->getSignalB())
368368
{
369369
for (const auto &con : conB->connectorUnits)
370370
it.connectorUnits[con.first] = con.second;

src/OMSimulatorLib/DirectedGraph.h

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434

3535
#include "ComRef.h"
3636
#include "Connector.h"
37+
#include "Connection.h"
3738
#include "Variable.h"
38-
39+
#include "OMSimulator/Types.h"
3940
#include <deque>
4041
#include <map>
4142
#include <set>
@@ -59,6 +60,7 @@ namespace oms
5960
std::set<oms::ComRef> component_names;
6061
double factor;
6162
bool suppressUnitConversion;
63+
oms_linear_transformation_t linearTransformation;
6264
};
6365

6466
class DirectedGraph
@@ -81,7 +83,7 @@ namespace oms
8183
const std::vector<Connector>& getNodes() const {return nodes;}
8284
const scc_t& getEdges() const {return edges;}
8385

84-
void setUnits(Connector* conA, Connector* conB, bool suppressUnitConversion);
86+
void setUnits(Connector* conA, Connector* conB, Connection* connection);
8587
void dumpNodes() const;
8688

8789
private:
@@ -99,14 +101,7 @@ namespace oms
99101
std::vector< scc_t > sortedConnections;
100102
bool sortedConnectionsAreValid;
101103

102-
struct suppressUnitConversion
103-
{
104-
oms::ComRef conA;
105-
oms::ComRef conB;
106-
bool unitConversion;
107-
};
108-
109-
std::vector<suppressUnitConversion> unitConversion;
104+
std::vector<Connection*> connections; // needed to set the units and resolve linear transformation of the connections in the strongly connected components
110105
};
111106
}
112107

src/OMSimulatorLib/OMSimulator.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,27 @@ oms_status_enu_t oms_setConnectionGeometry(const char *crefA, const char *crefB,
604604
return system->setConnectionGeometry(tailA, tailB, reinterpret_cast<const oms::ssd::ConnectionGeometry*>(geometry));
605605
}
606606

607+
oms_status_enu_t oms_setConnectionLinearTransformation(const char *crefA, const char *crefB, double factor, double offset)
608+
{
609+
oms::ComRef tailA(crefA);
610+
oms::ComRef modelCref = tailA.pop_front();
611+
oms::ComRef systemCref = tailA.pop_front();
612+
613+
oms::ComRef tailB(crefB);
614+
tailB.pop_front();
615+
tailB.pop_front();
616+
617+
oms::Model* model = oms::Scope::GetInstance().getModel(modelCref);
618+
if (!model)
619+
return logError_ModelNotInScope(modelCref);
620+
621+
oms::System* system = model->getSystem(systemCref);
622+
if (!system)
623+
return logError_SystemNotInModel(modelCref, systemCref);
624+
625+
return system->setConnectionLinearTransformation(tailA, tailB, factor, offset);
626+
}
627+
607628
oms_status_enu_t oms_getConnections(const char *cref, oms_connection_t ***connections)
608629
{
609630
oms::ComRef tail(cref);

src/OMSimulatorLib/System.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,33 @@ oms_status_enu_t oms::System::setConnectionGeometry(const oms::ComRef& crefA, co
13821382
return logError_ConnectionNotInSystem(crefA, crefB, this);
13831383
}
13841384

1385+
oms_status_enu_t oms::System::setConnectionLinearTransformation(const oms::ComRef& crefA, const oms::ComRef& crefB, double factor, double offset)
1386+
{
1387+
oms::ComRef tailA(crefA);
1388+
oms::ComRef headA = tailA.pop_front();
1389+
1390+
oms::ComRef tailB(crefB);
1391+
oms::ComRef headB = tailB.pop_front();
1392+
1393+
//If both A and B references the same subsystem, recurse into that subsystem
1394+
if (headA == headB)
1395+
{
1396+
auto subsystem = subsystems.find(headA);
1397+
if (subsystem != subsystems.end())
1398+
return subsystem->second->setConnectionLinearTransformation(tailA, tailB, factor, offset);
1399+
}
1400+
1401+
for (auto& connection : connections)
1402+
if (connection && connection->isEqual(crefA, crefB))
1403+
{
1404+
bool inverse = connection->isStrictEqual(crefB, crefA);
1405+
connection->setLinearTransformation(factor, offset);
1406+
return oms_status_ok;
1407+
}
1408+
1409+
return logError_ConnectionNotInSystem(crefA, crefB, this);
1410+
}
1411+
13851412
oms_status_enu_t oms::System::setBusGeometry(const oms::ComRef& cref, const oms::ssd::ConnectorGeometry *geometry)
13861413
{
13871414
oms::ComRef tail(cref);
@@ -1678,9 +1705,9 @@ oms_status_enu_t oms::System::updateDependencyGraphs()
16781705
oms::Connection::isValidExportConnectorName(*varA, *varB);
16791706
if (validConnection)
16801707
{
1681-
initializationGraph.setUnits(varA, varB, connection->getSuppressUnitConversion());
1682-
eventGraph.setUnits(varA, varB, connection->getSuppressUnitConversion());
1683-
simulationGraph.setUnits(varA, varB, connection->getSuppressUnitConversion());
1708+
initializationGraph.setUnits(varA, varB, connection);
1709+
eventGraph.setUnits(varA, varB, connection);
1710+
simulationGraph.setUnits(varA, varB, connection);
16841711
}
16851712
}
16861713
}

src/OMSimulatorLib/System.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ namespace oms
106106
oms_status_enu_t deleteConnection(const ComRef& crefA, const ComRef& crefB);
107107
oms_status_enu_t setConnectorGeometry(const ComRef& cref, const oms::ssd::ConnectorGeometry* geometry);
108108
oms_status_enu_t setConnectionGeometry(const ComRef &crefA, const ComRef &crefB, const oms::ssd::ConnectionGeometry* geometry);
109+
oms_status_enu_t setConnectionLinearTransformation(const oms::ComRef& crefA, const oms::ComRef& crefB, double factor, double offset);
109110
oms_status_enu_t addBus(const ComRef& cref);
110111
oms_status_enu_t newResources(const ComRef& cref, const std::string& ssvFilename, const std::string& ssmFilename = "", bool externalresources = false);
111112
oms_status_enu_t addConnectorToBus(const ComRef& busCref, const ComRef& connectorCref);

src/OMSimulatorLib/SystemSC.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <algorithm>
4343
#include <cstring>
4444
#include <sstream>
45+
#include <iostream>
4546

4647
int oms::cvode_rhs(realtype t, N_Vector y, N_Vector ydot, void* user_data)
4748
{
@@ -988,6 +989,10 @@ oms_status_enu_t oms::SystemSC::updateInputs(DirectedGraph& graph)
988989
else
989990
value = sortedConnections[i].factor*value;
990991

992+
// Check for linear transformation. Set the value multiplied by factor and added by offset i.e. (factor * value + offset).
993+
if (sortedConnections[i].linearTransformation.isSet)
994+
value = sortedConnections[i].linearTransformation.factor*value + sortedConnections[i].linearTransformation.offset;
995+
991996
if (oms_status_ok != setReal(graph.getNodes()[input].getName(), value)) return oms_status_error;
992997
}
993998
else if (graph.getNodes()[input].getType() == oms_signal_type_integer || graph.getNodes()[input].getType() == oms_signal_type_enum)

0 commit comments

Comments
 (0)