class EmptyModel(nn.Module):
super(EmptyModel, self).__init__()
# No trainable parameters, but add a linear layer to match Simulink requirements
self.linear = nn.Linear(7, 2, bias=False)
self.linear.weight.fill_(0.0)
# Returns the first two elements of the input as is, without any computation
dummy_input = torch.randn(1, 7, dtype=torch.float32)
do_constant_folding=True,
"input": {0: "batch_size"},
"output": {0: "batch_size"},
#define S_FUNCTION_NAME smex
#define S_FUNCTION_LEVEL 2
#include <onnxruntime_cxx_api.h>
static std::unique_ptr<Ort::Env> g_env;
static std::unique_ptr<Ort::Session> g_session;
static std::vector<const char*> g_input_node_names;
static std::vector<const char*> g_output_node_names;
static void mdlInitializeSizes(SimStruct *S)
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 7);
ssSetInputPortDataType(S, 0, SS_DOUBLE);
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetInputPortRequiredContiguous(S, 0, 1);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 2);
ssSetOutputPortDataType(S, 0, SS_DOUBLE);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
static void mdlInitializeSampleTimes(SimStruct *S)
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
static void mdlStart(SimStruct *S)
g_env = std::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_WARNING, "test");
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
const wchar_t* model_path = L"empty_model.onnx";
g_session = std::make_unique<Ort::Session>(*g_env, model_path, session_options);
Ort::AllocatorWithDefaultOptions allocator;
size_t num_input_nodes = g_session->GetInputCount();
g_input_node_names.resize(num_input_nodes);
for (size_t i = 0; i < num_input_nodes; i++) {
auto input_name = g_session->GetInputNameAllocated(i, allocator);
g_input_node_names[i] = input_name.get();
size_t num_output_nodes = g_session->GetOutputCount();
g_output_node_names.resize(num_output_nodes);
for (size_t i = 0; i < num_output_nodes; i++) {
auto output_name = g_session->GetOutputNameAllocated(i, allocator);
g_output_node_names[i] = output_name.get();
catch (const Ort::Exception& ex) {
mexErrMsgIdAndTxt("myOnnxSfunc:InitError", " %s", ex.what());
static void mdlOutputs(SimStruct *S, int_T tid)
const real_T *u = ssGetInputPortRealSignal(S, 0);
std::vector<float> input_data(7);
for (int i = 0; i < 7; i++) {
input_data[i] = static_cast<float>(u[i]);
std::vector<int64_t> input_shape = { 1, 7 };
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
auto output_tensors = g_session->Run(
Ort::RunOptions{ nullptr },
g_input_node_names.data(),
g_output_node_names.data(),
g_output_node_names.size()
float* output_data = output_tensors[0].GetTensorMutableData<float>();
auto type_info = output_tensors[0].GetTensorTypeAndShapeInfo();
auto output_shape = type_info.GetShape();
if (output_shape.size() != 2 || output_shape[0] != 1 || output_shape[1] < 2) {
mexErrMsgIdAndTxt("myOnnxSfunc:OutputError",
output_shape[0], output_shape[1]);
real_T *y = ssGetOutputPortRealSignal(S, 0);
y[0] = static_cast<double>(output_data[0]);
y[1] = static_cast<double>(output_data[1]);
catch (const Ort::Exception& ex) {
mexErrMsgIdAndTxt("myOnnxSfunc:RuntimeError", "%s", ex.what());
static void mdlTerminate(SimStruct *S)
g_input_node_names.clear();
g_output_node_names.clear();